libmongocrypt-1.19.0/000077500000000000000000000000001521103432300144645ustar00rootroot00000000000000libmongocrypt-1.19.0/.clang-format000066400000000000000000000017351521103432300170450ustar00rootroot00000000000000--- Language: Cpp # (Options are sorted alphabetically) AlignAfterOpenBracket: Align AlignEscapedNewlines: Right AlignOperands: AlignAfterOperator AllowAllArgumentsOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Attach ColumnLimit: 120 ContinuationIndentWidth: 4 IndentCaseLabels: false IndentWidth: 4 InsertBraces: true KeepEmptyLinesAtTheStartOfBlocks: false PointerAlignment: Right QualifierAlignment: Custom QualifierOrder: ["static", "inline", "const", "volatile", "type", "restrict"] SeparateDefinitionBlocks: Always SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpacesInLineCommentPrefix: Minimum: 1 Maximum: -1 UseTab: Never libmongocrypt-1.19.0/.clang-tidy000066400000000000000000000005161521103432300165220ustar00rootroot00000000000000# Older clang-tidy gives a warning for an uninitialized va_args, # even on the test case that they assert should not produce that warning: # https://github.com/llvm-mirror/clang/blob/59207d134acb8a1c6cb9974c32c1c04bd31f8f7b/test/Analysis/valist-uninitialized.c#L30 Checks: "-clang-analyzer-valist.Uninitialized" WarningsAsErrors: "*" libmongocrypt-1.19.0/.earthlyignore000066400000000000000000000000461521103432300173410ustar00rootroot00000000000000cmake-build/ cmake-build-deb/ _build/ libmongocrypt-1.19.0/.evergreen/000077500000000000000000000000001521103432300165245ustar00rootroot00000000000000libmongocrypt-1.19.0/.evergreen/benchmark-python.sh000077500000000000000000000016571521103432300223450ustar00rootroot00000000000000#!/usr/bin/env bash . "$(dirname "${BASH_SOURCE[0]}")/init.sh" if test "$OS_NAME" != "linux"; then log "Warning: Script is expected only to run on distro: rhel90-dbx-perf-large" log "More changes may be needed to run on other distros."; fi MONGOCRYPT_INSTALL_PREFIX=$LIBMONGOCRYPT_DIR/.install . "$(dirname "${BASH_SOURCE[0]}")/install-build-tools.sh" install_build_tools # Install libmongocrypt. build_dir="$LIBMONGOCRYPT_DIR/cmake-build" cmake \ -DCMAKE_INSTALL_PREFIX="$MONGOCRYPT_INSTALL_PREFIX" \ -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ -B"$build_dir" cmake --build "$build_dir" --target install # Run Python benchmarks. # Include path to installed libmongocrypt.so export LD_LIBRARY_PATH="$MONGOCRYPT_INSTALL_PREFIX/lib64" cd bindings/python/ uv venv venv . ./venv/bin/activate uv pip install -r requirements-test.txt uv pip install -e . export OUTPUT_FILE=results.json python test/performance/perf_test.py -v libmongocrypt-1.19.0/.evergreen/build_all.sh000077500000000000000000000133261521103432300210170ustar00rootroot00000000000000#!/bin/bash # Compiles libmongocrypt dependencies and targets. # # Set extra compilation for libmongocrypt variables by setting CFLAGS and CXXFLAGS. echo "Begin compile process" . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" set -eu # Directory where build files will be stored : "${BINARY_DIR:="$LIBMONGOCRYPT_DIR/cmake-build"}" # Additional compilation flags that apply only to the libmongocrypt build : "${LIBMONGOCRYPT_COMPILE_FLAGS:=}" # Additional CMake flags that apply only to the libmongocrypt build. (Used by the C driver) : "${LIBMONGOCRYPT_EXTRA_CMAKE_FLAGS:=}" # release_os_arch is set for release builds. : "${release_os_arch:=}" # Control the build configuration that is generated. export CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-RelWithDebInfo}" # Sets the default config for --build and CTest export CMAKE_CONFIG_TYPE="$CMAKE_BUILD_TYPE" # Control the install prefix export CMAKE_INSTALL_PREFIX="${MONGOCRYPT_INSTALL_PREFIX-}" # Have CTest print test failure info to stderr export CTEST_OUTPUT_ON_FAILURE=1 # Generate a compilation database for use by other tools export CMAKE_EXPORT_COMPILE_COMMANDS=1 # Permit skipping build of tests. BUILD_TESTING="${BUILD_TESTING-TRUE}" # Build nocrypto and sharedbson variants (true by defualt). LIBMONGOCRYPT_BUILD_VARIANTS="${LIBMONGOCRYPT_BUILD_VARIANTS:-TRUE}" # Accumulate arguments that are passed to CMake cmake_args=() # Temporary workarounds for rhel-62-64-bit and rhel72-zseries-test. To be removed. if cmake --help | grep -q -- '--fresh'; then cmake_args+=(--fresh) fi cmake_args+=( # Set the build type. CMake 3.22 recognizes this via environment variable -D CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" # Set the install prefix. CMake 3.29 recognizes this via environment variable -D CMAKE_INSTALL_PREFIX="$CMAKE_INSTALL_PREFIX" # Toggle compiling with shared BSON -D USE_SHARED_LIBBSON="${USE_SHARED_LIBBSON-FALSE}" # Toggle building of tests -D BUILD_TESTING="${BUILD_TESTING:?}" # Enable additional warnings-as-errors -D ENABLE_MORE_WARNINGS_AS_ERRORS=TRUE ) # shellcheck disable=SC2206 cmake_args+=($LIBMONGOCRYPT_EXTRA_CMAKE_FLAGS) : "${CONFIGURE_ONLY:=}" if [ "$PPA_BUILD_ONLY" ]; then # Clean-up from previous build iteration rm -rf -- "$LIBMONGOCRYPT_DIR"/cmake-build* "$CMAKE_INSTALL_PREFIX" cmake_args+=(-DENABLE_BUILD_FOR_PPA=ON) fi for suffix in "dll" "dylib" "so"; do cand="$(abspath "$LIBMONGOCRYPT_DIR/../mongocrypt_v1.$suffix")" if test -f "$cand"; then cmake_args+=("-DMONGOCRYPT_TESTING_CRYPT_SHARED_FILE=$cand") fi done . "$(dirname "${BASH_SOURCE[0]}")/install-build-tools.sh" install_build_tools # A command that prepends our custom compile flags for any CMake execution _cmake_with_env() { # Prepend our custom C and CXX flags for any possible CMake builds CFLAGS="$LIBMONGOCRYPT_COMPILE_FLAGS ${CFLAGS-}" \ CXXFLAGS="$LIBMONGOCRYPT_COMPILE_FLAGS ${CXXFLAGS-}" \ cmake "$@" } # Build and install libmongocrypt. _cmake_with_env "${cmake_args[@]}" \ -B "$BINARY_DIR" -S "$LIBMONGOCRYPT_DIR" if [ "$CONFIGURE_ONLY" ]; then echo "Only running cmake"; exit 0; fi echo "Installing libmongocrypt" _cmake_with_env --build "$BINARY_DIR" --target install # If release_os_arch names a minimum glibc requirement (e.g. "linux-x86_64-glibc_2_17-nocrypto"), # verify it matches the maximum glibc symbol used. if [[ "$release_os_arch" == *glibc* ]]; then expected_glibc=$(echo "$release_os_arch" | sed -r 's/.*glibc_([0-9]+)_([0-9]+).*/\1.\2/') if [ -f "$CMAKE_INSTALL_PREFIX/lib64/libmongocrypt.so" ]; then check_lib="$CMAKE_INSTALL_PREFIX/lib64/libmongocrypt.so" elif [ -f "$CMAKE_INSTALL_PREFIX/lib/libmongocrypt.so" ]; then check_lib="$CMAKE_INSTALL_PREFIX/lib/libmongocrypt.so" else echo "glibc version check failed: libmongocrypt.so not found under $CMAKE_INSTALL_PREFIX" exit 1 fi actual_glibc=$(objdump -T "$check_lib" | grep 'GLIBC_' | sed -r -e 's/.*GLIBC_([0-9.]+).*/\1/' | sort -u | tail -1) if [ "$actual_glibc" != "$expected_glibc" ]; then echo "glibc version check failed: release_os_arch requires glibc $expected_glibc but library uses glibc $actual_glibc" exit 1 fi echo "glibc version check passed: $actual_glibc" fi run_chdir "$BINARY_DIR" ctest # MONGOCRYPT-372, ensure macOS universal builds contain both x86_64 and arm64 architectures. if test "${CMAKE_OSX_ARCHITECTURES-}" != ''; then echo "Checking if libmongocrypt.dylib contains both x86_64 and arm64 architectures..." ARCHS=$(lipo -archs $MONGOCRYPT_INSTALL_PREFIX/lib/libmongocrypt.dylib) if [[ "$ARCHS" == *"x86_64"* && "$ARCHS" == *"arm64"* ]]; then echo "Checking if libmongocrypt.dylib contains both x86_64 and arm64 architectures... OK" else echo "Checking if libmongocrypt.dylib contains both x86_64 and arm64 architectures... ERROR. Got: $ARCHS" exit 1 fi fi if [ "$PPA_BUILD_ONLY" ]; then echo "Only building/installing for PPA"; exit 0; fi if [ "${LIBMONGOCRYPT_BUILD_VARIANTS:?}" != "TRUE" ]; then echo "Skipping build of libmongocrypt variants"; exit 0; fi # Build and install libmongocrypt with no native crypto. _cmake_with_env "${cmake_args[@]}" \ -DDISABLE_NATIVE_CRYPTO=ON \ -DCMAKE_INSTALL_PREFIX="$MONGOCRYPT_INSTALL_PREFIX/nocrypto" \ -B "$BINARY_DIR" -S "$LIBMONGOCRYPT_DIR" _cmake_with_env --build "$BINARY_DIR" --target install run_chdir "$BINARY_DIR" ctest # Build and install libmongocrypt without statically linking libbson _cmake_with_env "${cmake_args[@]}" \ -DUSE_SHARED_LIBBSON=ON \ -DCMAKE_INSTALL_PREFIX="$MONGOCRYPT_INSTALL_PREFIX/sharedbson" \ -B "$BINARY_DIR" -S "$LIBMONGOCRYPT_DIR" _cmake_with_env --build "$BINARY_DIR" --target install run_chdir "$BINARY_DIR" ctest libmongocrypt-1.19.0/.evergreen/clang-tidy.sh000077500000000000000000000006001521103432300211120ustar00rootroot00000000000000#!/bin/bash # Run after running "CONFIGURE_ONLY=ON compile.sh" to run the clang-tidy # static analyzer. # . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" CLANG_TIDY=/opt/mongodbtoolchain/v3/bin/clang-tidy $CLANG_TIDY --version python "$LIBMONGOCRYPT_DIR/etc/list-compile-files.py" \ "$LIBMONGOCRYPT_DIR/cmake-build/" \ | xargs $CLANG_TIDY -p "$LIBMONGOCRYPT_DIR/cmake-build/" libmongocrypt-1.19.0/.evergreen/compile.sh000077500000000000000000000005341521103432300205150ustar00rootroot00000000000000#!/bin/bash # Downloads and prepares the C driver source, then compiles libmongocrypt's # dependencies and targets. # # NOTE: This script is not meant to be invoked for Evergreen builds. It is a # convenience script for users of libmongocrypt . "$(dirname "${BASH_SOURCE[0]}")/init.sh" bash "$EVG_DIR/setup-env.sh" bash "$EVG_DIR/build_all.sh" libmongocrypt-1.19.0/.evergreen/config.yml000077500000000000000000002153541521103432300205310ustar00rootroot00000000000000# Welcome. Evergreen configuration can be time consuming to modify and test. # So each script has a header comment describing how to run it locally. # # Some environment variables are hidden in the evergreen project config. # View this in Evergreen => Projects => libmongocrypt. # functions: "cleanup environment": - command: shell.exec params: script: | set -o verbose rm -rf ~/.aws ~/.notary_env.sh exit 0 "fetch drivers-evergreen-tools": - command: subprocess.exec type: setup params: binary: bash args: - -c - | if [[ ! -d drivers-evergreen-tools ]]; then git clone --depth=1 https://github.com/mongodb-labs/drivers-evergreen-tools.git fi - command: subprocess.exec type: setup params: binary: bash working_dir: drivers-evergreen-tools args: - -c - find .evergreen -type f -name "*.sh" -exec chmod +rx "{}" \; - command: subprocess.exec type: setup params: binary: bash args: - -c - | set -o errexit . drivers-evergreen-tools/.evergreen/find-python3.sh echo "PYTHON3_BINARY: $(find_python3)" >|python3_binary.yml - command: expansions.update type: setup params: file: python3_binary.yml "fetch source": - command: git.get_project params: {directory: libmongocrypt} - command: shell.exec params: shell: bash script: |- set -o errexit . libmongocrypt/.evergreen/init.sh bash "$EVG_DIR/print-env-info.sh" # determine if we have a release tag present on HEAD head_tag=$(run_chdir libmongocrypt/ git tag -l --points-at HEAD '[0-9].*' || true) use_tag="" if test "${is_patch}" != "true"; then echo "Setting tag_upload_location to '$head_tag'" use_tag="$head_tag" fi echo "tag_upload_location: '$use_tag'" > tag_expansion.yml - command: expansions.update params: ignore_missing_file: true file: tag_expansion.yml "tar and upload libmongocrypt libraries": - command: archive.targz_pack params: target: libmongocrypt-${build_variant}-${tag_upload_location!|*revision}.tar.gz source_dir: install/libmongocrypt include: [./**] - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-${build_variant}-${tag_upload_location!|*revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix_copy}/libmongocrypt.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-${build_variant}-${tag_upload_location!|*revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - 'libmongocrypt-${build_variant}-${tag_upload_location!|*revision}.tar.gz' "build and test": - command: "shell.exec" params: shell: bash script: |- . libmongocrypt/.evergreen/init.sh export LSAN_OPTIONS="suppressions=$LIBMONGOCRYPT_DIR/.lsan-suppressions" export VS_VERSION=${vs_version|} export VS_TARGET_ARCH=${vs_target_arch|amd64} export CMAKE_GENERATOR=${CMAKE_GENERATOR|Ninja} # Even on Windows. export release_os_arch=${release_os_arch} env ${compile_env|} \ bash "$EVG_DIR/env-run.sh" \ bash "$EVG_DIR/build_all.sh" env ${compile_env|} \ bash "$EVG_DIR/env-run.sh" \ bash "$EVG_DIR/linker-tests.sh" env ${compile_env|} \ bash "$EVG_DIR/env-run.sh" \ bash "$EVG_DIR/pkgconfig-tests.sh" "create packages and repos": - command: "shell.exec" params: shell: bash script: | env "WORKDIR=${workdir}" \ "PYTHON=${PYTHON3_BINARY|}" \ "PACKAGER_DISTRO=${packager_distro}" \ "PACKAGER_ARCH=${packager_arch}" \ ${compile_env|} \ bash libmongocrypt/.evergreen/create-packages-and-repos.sh "upload packages and repos": - command: archive.targz_pack params: target: libmongocrypt-distro-packages-${tag_upload_location!|*revision}.tar.gz source_dir: libmongocrypt/repo include: [./**] - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt-distro-packages.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-distro-packages-${tag_upload_location!|*revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - 'libmongocrypt-distro-packages-${tag_upload_location!|*revision}.tar.gz' "run clang-tidy": - command: "shell.exec" params: script: |- env ${compile_env|} CONFIGURE_ONLY=ON ${clang_env|CC=clang CXX=clang++} \ bash libmongocrypt/.evergreen/build_all.sh ./libmongocrypt/.evergreen/clang-tidy.sh "test python": - command: "shell.exec" params: script: |- export MONGOCRYPT_DIR="$(pwd)/all/${variant_name}" cd ./libmongocrypt/bindings/python && ${test_env|} ./.evergreen/test.sh "test python integ": - command: ec2.assume_role params: role_arn: ${drivers_test_secrets_role} - command: subprocess.exec params: binary: bash include_expansions_in_env: - project_directory - variant_name - AWS_SECRET_ACCESS_KEY - AWS_ACCESS_KEY_ID - AWS_SESSION_TOKEN args: - ./libmongocrypt/bindings/python/.evergreen/integ-setup.sh - command: expansions.update params: file: expansion.yml - command: subprocess.exec params: binary: bash background: true args: - ${DRIVERS_TOOLS}/.evergreen/csfle/start-servers.sh - command: subprocess.exec params: binary: bash args: - ${DRIVERS_TOOLS}/.evergreen/csfle/await-servers.sh - command: subprocess.exec params: binary: bash working_dir: "${DRIVERS_TOOLS}" env: TOPOLOGY: replica_set MONGODB_VERSION: "8.0" args: - ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh - command: "subprocess.exec" params: binary: bash include_expansions_in_env: - DRIVERS_TOOLS - PYMONGO_DIR - MONGOCRYPT_DIR args: - ./libmongocrypt/bindings/python/.evergreen/integ-test.sh - command: subprocess.exec params: binary: bash include_expansions_in_env: - DRIVERS_TOOLS args: - ./libmongocrypt/bindings/python/.evergreen/integ-teardown.sh - command: attach.xunit_results params: file: ${PYMONGO_DIR}/xunit-results/TEST-*.xml "download tarball": - command: s3.get params: role_arn: ${upload_arn} remote_file: 'libmongocrypt/${variant_name}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt.tar.gz' bucket: ${upload_bucket} extract_to: all/${variant_name} "setup packaging credentials": - command: shell.exec params: silent: true shell: bash script: | set -o errexit if [ "${is_patch}" = "true" ]; then exit 0 fi # set AWS credentials rm -rf ~/.aws mkdir -p ~/.aws cat <> ~/.aws/config [default] region = us-east-1 EOF cat <> ~/.aws/credentials [default] aws_access_key_id = ${repo_aws_key} aws_secret_access_key = ${repo_aws_secret} EOF # set notary credentials rm -f ~/.notary_env.sh cat < ~/.notary_env.sh export NOTARY_TOKEN=${repo_notary_secret} export NOTARY_KEY_NAME=${repo_notary_name} export BARQUE_USERNAME=${barque_username} export BARQUE_API_KEY=${barque_api_key} EOF "publish packages": - command: shell.exec params: working_dir: libmongocrypt shell: bash script: |- [ -f ~/.notary_env.sh ] && . ~/.notary_env.sh set -o xtrace set -o errexit set -o verbose if [ "${is_patch}" = "true" ]; then echo "patch build, skipping packaging publication" exit 0 fi # Some venv-activate scripts are not nounset-clean set +u # Need requests and pycrypto for notary-client.py python -m virtualenv venv cd venv . bin/activate ./bin/pip install requests ./bin/pip install pycrypto cd .. # Get the current version of libmongocrypt. pkg_version="$(python etc/calc_release_version.py)" CURATOR_RELEASE=${curator_release|"e0b5f66fc89ec0acddcd40ea5f447a8300ded2b9"} curl -L -O http://boxes.10gen.com/build/curator/curator-dist-rhel70-$CURATOR_RELEASE.tar.gz tar -zxvf curator-dist-rhel70-$CURATOR_RELEASE.tar.gz ./curator version if [ "${project}" = 'libmongocrypt-release' ]; then package_url_prefix="https://downloads.mongodb.org" else package_url_prefix="https://mciuploads.s3.amazonaws.com" fi ./curator repo submit --config etc/repo_config.yaml --distro ${packager_distro} --edition org --version $pkg_version --arch ${packager_arch} --service https://barque.corp.mongodb.com/ --packages $package_url_prefix/libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt-distro-packages.tar.gz "build python release": - command: shell.exec params: shell: bash script: | set -ex cd ./libmongocrypt/bindings/python PYTHON=${PYTHON} ./scripts/release.sh "upload python release": - command: archive.targz_pack params: target: "release-files-${tag_upload_location!|*revision}.tgz" source_dir: "libmongocrypt/bindings/python/dist" include: - "*" - command: s3.put params: role_arn: ${upload_arn} skip_existing: true local_file: release-files-${tag_upload_location!|*revision}.tgz remote_file: 'libmongocrypt/python-release/${branch_name}/${libmongocrypt_s3_suffix}/${task_id}-${execution}-release-files.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} content_type: ${content_type|application/gzip} display_name: Release Python files - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - release-files-${tag_upload_location!|*revision}.tgz "download and merge python releases": - command: ec2.assume_role type: setup params: role_arn: ${upload_arn} - command: shell.exec params: silent: true shell: "bash" script: | # set AWS credentials rm -rf ~/.aws mkdir -p ~/.aws cat <> ~/.aws/config [default] region = us-east-1 EOF - command: shell.exec params: shell: "bash" include_expansions_in_env: &aws-params-env - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - AWS_SESSION_TOKEN script: | set -o xtrace # Download all the release files. aws s3 cp --recursive s3://${upload_bucket}/${project}/python-release/${branch_name}/${libmongocrypt_s3_suffix}/ release/ # Combine releases into one directory. ls -la release/ mkdir releases for REL in release/*; do tar zxvf $REL -C releases/ done - command: archive.targz_pack params: target: "release-files-all-${tag_upload_location!|*revision}.tgz" source_dir: "releases/" include: - "*" - command: s3.put params: role_arn: ${upload_arn} skip_existing: true local_file: release-files-all-${tag_upload_location!|*revision}.tgz remote_file: 'libmongocrypt/python-release/${branch_name}/${libmongocrypt_s3_suffix}/${task_id}-${execution}-release-files-all.tar.gz' # The merged results are placed in the CDN bucket for releases bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} content_type: ${content_type|application/gzip} display_name: Release Python files all - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - release-files-all-${tag_upload_location!|*revision}.tgz earthly: - command: shell.exec params: shell: bash working_dir: ${working_dir|libmongocrypt} script: | # Authenticate to artifactory. echo "${artifactory_password}" | docker login --password-stdin --username "${artifactory_username}" artifactory.corp.mongodb.com bash ${workdir}/libmongocrypt/.evergreen/earthly.sh ${args} sbom: - command: ec2.assume_role type: setup params: role_arn: ${kondukto_role_arn} - command: subprocess.exec type: setup params: binary: bash include_expansions_in_env: *aws-params-env args: - -c - | set -o errexit set -o pipefail kondukto_token="$(aws secretsmanager get-secret-value --secret-id "kondukto-token" --region "us-east-1" --query 'SecretString' --output text)" printf "KONDUKTO_TOKEN: %s\n" "$kondukto_token" >|expansions.kondukto.yml - command: expansions.update type: setup params: file: expansions.kondukto.yml - command: subprocess.exec type: test params: binary: bash working_dir: libmongocrypt include_expansions_in_env: - artifactory_password - artifactory_username - branch_name - KONDUKTO_TOKEN args: - -c - .evergreen/sbom.sh - command: s3.put type: test params: display_name: Augmented SBOM role_arn: ${upload_arn} skip_existing: true bucket: ${upload_bucket} content_type: application/json local_file: libmongocrypt/cyclonedx.augmented.sbom.json permissions: ${upload_permissions} visibility: ${upload_visibility} remote_file: libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/sbom/cyclonedx.augmented.sbom.json tasks: - name: build-and-test-and-upload commands: - func: "fetch source" - func: "build and test" - func: "tar and upload libmongocrypt libraries" - name: build-packages commands: - func: "fetch source" - func: "fetch drivers-evergreen-tools" # Set PYTHON3_BINARY. - func: "create packages and repos" - func: "upload packages and repos" - name: build-with-earthly commands: - func: "fetch source" - func: "earthly" vars: args: --artifact +build/libmongocrypt-install --env=${earthly_env} ${workdir}/install - func: "tar and upload libmongocrypt libraries" - name: build-deb-packages-with-earthly commands: - func: "fetch source" - func: "earthly" vars: args: +create-deb-packages-and-repos --env=${earthly_env} --packager_distro=${packager_distro} --packager_arch=${packager_arch} - func: "upload packages and repos" - name: publish-deb-packages-with-earthly depends_on: build-deb-packages-with-earthly commands: - func: "fetch source" - func: "setup packaging credentials" - func: "publish packages" - name: clang-tidy commands: - func: "fetch source" - func: "run clang-tidy" - name: build-and-test-shared-bson commands: - func: "fetch source" - func: "build and test" vars: compile_env: >- ${compile_env|} USE_SHARED_LIBBSON=true - name: build-and-test-asan commands: - func: "fetch source" - func: "build and test" vars: # Add detect_odr_violation=0 to ASAN_OPTIONS to ignore odr-violation in IntelDFP symbol: __dpml_bid_globals_table compile_env: >- ${compile_env|} LIBMONGOCRYPT_COMPILE_FLAGS="-fsanitize=address -pthread" ASAN_OPTIONS="detect_leaks=1 detect_odr_violation=0" - name: build-and-test-ubsan commands: - func: "fetch source" - func: "build and test" vars: compile_env: >- ${compile_env|} LIBMONGOCRYPT_COMPILE_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer" UBSAN_OPTIONS="halt_on_error=1,print_stacktrace=1" - name: build-and-test-asan-mac commands: - func: "fetch source" - func: "build and test" # Exclude leak detection. clang on macos-11-amd64 reports: "detect_leaks is not supported on this platform" vars: compile_env: >- ${compile_env|} LIBMONGOCRYPT_COMPILE_FLAGS="-fsanitize=address" - name: test-python depends_on: - build-and-test-and-upload commands: - func: "fetch source" - func: "download tarball" vars: { variant_name: "${build_variant}" } - func: "test python" vars: { variant_name: "${build_variant}" } - name: test-python-windows depends_on: # Depends on the windows-64-vs2017-test upload. - variant: windows-test name: build-and-test-and-upload commands: - func: "fetch source" - func: "download tarball" vars: { variant_name: windows-test } - func: "test python" vars: { variant_name: windows-test } - name: test-python-integ depends_on: - build-and-test-and-upload commands: - func: "fetch source" - func: "download tarball" vars: { variant_name: "${build_variant}" } - func: "test python integ" vars: { variant_name: "${build_variant}" } - name: "release-python-macos-1100" tags: ["release_python_tag"] run_on: macos-1100 commands: - func: "fetch source" - func: "build python release" vars: { PYTHON: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 } - func: "upload python release" - name: "release-python-linux" tags: ["release_python_tag"] run_on: ubuntu2004-large exec_timeout_secs: 3600 # 60 minutes (manylinux task is slow). commands: - func: "fetch source" - func: "build python release" vars: { PYTHON: /opt/python/3.9/bin/python3 } - func: "upload python release" - name: "release-python-windows" tags: ["release_python_tag"] run_on: windows-64-vsMulti-small commands: - func: "fetch source" - func: "build python release" vars: { PYTHON: C:/python/Python39/python.exe } - func: "upload python release" - name: "release-python-combine" tags: ["release_python_tag"] run_on: rhel84-small depends_on: - name: "*" variant: ".release_python_tag" patch_optional: true commands: - func: "download and merge python releases" - name: upload-java # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks. depends_on: - variant: rhel-62-64-bit name: build-and-test-and-upload - variant: rhel72-zseries-test name: build-and-test-and-upload - variant: rhel-71-ppc64el name: build-and-test-and-upload - variant: ubuntu1604-arm64 name: build-and-test-and-upload - variant: windows-test name: build-and-test-and-upload - variant: macos_x86_64 name: build-and-test-and-upload - variant: macos name: build-and-test-and-upload commands: - func: "fetch source" # To set `tag_upload_location` expansion. - command: shell.exec params: script: mkdir all - func: "download tarball" vars: { variant_name: "rhel-62-64-bit" } - func: "download tarball" vars: { variant_name: "rhel72-zseries-test" } - func: "download tarball" vars: { variant_name: "rhel-71-ppc64el" } - func: "download tarball" vars: { variant_name: "ubuntu1604-arm64" } - func: "download tarball" vars: { variant_name: "windows-test" } - func: "download tarball" vars: { variant_name: "macos_x86_64" } - func: "download tarball" vars: { variant_name: "macos" } - command: archive.targz_pack params: target: libmongocrypt-java-${revision}.tar.gz source_dir: all include: [./**] - command: shell.exec params: script: |- set -o errexit if [ -n "${tag_upload_location}" ]; then # the "fetch source" step detected a release tag on HEAD, so we # prepare a local file for upload to a location based on the tag cp -a libmongocrypt-java-${revision}.tar.gz libmongocrypt-java-${tag_upload_location}.tar.gz fi - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/java/${revision}/libmongocrypt-java.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-java-${revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/java/${tag_upload_location}/libmongocrypt-java.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true display_name: 'libmongocrypt-java-${tag_upload_location}.tar.gz' local_file: 'libmongocrypt-java-${tag_upload_location}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - libmongocrypt-java-${tag_upload_location!|*revision}.tar.gz - name: upload-all # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks. depends_on: - variant: ubuntu1604 name: build-and-test-and-upload - variant: macos_x86_64 name: build-and-test-and-upload - variant: rhel72-zseries-test name: build-and-test-and-upload - variant: windows-test name: build-and-test-and-upload - variant: linux-64-amazon-ami name: build-and-test-and-upload - variant: amazon2 name: build-and-test-and-upload - variant: amazon2-arm64 name: build-and-test-and-upload - variant: amazon2023 name: build-and-test-and-upload - variant: amazon2023-arm64 name: build-and-test-and-upload - variant: debian13 name: build-and-test-and-upload - variant: debian12 name: build-and-test-and-upload - variant: debian11 name: build-and-test-and-upload - variant: debian10 name: build-and-test-and-upload - variant: debian92 name: build-and-test-and-upload - variant: rhel-62-64-bit name: build-and-test-and-upload - variant: rhel-70-64-bit name: build-and-test-and-upload - variant: rhel-71-ppc64el name: build-and-test-and-upload - variant: rhel-80-64-bit name: build-and-test-and-upload - variant: rhel-81-ppc64el name: build-and-test-and-upload - variant: rhel-82-arm64 name: build-and-test-and-upload - variant: rhel-83-zseries name: build-and-test-and-upload - variant: rhel-91-64-bit name: build-and-test-and-upload - variant: rhel-91-arm64 name: build-and-test-and-upload - variant: suse12-64 name: build-and-test-and-upload - variant: suse15-64 name: build-and-test-and-upload - variant: ubuntu1604-arm64 name: build-and-test-and-upload - variant: ubuntu1804-64 name: build-and-test-and-upload - variant: ubuntu1804-arm64 name: build-and-test-and-upload - variant: ubuntu2004-64 name: build-and-test-and-upload - variant: ubuntu2004-arm64 name: build-and-test-and-upload - variant: ubuntu2204-64 name: build-and-test-and-upload - variant: ubuntu2204-arm64 name: build-and-test-and-upload - variant: ubuntu2404-64 name: build-and-test-and-upload - variant: ubuntu2404-arm64 name: build-and-test-and-upload - variant: macos name: build-and-test-and-upload - variant: alpine-amd64-earthly name: build-with-earthly - variant: alpine-arm64-earthly name: build-with-earthly commands: - func: "fetch source" - command: shell.exec params: script: mkdir all - func: "download tarball" vars: { variant_name: ubuntu1604 } - func: "download tarball" vars: { variant_name: "macos" } - func: "download tarball" vars: { variant_name: "rhel72-zseries-test" } - func: "download tarball" vars: { variant_name: "windows-test" } - func: "download tarball" vars: { variant_name: "linux-64-amazon-ami" } - func: "download tarball" vars: { variant_name: "amazon2" } - func: "download tarball" vars: { variant_name: "amazon2-arm64" } - func: "download tarball" vars: { variant_name: "amazon2023" } - func: "download tarball" vars: { variant_name: "amazon2023-arm64" } - func: "download tarball" vars: { variant_name: "debian13" } - func: "download tarball" vars: { variant_name: "debian12" } - func: "download tarball" vars: { variant_name: "debian11" } - func: "download tarball" vars: { variant_name: "debian10" } - func: "download tarball" vars: { variant_name: "debian92" } - func: "download tarball" vars: { variant_name: "rhel-62-64-bit" } - func: "download tarball" vars: { variant_name: "rhel-70-64-bit" } - func: "download tarball" vars: { variant_name: "rhel-71-ppc64el" } - func: "download tarball" vars: { variant_name: "rhel-80-64-bit" } - func: "download tarball" vars: { variant_name: "rhel-81-ppc64el" } - func: "download tarball" vars: { variant_name: "rhel-82-arm64" } - func: "download tarball" vars: { variant_name: "rhel-83-zseries" } - func: "download tarball" vars: { variant_name: "rhel-91-64-bit" } - func: "download tarball" vars: { variant_name: "rhel-91-arm64" } - func: "download tarball" vars: { variant_name: "suse12-64" } - func: "download tarball" vars: { variant_name: "suse15-64" } - func: "download tarball" vars: { variant_name: "ubuntu1604-arm64" } - func: "download tarball" vars: { variant_name: "ubuntu1804-64" } - func: "download tarball" vars: { variant_name: "ubuntu1804-arm64" } - func: "download tarball" vars: { variant_name: "ubuntu2004-64" } - func: "download tarball" vars: { variant_name: "ubuntu2004-arm64" } - func: "download tarball" vars: { variant_name: "ubuntu2204-64" } - func: "download tarball" vars: { variant_name: "ubuntu2204-arm64" } - func: "download tarball" vars: { variant_name: "macos" } - func: "download tarball" vars: { variant_name: "alpine-amd64-earthly" } - func: "download tarball" vars: { variant_name: "alpine-arm64-earthly" } - command: archive.targz_pack params: target: libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz source_dir: all include: [./**] - command: shell.exec params: script: |- set -o errexit if [ -n "${tag_upload_location}" ]; then # the "fetch source" step detected a release tag on HEAD, so we # prepare a local file for upload to a location based on the tag if [[ "$tag_upload_location" = *-* ]]; then # Unstable release, like 1.1.0-beta1 or 1.0.1-rc0. mkdir unstable cp -a libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz unstable/libmongocrypt-all-${tag_upload_location}.tar.gz else mkdir stable cp -a libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz stable/libmongocrypt-all-${tag_upload_location}.tar.gz fi fi - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${branch_name}/${libmongocrypt_s3_suffix_copy}/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${tag_upload_location}/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for tagged release. display_name: 'libmongocrypt-all-${tag_upload_location}.tar.gz' local_file: 'libmongocrypt-all-${tag_upload_location}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/latest/stable/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for stable release. display_name: 'stable/libmongocrypt-all-${tag_upload_location}.tar.gz' local_file: 'stable/libmongocrypt-all-${tag_upload_location}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/latest/unstable/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for unstable release. display_name: 'unstable/libmongocrypt-all-${tag_upload_location}.tar.gz' local_file: 'unstable/libmongocrypt-all-${tag_upload_location}.tar.gz' content_type: '${content_type|application/x-gzip}' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz - name: sign-all # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks. patchable: false # Garasign credentials are marked as "Admin only" in Evergreen project. "Admin only" variables are not included in patch builds. To test a patch: temporarily unselect "Admin only". depends_on: upload-all commands: - func: "fetch source" # To get Earthfile. - command: s3.get params: role_arn: '${upload_arn}' remote_file: 'libmongocrypt/all/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt-all.tar.gz' bucket: ${upload_bucket} local_file: 'libmongocrypt/libmongocrypt-all.tar.gz' - func: "earthly" # Sign tarball. vars: args: --secret garasign_username=${garasign_username} --secret garasign_password=${garasign_password} +sign --file_to_sign=libmongocrypt-all.tar.gz --output_file=libmongocrypt-all.asc --is_patch="${is_patch}" # Upload to same locations as libmongocrypt-all.tar.gz - command: shell.exec params: script: |- set -o errexit cd libmongocrypt if [ -n "${tag_upload_location}" ]; then # the "fetch source" step detected a release tag on HEAD, so we # prepare a local file for upload to a location based on the tag cp -a libmongocrypt-all.asc libmongocrypt-all-${tag_upload_location}.asc if [[ "$tag_upload_location" = *-* ]]; then # Unstable release, like 1.1.0-beta1 or 1.0.1-rc0. mkdir unstable cp -a libmongocrypt-all.asc unstable/libmongocrypt-all-${tag_upload_location}.asc else mkdir stable cp -a libmongocrypt-all.asc stable/libmongocrypt-all-${tag_upload_location}.asc fi fi - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt-all.asc' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt/libmongocrypt-all.asc' content_type: 'application/pgp-signature' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${branch_name}/${libmongocrypt_s3_suffix_copy}/libmongocrypt-all.asc' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt/libmongocrypt-all.asc' content_type: 'application/pgp-signature' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/${tag_upload_location}/libmongocrypt-all.asc' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for tagged release. display_name: 'libmongocrypt-all-${tag_upload_location}.asc' local_file: 'libmongocrypt/libmongocrypt-all-${tag_upload_location}.asc' content_type: 'application/pgp-signature' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/latest/stable/libmongocrypt-all.asc' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for stable release. display_name: 'stable/libmongocrypt-all-${tag_upload_location}.asc' local_file: 'libmongocrypt/stable/libmongocrypt-all-${tag_upload_location}.asc' content_type: 'application/pgp-signature' - command: s3.put params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/all/latest/unstable/libmongocrypt-all.asc' bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} optional: true # Do not fail task if `local_file` does not exist. `local_file` only exists for unstable release. display_name: 'unstable/libmongocrypt-all-${tag_upload_location}.asc' local_file: 'libmongocrypt/unstable/libmongocrypt-all-${tag_upload_location}.asc' content_type: 'application/pgp-signature' - name: publish-packages depends_on: build-packages commands: - func: "fetch source" - func: "setup packaging credentials" - func: "publish packages" - name: windows-upload-release # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks. patchable: false # Garasign credentials are marked as "Admin only" in Evergreen and are not included in patch builds. depends_on: - variant: windows-test name: build-and-test-and-upload commands: - func: "fetch source" - command: shell.exec params: shell: bash working_dir: libmongocrypt script: |- set -o xtrace set -o errexit . .evergreen/init.sh libmongocrypt_release_version=$(uvx python ./etc/calc_release_version.py) case "$libmongocrypt_release_version" in *+*) # Not a tagged release. Use full release version suffix. upload_suffix="$libmongocrypt_release_version" ;; *-*) # Unstable release, like 1.1.0-beta1 or 1.0.1-rc0. Overwrite the latest unstable URL. upload_suffix='_unstable' ;; *) # Tagged release. Overwrite the latest stable URL. upload_suffix='' ;; esac # Add expansions used in later commands: echo "libmongocrypt_release_version: '$libmongocrypt_release_version'" > expansions.yml echo "upload_suffix: '$upload_suffix'" >> expansions.yml - command: expansions.update params: file: libmongocrypt/expansions.yml - command: s3.get # Download Windows build. params: role_arn: ${upload_arn} remote_file: 'libmongocrypt/windows-test/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt.tar.gz' bucket: ${upload_bucket} extract_to: libmongocrypt_download - command: shell.exec params: script: |- set -o xtrace set -o errexit # Move just the mongocrypt files needed into the final upload mkdir libmongocrypt_upload mkdir libmongocrypt_upload/bin mkdir libmongocrypt_upload/include mv libmongocrypt_download/bin/mongocrypt.dll libmongocrypt_upload/bin/mongocrypt.dll mv libmongocrypt_download/include/mongocrypt libmongocrypt_upload/include - command: archive.targz_pack params: target: libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz source_dir: libmongocrypt_upload include: [./**] - command: s3.put # Windows tarballs were previously documented as available from these fixed URLs: # (stable) : https://s3.amazonaws.com/mciuploads/libmongocrypt/windows/latest_release/libmongocrypt.tar.gz # (unstable): https://s3.amazonaws.com/mciuploads/libmongocrypt/windows/latest_release/libmongocrypt_unstable.tar.gz # Documentation now refers to the GitHub release page, which includes the per-release tarball. # The fixed URL upload is kept to avoid possibly breaking expectations. Consider removing in the future. params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/windows/latest_release/libmongocrypt${upload_suffix}.tar.gz' display_name: (Deprecated) libmongocrypt${upload_suffix}.tar.gz bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz' content_type: 'application/x-gzip' - command: s3.put # Upload tarball for GitHub Release. params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-windows-x86_64-${libmongocrypt_release_version}.tar.gz' display_name: libmongocrypt-windows-x86_64-${libmongocrypt_release_version}.tar.gz bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz' content_type: 'application/x-gzip' - command: shell.exec params: shell: bash script: |- set -o errexit # Copy file to sign into `libmongocrypt` directory to be used by Earthly. cp libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz libmongocrypt - func: "earthly" # Sign tarball. vars: args: --secret garasign_username=${garasign_username} --secret garasign_password=${garasign_password} +sign --file_to_sign=libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz --output_file=libmongocrypt_upload.asc - command: s3.put # Upload signature for GitHub Release. params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-windows-x86_64-${libmongocrypt_release_version}.asc' display_name: libmongocrypt-windows-x86_64-${libmongocrypt_release_version}.asc bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt/libmongocrypt_upload.asc' content_type: 'application/pgp-signature' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz - name: upload-release run_on: ubuntu2404-latest-small patchable: false # Garasign credentials are marked as "Admin only" in Evergreen and are not included in patch builds. To test a patch: temporarily unselect "Admin only". commands: - func: "fetch source" - command: s3.get # Download build. params: role_arn: ${upload_arn} remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt.tar.gz' bucket: ${upload_bucket} extract_to: libmongocrypt_download - command: shell.exec params: shell: bash script: |- set -o xtrace set -o errexit # Move just the mongocrypt files needed into the final upload mkdir libmongocrypt_upload if [[ "${release_os_arch}" == *"nocrypto"* ]]; then # Publish libmongocrypt library without crypto dependency. srcdir="libmongocrypt_download/nocrypto" else srcdir="libmongocrypt_download" fi # Move headers mkdir libmongocrypt_upload/include mv $srcdir/include/mongocrypt libmongocrypt_upload/include # Move library if [ -f "$srcdir/lib64/libmongocrypt.so" ]; then mkdir libmongocrypt_upload/lib64 mv $srcdir/lib64/libmongocrypt.so libmongocrypt_upload/lib64 elif [ -f "$srcdir/lib/libmongocrypt.so" ]; then mkdir libmongocrypt_upload/lib mv $srcdir/lib/libmongocrypt.so libmongocrypt_upload/lib elif [ -f "$srcdir/lib/libmongocrypt.dylib" ]; then mkdir libmongocrypt_upload/lib mv $srcdir/lib/libmongocrypt.dylib libmongocrypt_upload/lib elif [ -f "$srcdir/bin/mongocrypt.dll" ]; then mkdir libmongocrypt_upload/bin mv $srcdir/bin/mongocrypt.dll libmongocrypt_upload/bin fi - command: archive.targz_pack params: target: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz source_dir: libmongocrypt_upload include: [./**] - command: s3.put # Upload tarball for GitHub Release. params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz' display_name: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz' content_type: 'application/x-gzip' - command: shell.exec params: shell: bash script: |- set -o errexit # Copy file to sign into `libmongocrypt` directory to be used by Earthly. cp libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz libmongocrypt - func: "earthly" # Sign tarball. vars: args: --secret garasign_username=${garasign_username} --secret garasign_password=${garasign_password} +sign --file_to_sign=libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz --output_file=libmongocrypt_upload.asc - command: s3.put # Upload signature for GitHub Release. params: role_arn: ${upload_arn} skip_existing: true remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.asc' display_name: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.asc bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} local_file: 'libmongocrypt/libmongocrypt_upload.asc' content_type: 'application/pgp-signature' - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz - name: debian-package-build run_on: &deb-package-build-run_on - ubuntu2404-large - ubuntu2404-small tags: [packaging] commands: - func: "fetch source" - command: shell.exec type: test params: working_dir: "libmongocrypt" shell: bash script: |- set -o errexit set -o xtrace bash .evergreen/debian_package_build.sh --is-patch=${is_patch} --pkg-file-name=${tag_upload_location!|*revision} - command: s3.put params: role_arn: ${upload_arn} skip_existing: true local_file: deb-${tag_upload_location!|*revision}.tar.gz remote_file: libmongocrypt/${branch_name}/${revision}/${version_id}/${build_id}/${execution}/debian-packages.tar.gz bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} content_type: ${content_type|application/x-gzip} display_name: "deb.tar.gz" - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - deb-${tag_upload_location!|*revision}.tar.gz allowed_requesters: - ad_hoc - commit - # github_merge_queue - # github_pr - # github_tag - patch - trigger - name: debian-package-build-i386 run_on: *deb-package-build-run_on tags: [packaging] commands: - func: "fetch source" - command: shell.exec type: test params: working_dir: "libmongocrypt" shell: bash script: |- set -o errexit set -o xtrace bash .evergreen/debian_package_build.sh --arch=i386 --is-patch=${is_patch} --pkg-file-name=${tag_upload_location!|*revision} - command: s3.put params: role_arn: ${upload_arn} skip_existing: true local_file: deb-${tag_upload_location!|*revision}.tar.gz remote_file: libmongocrypt/${branch_name}/${revision}/${version_id}/${build_id}/${execution}/debian-packages-i386.tar.gz bucket: ${upload_bucket} permissions: ${upload_permissions} visibility: ${upload_visibility} content_type: ${content_type|application/x-gzip} display_name: "deb.tar.gz" - command: papertrail.trace params: key_id: ${papertrail_key_id} secret_key: ${papertrail_secret_key} product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}. filenames: - deb-${tag_upload_location!|*revision}.tar.gz - name: rpm-package-build tags: [packaging] run_on: &docker-distros # * The RHEL76-docker distro runs an old and unsupported version of Docker. # * (We require the --mount parameter) - ubuntu2204-large - debian10 - debian11 - amazon2 commands: - func: fetch source - func: earthly vars: args: +rpm-runtime-test allowed_requesters: - ad_hoc - commit - # github_merge_queue - # github_pr - # github_tag - patch - trigger - name: check-format tags: [misc] run_on: *docker-distros commands: - func: fetch source - func: earthly vars: args: +check-format - name: benchmark-python commands: - func: "fetch source" - command: "subprocess.exec" params: binary: bash working_dir: "./libmongocrypt" args: - "./.evergreen/benchmark-python.sh" - command: shell.exec params: script: | # We use the requester expansion to determine whether the data is from a mainline evergreen run or not if [ "${requester}" == "commit" ]; then is_mainline=true else is_mainline=false fi # Parse the username out of the order_id. Patches append the username. The Signal Processing Service (SPS) endpoint does not need the other information. parsed_order_id=$(echo "${revision_order_id}" | awk -F'_' '{print $NF}') # Submit the performance data to the SPS endpoint response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X 'POST' \ "https://performance-monitoring-api.corp.mongodb.com/raw_perf_results/cedar_report?project=${project_id}&version=${version_id}&variant=${build_variant}&order=$parsed_order_id&task_name=${task_name}&task_id=${task_id}&execution=${execution}&mainline=$is_mainline" \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d @libmongocrypt/bindings/python/results.json) http_status=$(echo "$response" | grep "HTTP_STATUS" | awk -F':' '{print $2}') response_body=$(echo "$response" | sed '/HTTP_STATUS/d') # We want to throw an error if the data was not successfully submitted if [ "$http_status" -ne 200 ]; then echo "Error: Received HTTP status $http_status" echo "Response Body: $response_body" exit 1 fi echo "Response Body: $response_body" echo "HTTP Status: $http_status" - name: sbom commands: - func: fetch source - func: sbom pre: # Update the evergreen expansion to dynamically set the ${libmongocrypt_s3_suffix} and ${libmongocrypt_s3_suffix_copy} expansions. - command: "shell.exec" params: # Uploads are prefixed with ${project}/${build_variant}/${branch_name|all} script: |- if [ "${is_patch}" = "true" ]; then # patch build. REMOTE_SUFFIX="${revision}/${version_id}" REMOTE_SUFFIX_COPY="latest/${version_id}" elif [ "${branch_name}" = "master" ]; then # waterfall build. REMOTE_SUFFIX="${revision}" REMOTE_SUFFIX_COPY="latest" else # waterfall build, not on master branch. REMOTE_SUFFIX="${revision}" REMOTE_SUFFIX_COPY="latest-${branch_name}" fi # If we are a non-patch build in the libmongocrypt-release project, we upload to a restricted # CDN S3 bucket. Otherwise, we upload to a less restricted bucket for convenience. The corresponding # role_arn_... values come from EVG project configuration variables stored on the EVG server if test "${is_patch}" = 'true' || test "${project}" != 'libmongocrypt-release'; then echo "Using upload bucket: mciuploads" echo "Uploads will be available to download at https://mciuploads.s3.amazonaws.com/" upload_bucket='mciuploads' upload_arn='${role_arn_for_mciuploads}' upload_permissions='public-read' upload_visibility='public' else echo "Using upload bucket: cdn-origin-libmongocrypt" echo "Uploads will be available to download at https://downloads.mongodb.org/" upload_bucket='cdn-origin-libmongocrypt' upload_arn='${role_arn_for_release}' upload_permissions='private' upload_visibility='signed' fi if test "${project}" = 'libmongocrypt'; then papertrail_project='libmongocrypt-dev' else papertrail_project='libmongocrypt-release' fi PROJECT_DIRECTORY="$(pwd)" echo "libmongocrypt_s3_suffix: $REMOTE_SUFFIX" echo "libmongocrypt_s3_suffix_copy: $REMOTE_SUFFIX_COPY" echo "project_directory: $PROJECT_DIRECTORY" echo "Upload S3 bucket: $upload_bucket" cat < expansion.yml libmongocrypt_s3_suffix: "$REMOTE_SUFFIX" libmongocrypt_s3_suffix_copy: "$REMOTE_SUFFIX_COPY" project_directory: "$PROJECT_DIRECTORY" upload_bucket: "$upload_bucket" upload_arn: "$upload_arn" upload_permissions: "$upload_permissions" upload_visibility: "$upload_visibility" papertrail_project: "$papertrail_project" EOT - command: expansions.update params: file: expansion.yml - func: "cleanup environment" post: - func: "cleanup environment" # NOTE: When adding a new variant, update the "upload-all" task. buildvariants: - name: ubuntu1604 display_name: "Ubuntu 16.04 64-bit" run_on: ubuntu1604-test expansions: packager_distro: ubuntu1604 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - ubuntu2004-small - name: macos_x86_64 display_name: "macOS (x86_64) 11" run_on: macos-1100 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan-mac - name: rhel72-zseries-test display_name: "RHEL 7.2 on zSeries" run_on: rhel72-zseries-test expansions: packager_distro: rhel72 packager_arch: s390x release_os_arch: linux-s390x-glibc_2_7-nocrypto tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - rhel70-small - name: upload-release depends_on: build-and-test-and-upload - name: rhel83-zseries # rhel83-zseries has a new enough g++ to build the C++ tests. # rhel72-zseries-test does not build the C++ tests. display_name: "RHEL 8.3 on zSeries" run_on: rhel83-zseries-small tasks: - build-and-test-and-upload - name: windows-test display_name: "Windows 2016" run_on: windows-64-vs2017-test expansions: vs_version: "15" vs_target_arch: amd64 release_os_arch: windows-x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - name: upload-release depends_on: build-and-test-and-upload - name: windows-test-python display_name: "Windows Python" run_on: windows-64-vsMulti-small tasks: - test-python-windows - name: python-release display_name: Python Release batchtime: 20160 # 14 days tags: ["release_python_tag"] tasks: - ".release_python_tag" - name: linux-64-amazon-ami display_name: "Amazon Linux" run_on: amazon1-2018-test expansions: packager_distro: amazon packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - rhel70-small - name: amazon2 display_name: "Amazon Linux 2" run_on: amazon2-test expansions: packager_distro: amazon2 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - rhel70-small - name: amazon2-arm64 display_name: "Amazon Linux 2 (arm64)" run_on: amazon2-arm64 expansions: packager_distro: amazon2 packager_arch: aarch64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - rhel70-small - name: amazon2023 display_name: "Amazon Linux 2023" run_on: amazon2023.0-small expansions: packager_distro: amazon2023 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: amazon2023-arm64 display_name: "Amazon Linux 2023 (arm64)" run_on: amazon2023-arm64-small expansions: packager_distro: amazon2023 packager_arch: aarch64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: debian11 display_name: "Debian 11" run_on: debian11-large expansions: packager_distro: debian11 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - ubuntu2004-small - name: debian13 display_name: "Debian 13" run_on: debian13-m6i-xlarge expansions: packager_distro: debian13 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - ubuntu2004-small - name: debian12 display_name: "Debian 12" run_on: debian12-large expansions: packager_distro: debian12 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - ubuntu2004-small - name: debian10 display_name: "Debian 10" run_on: debian10-test expansions: packager_distro: debian10 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - ubuntu2004-small - name: debian92 display_name: "Debian 9.2" run_on: debian92-test expansions: packager_distro: debian92 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - ubuntu2004-small - name: rhel-62-64-bit display_name: "RHEL 6.2 64-bit" run_on: rhel62-small expansions: compile_env: >- LIBMONGOCRYPT_COMPILE_FLAGS="-D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS" BUILD_TESTING=OFF tasks: - build-and-test-and-upload - build-and-test-shared-bson - name: rhel-70-64-bit display_name: "RHEL 7.0 64-bit" run_on: rhel70-small expansions: packager_distro: rhel70 packager_arch: x86_64 release_os_arch: linux-x86_64-glibc_2_7-nocrypto tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - rhel70-small - name: upload-release depends_on: build-and-test-and-upload - name: rhel-71-ppc64el display_name: "RHEL 7.1 ppc64el" run_on: rhel71-power8-test expansions: packager_distro: rhel71 packager_arch: ppc64le release_os_arch: linux-ppc64le-glibc_2_17-nocrypto tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - rhel70-small - name: upload-release depends_on: build-and-test-and-upload - name: rhel-80-64-bit display_name: "RHEL 8.0 64-bit" run_on: rhel80-test expansions: packager_distro: rhel80 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - test-python - test-python-integ - build-packages - name: publish-packages distros: - rhel70-small - name: rhel-82-arm64 display_name: "RHEL 8.2 arm64" run_on: rhel82-arm64-small expansions: packager_distro: rhel82 packager_arch: aarch64 release_os_arch: linux-arm64-glibc_2_17-nocrypto tasks: - build-and-test-and-upload - test-python - test-python-integ - build-packages - name: publish-packages distros: - rhel70-small - name: upload-release depends_on: build-and-test-and-upload - name: rhel-81-ppc64el display_name: "RHEL 8.1 ppc64el" run_on: rhel81-power8-small expansions: packager_distro: rhel81 packager_arch: ppc64le tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: rhel-83-zseries display_name: "RHEL 8.3 zSeries" run_on: rhel83-zseries-small expansions: packager_distro: rhel83 packager_arch: s390x tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: rhel-91-64-bit display_name: "RHEL 9.1 64-bit" run_on: rhel91-small expansions: packager_distro: rhel91 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: rhel-91-arm64 display_name: "RHEL 9.1 arm64" run_on: rhel91-arm64-small expansions: packager_distro: rhel91 packager_arch: aarch64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - rhel70-small - name: suse12-64 display_name: "SLES 12 64-bit" run_on: suse12-sp5-small expansions: packager_distro: suse12 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - rhel70-small - name: suse15-64 display_name: "SLES 15 64-bit" run_on: suse15-test expansions: packager_distro: suse15 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - rhel70-small - name: ubuntu1604-arm64 display_name: "Ubuntu 16.04 arm64" run_on: ubuntu1604-arm64-large expansions: packager_distro: ubuntu1604 packager_arch: arm64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu1804-64 display_name: "Ubuntu 18.04 64-bit" run_on: ubuntu1804-test expansions: packager_distro: ubuntu1804 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu1804-64-clang7 # ubuntu1804-64-clang7 is used to test Ubuntu 18.04 with Clang 7.0.1. # This is a supported configuration built by the MongoDB Server. # The MongoDB Server vendors libmongocrypt. Refer: MONGOCRYPT-501. display_name: "Ubuntu 18.04 64-bit clang7" run_on: ubuntu1804-test expansions: compile_env: >- CC=/opt/mongodbtoolchain/v3/bin/clang CXX=/opt/mongodbtoolchain/v3/bin/clang++ tasks: - build-and-test-and-upload - name: ubuntu1804-arm64 display_name: "Ubuntu 18.04 arm64" run_on: ubuntu1804-arm64-build expansions: packager_distro: ubuntu1804 packager_arch: arm64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu2004-64 display_name: "Ubuntu 20.04 64-bit" run_on: ubuntu2004-small expansions: packager_distro: ubuntu2004 packager_arch: x86_64 tasks: - clang-tidy - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - upload-java - build-packages - publish-packages - name: ubuntu2004-arm64 display_name: "Ubuntu 20.04 arm64" run_on: ubuntu2004-arm64-small expansions: packager_distro: ubuntu2004 packager_arch: arm64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu2404-64 display_name: "Ubuntu 24.04 64-bit" run_on: ubuntu2404-small expansions: packager_distro: ubuntu2404 packager_arch: x86_64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu2404-arm64 display_name: "Ubuntu 24.04 arm64" run_on: ubuntu2404-arm64-small expansions: packager_distro: ubuntu2404 packager_arch: arm64 tasks: - build-and-test-and-upload - build-packages - name: publish-packages distros: - ubuntu2004-small - name: ubuntu2204-64 display_name: "Ubuntu 22.04 64-bit" run_on: ubuntu2204-small expansions: packager_distro: ubuntu2204 packager_arch: x86_64 clang_env: CC=clang-12 CXX=clang++-12 tasks: - clang-tidy - build-and-test-and-upload - build-and-test-shared-bson - build-and-test-asan - build-and-test-ubsan - upload-java - build-packages - name: publish-packages distros: - ubuntu2004-small - windows-upload-release - name: ubuntu2204-arm64 display_name: "Ubuntu 22.04 arm64" run_on: ubuntu2204-arm64-small expansions: packager_distro: ubuntu2204 packager_arch: arm64 tasks: - build-and-test-and-upload - build-and-test-shared-bson - build-packages - name: publish-packages distros: - ubuntu2004-small - name: publish display_name: "Publish" run_on: ubuntu2404-latest-small tasks: - name: "upload-java" - name: "upload-all" - name: "sign-all" - name: packaging display_name: "Linux Distro Packaging" tasks: [.packaging] - name: misc display_name: Miscellaneous tasks: [.misc] - name: macos display_name: macOS m1 (Apple LLVM) run_on: macos-1100-arm64 expansions: compile_env: >- CMAKE_OSX_ARCHITECTURES="arm64;x86_64" CMAKE=/opt/homebrew/bin/cmake release_os_arch: macos-universal tasks: - build-and-test-and-upload - name: test-python distros: macos-14-arm64 - name: upload-release depends_on: build-and-test-and-upload - name: windows-vs2017-32bit # Test Windows 32 bit builds for PHPC. PHPC builds libmongocrypt from source. See MONGOCRYPT-391. display_name: "Windows VS 2017 32-bit compile" run_on: windows-64-vs2017-small expansions: compile_env: WINDOWS_32BIT=ON vs_version: "15" vs_target_arch: x86 tasks: - build-and-test-and-upload - name: benchmark display_name: "Benchmark" # rhel90-dbx-perf-large is the dedicated performance distro referenced in DRIVERS-2666. run_on: rhel90-dbx-perf-large tasks: - benchmark-python - name: alpine-amd64-earthly display_name: "Alpine Linux 3.23 amd64 (via Earthly)" expansions: earthly_env: alpine release_os_arch: linux-x86_64-musl_1_2-nocrypto tasks: - name: build-with-earthly run_on: ubuntu2404-latest-large - name: upload-release depends_on: build-with-earthly - name: alpine-arm64-earthly display_name: "Alpine Linux 3.23 arm64 (via Earthly)" expansions: earthly_env: alpine release_os_arch: linux-arm64-musl_1_2-nocrypto tasks: - name: build-with-earthly run_on: ubuntu2404-arm64-latest-large - name: upload-release depends_on: build-with-earthly - name: debian11-arm64-earthly display_name: "Debian 11 arm64 (via Earthly)" expansions: packager_distro: debian11 packager_arch: arm64 earthly_env: deb11 tasks: - name: build-deb-packages-with-earthly # Use an arm64 distro to match the intended target architecture of .deb packages. run_on: ubuntu2404-arm64-latest-large - name: publish-deb-packages-with-earthly # Use a distro suitable for running curator to publish .deb packages. # Use 22.04 for Python 3.10 to avoid failure installing pycrypto. run_on: ubuntu2204-latest-small - name: debian13-arm64-earthly display_name: "Debian 13 arm64 (via Earthly)" expansions: packager_distro: debian13 packager_arch: arm64 earthly_env: deb13 tasks: - name: build-deb-packages-with-earthly # Use an arm64 distro to match the intended target architecture of .deb packages. run_on: ubuntu2404-arm64-latest-large - name: publish-deb-packages-with-earthly # Use a distro suitable for running curator to publish .deb packages. # Use 22.04 for Python 3.10 to avoid failure installing pycrypto. run_on: ubuntu2204-latest-small - name: debian12-arm64-earthly display_name: "Debian 12 arm64 (via Earthly)" expansions: packager_distro: debian12 packager_arch: arm64 earthly_env: deb12 tasks: - name: build-deb-packages-with-earthly # Use an arm64 distro to match the intended target architecture of .deb packages. run_on: ubuntu2404-arm64-latest-large - name: publish-deb-packages-with-earthly # Use a distro suitable for running curator to publish .deb packages. # Use 22.04 for Python 3.10 to avoid failure installing pycrypto. run_on: ubuntu2204-latest-small - name: sbom display_name: SBOM tasks: - name: sbom # Just for high host availability. Not platform-specific. run_on: rhel80 libmongocrypt-1.19.0/.evergreen/create-packages-and-repos.sh000066400000000000000000000061621521103432300237720ustar00rootroot00000000000000#!/bin/bash . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" # Generate an error if these are unset: : "$PACKAGER_DISTRO" "$PACKAGER_ARCH" if test -d "$WORKDIR/venv"; then if test "$OS" = "Windows_NT"; then # Need to quote the path on Windows to preserve the separator. . "$WORKDIR/venv/Scripts/activate" 2> /tmp/activate_error.log else . "$WORKDIR/venv/bin/activate" 2> /tmp/activate_error.log fi if test $? -ne 0; then echo "Failed to activate virtualenv: $(cat /tmp/activate_error.log)" fi python=python else # Require PYTHON be set: : "${PYTHON:?}" python="${PYTHON}" fi export PYTHONPATH : "${PYTHONPATH:=}" if test "$OS" = "Windows_NT"; then PYTHONPATH="$PYTHONPATH;$(cygpath -w "$WORKDIR/src")" else PYTHONPATH="$PYTHONPATH:$WORKDIR/src" fi # Get current version of libmongocrypt. pushd "$LIBMONGOCRYPT_DIR" mongocrypt_version="$("$python" etc/calc_release_version.py)" popd PPA_BUILD_ONLY=1 "$LIBMONGOCRYPT_DIR/.evergreen/build_all.sh" pkg_version=$mongocrypt_version tar -zcv -C install \ --transform="s|^libmongocrypt/|libmongocrypt-$pkg_version/|" \ --exclude=nocrypto \ --exclude=sharedbson \ -f "libmongocrypt-$pkg_version.tar.gz" \ libmongocrypt pushd "$LIBMONGOCRYPT_DIR/" (git remote | grep -q upstream) || git remote add upstream https://github.com/mongodb/libmongocrypt git fetch upstream git checkout $(git rev-parse upstream/debian/unstable) -- debian # The 1.16.0-1 Debian package bumped the debhelper compatibility level to 13, but this level # isn't supported on some of the older (now unsupported) versions we still build for; so, # patch back to a lower level for the versions that don't support the current level if [[ ! -x /usr/bin/dh_assistant ]]; then patch -p1 < Uploaders: Kevin Albertson , Roberto C. Sanchez -Build-Depends: debhelper-compat (= 13), +Build-Depends: debhelper (>= 10), cmake, libssl-dev, pkgconf, EOF else # even on the newer versions, we need to specify via debian/compat, owing to the # barebones debian/control file we use for PPA packaging echo 13 > debian/compat fi popd pushd "$LIBMONGOCRYPT_DIR/etc/" # The files from libmongocrypt/debian/ are the official maintainer scripts, # but libmongocrypt/etc/debian/ contains a few custom scripts that are # meant to support the packager.py workflow. This step "fills in" around # those custom scripts. cp -nr ../debian/* debian/ command "$python" ./packager.py \ --prefix "$LIBMONGOCRYPT_DIR" \ --distros "$PACKAGER_DISTRO" \ --tarball "$LIBMONGOCRYPT_DIR-$pkg_version.tar.gz" \ --library-version "$pkg_version" \ --metadata-gitspec HEAD \ --arches "$PACKAGER_ARCH" popd libmongocrypt-1.19.0/.evergreen/debian_package_build.sh000066400000000000000000000065051521103432300231420ustar00rootroot00000000000000#!/bin/env bash # # Test libmongocrypt's Debian packaging scripts. # # Supported options: # --is-patch={true,false} # If "true", this is an Evergreen patch build. (Default 'false') # --arch= # If specified, sets the "--arch" option for debootstrap. set -euxo pipefail IS_PATCH=false _dbs_args=() for arg in "$@"; do case $arg in --arch=*) a="${arg#*=}" _dbs_args+=(--arch "$a") ;; --is-patch=*) IS_PATCH="${arg#*=}" ;; --pkg-file-name=*) PKG_FILE_NAME="-${arg#*=}" ;; *) echo "Unknown argument '$arg'" exit 1 ;; esac done on_exit () { sudo umount ./unstable-chroot/proc ./unstable-chroot/sys if [ -e ./unstable-chroot/debootstrap/debootstrap.log ]; then echo "Dumping debootstrap.log" cat ./unstable-chroot/debootstrap/debootstrap.log fi } trap on_exit EXIT if [ "${IS_PATCH}" = "true" ]; then git diff HEAD > ../upstream.patch git clean -fdx git reset --hard HEAD (git remote | grep -q upstream) || git remote add upstream https://github.com/mongodb/libmongocrypt git fetch upstream git checkout $(git rev-parse upstream/debian/unstable) -- debian if [ -s ../upstream.patch ]; then [ -d debian/patches ] || mkdir debian/patches mv ../upstream.patch debian/patches/ echo upstream.patch >> debian/patches/series git add debian/patches/* git commit -m 'Evergreen patch build - upstream changes' git log -n1 -p fi fi export CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" cd .. _dbs_args+=(unstable) git clone https://salsa.debian.org/installer-team/debootstrap.git debootstrap.git export DEBOOTSTRAP_DIR=`pwd`/debootstrap.git sudo -E ./debootstrap.git/debootstrap "${_dbs_args[@]}" ./unstable-chroot/ http://cdn-aws.deb.debian.org/debian sudo mount sysfs ./unstable-chroot/sys -t sysfs sudo mount proc ./unstable-chroot/proc -t proc cp -a libmongocrypt ./unstable-chroot/tmp/ sudo chroot ./unstable-chroot /bin/bash -c '(set -o xtrace && \ apt-get install -y build-essential git-buildpackage fakeroot debhelper cmake curl ca-certificates libssl-dev pkg-config libbson-dev libintelrdfpmath-dev python3-packaging && \ chown -R root:root /tmp/libmongocrypt && \ cd /tmp/libmongocrypt && \ git clean -fdx && \ git reset --hard HEAD && \ python3 etc/calc_release_version.py > VERSION_CURRENT && \ git add --force VERSION_CURRENT && \ git commit VERSION_CURRENT -m "Set current version" && \ (git remote | grep -q upstream) || git remote add upstream https://github.com/mongodb/libmongocrypt git fetch upstream git checkout $(git rev-parse upstream/debian/unstable) -- debian LANG=C /bin/bash ./debian/build_snapshot.sh && \ debc ../*.changes && \ dpkg -i ../*.deb && \ /usr/bin/gcc $(pkgconf --cflags libmongocrypt bson2) -o example-state-machine test/example-state-machine.c -lmongocrypt -lbson2 )' [ -e ./unstable-chroot/tmp/libmongocrypt/example-state-machine ] || (echo "Example 'example-state-machine' was not built!" ; exit 1) (cd ./unstable-chroot/tmp/ ; tar zcvf "../../deb${PKG_FILE_NAME}.tar.gz" *.dsc *.orig.tar.gz *.debian.tar.xz *.build *.deb) # Build a second time, to ensure a "double build" works sudo chroot ./unstable-chroot /bin/bash -c "(\ cd /tmp/libmongocrypt && \ rm -f example-state-machine && \ git status --ignored && \ dpkg-buildpackage -b && dpkg-buildpackage -S )" libmongocrypt-1.19.0/.evergreen/earthly.sh000077500000000000000000000023031521103432300205310ustar00rootroot00000000000000#!/usr/bin/env bash . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" set -euo pipefail : "${EARTHLY_VERSION:=0.8.16}" # Calc the arch of the executable we want arch="$(uname -m)" case "$arch" in x86_64) arch=amd64 ;; aarch64|arm64) arch=arm64 ;; *) echo "Unknown architecture: $arch" 1>&1 exit 99 ;; esac # The location where the Earthly executable will live cache_dir="$USER_CACHES_DIR/earthly-sh/$EARTHLY_VERSION" mkdir -p "$cache_dir" exe_filename="earthly-$OS_NAME-$arch$EXE_SUFFIX" if [[ "$OS_NAME" == "macos" ]]; then # Earthly downloads use `darwin`. exe_filename="earthly-darwin-$arch$EXE_SUFFIX" fi exe_path="$cache_dir/$exe_filename" if test -f "$exe_path" && ! "$exe_path" --version; then echo "Failed to execute Earthly executable, removing and re-downloading" rm "$exe_path" fi # Download if it isn't already present if ! test -f "$exe_path"; then echo "Downloading $exe_filename $EARTHLY_VERSION" url="https://github.com/earthly/earthly/releases/download/v$EARTHLY_VERSION/$exe_filename" curl --retry 5 -LsS --max-time 120 --fail "$url" --output "$exe_path" fi chmod a+x "$exe_path" "$exe_path" "$@" libmongocrypt-1.19.0/.evergreen/env-run.sh000066400000000000000000000013051521103432300204510ustar00rootroot00000000000000#!/usr/bin/env bash # Executes a subcommand using a VS environment if one is requested, otherwise # just executes the command with no modified environment set -eu if test -n "${VS_VERSION-}"; then here="$(dirname "${BASH_SOURCE[0]}")" # Set CC and CXX to force CMake to use cl.exe even if GCC/Clang is visible on PATH env _run_argv="$*" \ CC=cl \ CXX=cl \ powershell -NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted \ -Command "$here/vs-env-run.ps1" \ -Version "$VS_VERSION*" \ -TargetArch "${VS_TARGET_ARCH-amd64}" \ -Command "{ & Invoke-Expression \$env:_run_argv }" else command "$@" fi libmongocrypt-1.19.0/.evergreen/init.sh000066400000000000000000000154751521103432300200370ustar00rootroot00000000000000#!/bin/bash # Initial variables and helper functions for the libmongocrypt build ## Variables set by this file: # EVG_DIR = The path to the directory containing this script file # LIBMONGOCRYPT_DIR = The path to the libmongocrypt source directory # OS_NAME = One of 'windows', 'linux', 'macos', or 'unknown' ## (All of the above directory paths are native absolute paths) ## This script defines the following commands: # * abspath # Convert a given path into an absolute path. Relative paths are # resolved relative to the working directory. # # * have_command # Return zero if is the name of a command that can be executed, # returns non-zero otherwise. # # * run_chdir [args ...] # Run the given command with a working directory given by # # * log # Print to stderr # # * fail # Print to stderr and return non-zero # # * native_path # On MinGW/Cygwin/MSYS, convert the given Cygwin path to a Windows-native # path. NOTE: the MinGW runtime will almost always automatically convert # filepaths automatically when passed to non-MinGW programs, so this # utility is not usually needed. set -o errexit set -o pipefail set -o nounset # Inhibit msys path conversion export MSYS2_ARG_CONV_EXCL="*" if test "${TRACE:-0}" != "0"; then set -o xtrace fi # Write a message to stderr function log() { echo "${@}" 1>&2 return 0 } function debug() { if test "${DEBUG:-0}" != "0"; then log "${@}" fi } # Print a message and return non-zero function fail() { log "${@}" return 1 } # Determine whether we can execute the given name as a command function have_command() { test "$#" -eq 1 || fail "have_command expects a single argument" if type "${1}" > /dev/null 2>&1; then return 0 fi return 1 } # Run a command in a different directory: # * run_chdir [command ...] function run_chdir() { test "$#" -gt 1 || fail "run_chdir expects at least two arguments" local _dir="$1" shift pushd "$_dir" > /dev/null debug "Run in directory [$_dir]:" "$@" "$@" local _rc=$? popd > /dev/null return $_rc } # Given a path string, convert it to an absolute path with no redundant components or directory separators function abspath() { test "$#" -eq 1 || fail "abspath expects a single argument" local ret local arg="$1" debug "Resolve path [$arg]" # The parent path: local _parent _parent="$(dirname "$arg")" # The filename part: local _fname _fname="$(basename "$arg")" # There are four cases to consider from dirname: if test "$_parent" = "."; then # The parent is '.' as in './foo' # Replace the leading '.' with the working directory _parent="$PWD" elif test "$_parent" = ".."; then # The parent is '..' as in '../foo' # Replace a leading '..' with the parent of the working directory _parent="$(dirname "$PWD")" elif test "$arg" = "$_parent"; then # The parent is itself, as in '/' # A root directory is its own parent according to 'dirname' printf %s "$arg" return 0 else # The parent is some other path, like 'foo' in 'foo/bar' # Resolve the parent path _parent="$(set +x; DEBUG=0 abspath "$_parent")" fi # At this point $_parent is an absolute path if test "$_fname" = ".."; then # Strip one component ret="$(dirname "$_parent")" elif test "$_fname" = "."; then # Drop a '.' at the end of a path ret="$_parent" else # Join the result ret="$_parent/$_fname" fi # Remove duplicate dir separators while [[ "$ret" =~ "//" ]]; do ret="${ret//\/\///}" done debug "Resolved to: [$arg] -> [$ret]" printf %s "$ret" } # Get the platform name: One of 'windows', 'macos', 'linux', or 'unknown' function os_name() { have_command uname || fail "No 'uname' executable found" debug "Uname is [$(uname -a)]" local _uname _uname="$(uname -a | tr '[:upper:]' '[:lower:]')" local _os_name="unknown" if [[ "$_uname" =~ .*cygwin|windows|mingw|msys.* ]] || (have_command cmd.exe && ! [[ $_uname =~ .*wsl.* ]]); then # We are running on Windows, and not within a WSL environment _os_name="windows" elif [[ $_uname =~ darwin.* ]]; then _os_name='macos' elif [[ $_uname =~ linux.* ]]; then _os_name='linux' fi printf %s "$_os_name" } # Ensure the given path is in a native format (converts cygwin paths to Windows-local paths) function native_path() { test "$#" -eq 1 || fail "native_path expects one argument" if test "$OS_NAME" = "windows"; then have_command cygpath || fail "No 'cygpath' command is available, but we require it to normalize file paths." debug "Convert path [$1]" local r r="$(cygpath -w "$1")" debug "Convert to [$r]" printf %s "$r" else printf %s "$1" fi } # Join the given arguments with the given joiner string. Writes to stdout # Usage: join_str [argv [...]] function join_str() { local joiner first joiner="$1" first="${2-}" if shift 2; then # Print each element. Do a string-replace of the beginning of each # subsequent string with the joiner. printf "%s" "$first" "${@/#/$joiner}" fi } OS_NAME="$(os_name)" _init_sh_this_file="$(abspath "${BASH_SOURCE[0]}")" _init_sh_evg_dir="$(dirname "${_init_sh_this_file}")" # Get the EVG dir as a native absolute path. All other path vars are derived from # this one, and will therefore remain as native paths EVG_DIR="$(native_path "${_init_sh_evg_dir}")" LIBMONGOCRYPT_DIR="$(dirname "${EVG_DIR}")" is_true() { declare var="$1" declare val="${!var-}" # Default is '' empty case "$val" in 1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0;; 0|false|FALSE|False|no|NO|No|off|OFF|Off|"") return 1;; *) log "Unknown 'boolean' value for \$$var: '$val'" return 2;; esac } is_false() { ! is_true "$@" } EXE_SUFFIX="" if test "$OS_NAME" = "windows"; then EXE_SUFFIX=".exe" fi if test "${USER_CACHES_DIR:=${XDG_CACHE_HOME:-}}" = ""; then case "$OS_NAME" in linux) USER_CACHES_DIR=$HOME/.cache ;; macos) USER_CACHES_DIR=$HOME/Library/Caches ;; windows) USER_CACHES_DIR=${LOCALAPPDATA:-$USERPROFILE/.cache} ;; *) log "Using ~/.cache as fallback user caching directory" USER_CACHES_DIR="$(abspath ~/.cache)" esac fi # Ensure we are dealing with a complete path USER_CACHES_DIR="$(abspath "$USER_CACHES_DIR")" : "${BUILD_CACHE_BUST:=1}" : "${BUILD_CACHE_DIR:="$USER_CACHES_DIR/libmongocrypt/build.$BUILD_CACHE_BUST"}" # Silence shellcheck: : "$LIBMONGOCRYPT_DIR,$EXE_SUFFIX" libmongocrypt-1.19.0/.evergreen/init.test.sh000066400000000000000000000020761521103432300210060ustar00rootroot00000000000000#!/bin/bash # Test shell utilities. . "$(dirname "${BASH_SOURCE[0]}")/init.sh" function assert_eq() { if [ "$1" != "$2" ]; then echo "${BASH_SOURCE[0]}:${BASH_LINENO[0]} assertion failed: '$1' != '$2'" return 1 fi } function test_abspath() { mkdir -p /tmp/a/b/c cd /tmp/a/b/c got=$(abspath .) expect=/tmp/a/b/c assert_eq "$got" "$expect" got=$(abspath ..) expect=/tmp/a/b assert_eq "$got" "$expect" got=$(abspath .././foo.txt) expect=/tmp/a/b/foo.txt assert_eq "$got" "$expect" got=$(abspath /foo.txt) expect=/foo.txt assert_eq "$got" "$expect" got=$(abspath /tmp/a/../a/foo.txt) expect=/tmp/a/foo.txt assert_eq "$got" "$expect" got=$(abspath /tmp//a//b//c//foo.txt) expect=/tmp/a/b/c/foo.txt assert_eq "$got" "$expect" pushd /tmp > /dev/null got=$(abspath ./a/b/c/foo.txt) expect=/tmp/a/b/c/foo.txt assert_eq "$got" "$expect" popd > /dev/null got=$(abspath /a/b/c/foo.txt) expect=/a/b/c/foo.txt assert_eq "$got" "$expect" } test_abspath libmongocrypt-1.19.0/.evergreen/install-build-tools.sh000077500000000000000000000063031521103432300227660ustar00rootroot00000000000000#!/usr/bin/env bash export_uv_tool_dirs() { : "${UV_TOOL_DIR:="$(mktemp -d)"}" || return : "${UV_TOOL_BIN_DIR:="$(mktemp -d)"}" || return PATH="${UV_TOOL_BIN_DIR:?}:${PATH:-}" # Windows requires "C:\path\to\dir" instead of "/cygdrive/c/path/to/dir" (PATH is automatically converted). if [[ "${OSTYPE:?}" == cygwin ]]; then UV_TOOL_DIR="$(cygpath -aw "${UV_TOOL_DIR:?}")" || return UV_TOOL_BIN_DIR="$(cygpath -aw "${UV_TOOL_BIN_DIR:?}")" || return fi UV_PYTHON_INSTALL_DIR="${UV_TOOL_DIR:?}" export PATH UV_TOOL_DIR UV_TOOL_BIN_DIR UV_PYTHON_INSTALL_DIR } install_build_tools() { export_uv_tool_dirs || return declare rhel6 rhel7 rhel6="$([[ -f /etc/redhat-release ]] && perl -lne "m|Red Hat Enterprise Linux Server release 6(?:\.\d+)?| || exit 1" /etc/redhat-release && echo "1" || echo "0")" || true rhel7="$([[ -f /etc/redhat-release ]] && perl -lne "m|Red Hat Enterprise Linux Server release 7(?:\.\d+)?| || exit 1" /etc/redhat-release && echo "1" || echo "0")" || true # Temporary workarounds for rhel-62-64-bit and rhel72-zseries-test. To be removed. if [[ "${rhel6:?}" == "1" || "${rhel7:?}" == "1" ]]; then export UV_PYTHON="/opt/mongodbtoolchain/v4/bin/python3" fi if ! command -v uv &>/dev/null; then echo "missing system-provided uv binary: fallback to uv-installer.sh" >&2 : "${EVG_DIR:="$(dirname "${BASH_SOURCE[0]}")"}" || return . "${EVG_DIR:?}/init.sh" || return version="$(perl -lne 'm|APP_VERSION=\"(.*)\"| && print $1' "${EVG_DIR:?}/uv-installer.sh")" || return uv_install_dir="${USER_CACHES_DIR:?}/uv-$version" script="$(mktemp)" || return cp -f "${EVG_DIR:?}/uv-installer.sh" "${script:?}" || return chmod +x "${script:?}" || return env \ UV_INSTALL_DIR="${uv_install_dir:?}" \ UV_UNMANAGED_INSTALL=1 \ INSTALLER_PRINT_VERBOSE=1 \ "${script:?}" || return PATH="${uv_install_dir:?}:$PATH" uv --version || return fi if [[ "${rhel7:?}" == "1" ]]; then uv tool install -q cmake || return ln -sf /opt/mongodbtoolchain/v4/bin/ninja "${UV_TOOL_BIN_DIR:?}/ninja" || return elif [[ "${rhel6:?}" == "1" ]]; then ln -sf /opt/mongodbtoolchain/v4/bin/cmake "${UV_TOOL_BIN_DIR:?}/cmake" || return ln -sf /opt/mongodbtoolchain/v4/bin/ctest "${UV_TOOL_BIN_DIR:?}/ctest" || return ln -sf /opt/mongodbtoolchain/v4/bin/ninja "${UV_TOOL_BIN_DIR:?}/ninja" || return else # PyPI `cmake` requires a sufficiently recent Python version. uv python install --no-bin -q || uv python install -q || return uv tool install -q cmake || return if [[ -f /etc/redhat-release && -x /opt/mongodbtoolchain/v4/bin/ninja ]]; then # Avoid strange "Could NOT find Threads" CMake configuration error on RHEL when using PyPI CMake, PyPI Ninja, and # C++20 or newer by using MongoDB Toolchain's Ninja binary instead. ln -sf /opt/mongodbtoolchain/v4/bin/ninja "${UV_TOOL_BIN_DIR:?}/ninja" || return else uv tool install -q ninja || return fi fi uvx python --version || return cmake --version | head -n 1 || return echo "ninja version: $(ninja --version)" || return if [[ "${OSTYPE:?}" != "cygwin" ]]; then export CMAKE_GENERATOR="${CMAKE_GENERATOR:="Ninja"}" fi } libmongocrypt-1.19.0/.evergreen/linker-tests.sh000077500000000000000000000074051521103432300215150ustar00rootroot00000000000000#!/bin/bash # Directory layout # .evergreen # -linker_tests_deps # --app # --bson_patches # # linker_tests (created by this script) # -libmongocrypt-cmake-build (for artifacts built from libmongocrypt source) # -app-cmake-build # -mongo-c-driver # --cmake-build # -install # --bson1 # --bson2 # --libmongocrypt # . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" linker_tests_root="$LIBMONGOCRYPT_DIR/linker_tests" linker_tests_deps_root="$EVG_DIR/linker_tests_deps" rm -rf -- "$linker_tests_root" mkdir -p "$linker_tests_root"/{install,libmongocrypt-cmake-build,app-cmake-build} echo "Make libbson1 ..." run_chdir "$linker_tests_root" bash "$EVG_DIR/prep_c_driver_source.sh" MONGOC_DIR="$linker_tests_root/mongo-c-driver" # Disable extra alignment in libbson and libmongocrypt to ensure agreement. # libmongocrypt disables by default, but may enable if a system install of libbson is detected with extra alignment. common_cmake_args=( -DENABLE_EXTRA_ALIGNMENT=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo # Toggle building of tests -D BUILD_TESTING="${BUILD_TESTING-TRUE}" ) run_chdir "$MONGOC_DIR" git apply --ignore-whitespace "$linker_tests_deps_root/bson_patches/libbson1.patch" # Apply patches to fix compile on RHEL 6.2. TODO: try to remove once RHEL 6.2 is dropped (MONGOCRYPT-688). run_chdir "$MONGOC_DIR" git apply "$LIBMONGOCRYPT_DIR/etc/libbson-remove-GCC-diagnostic-pragma.patch" run_chdir "$MONGOC_DIR" git apply "$LIBMONGOCRYPT_DIR/etc/mongo-common-test-harness.patch" . "$(dirname "${BASH_SOURCE[0]}")/install-build-tools.sh" install_build_tools BUILD_PATH="$MONGOC_DIR/cmake-build" BSON1_INSTALL_PATH="$linker_tests_root/install/bson1" SRC_PATH="$MONGOC_DIR" cmake \ -DENABLE_MONGOC=OFF \ "${common_cmake_args[@]}" \ -DCMAKE_INSTALL_PREFIX="$BSON1_INSTALL_PATH" \ "-H$SRC_PATH" \ "-B$BUILD_PATH" cmake --build "$BUILD_PATH" --target install --config RelWithDebInfo echo "Make libbson1 ... done" echo "Prepare libbson2 ..." run_chdir "$MONGOC_DIR" git reset --hard run_chdir "$MONGOC_DIR" git apply --ignore-whitespace "$linker_tests_deps_root/bson_patches/libbson2.patch" # Apply patch to fix compile on RHEL 6.2. TODO: try to remove once RHEL 6.2 is dropped (MONGOCRYPT-688). run_chdir "$MONGOC_DIR" git apply "$LIBMONGOCRYPT_DIR/etc/libbson-remove-GCC-diagnostic-pragma.patch" run_chdir "$MONGOC_DIR" git apply "$LIBMONGOCRYPT_DIR/etc/mongo-common-test-harness.patch" LIBBSON2_SRC_DIR="$MONGOC_DIR" echo "Prepare libbson2 ... done" echo "Build libmongocrypt, static linking against libbson2 ..." BUILD_DIR="$linker_tests_root/libmongocrypt-cmake-build" LMC_INSTALL_PATH="$linker_tests_root/install/libmongocrypt" SRC_PATH="$LIBMONGOCRYPT_DIR" cmake \ "-DMONGOCRYPT_MONGOC_DIR=$LIBBSON2_SRC_DIR" \ "${common_cmake_args[@]}" \ -DCMAKE_INSTALL_PREFIX="$LMC_INSTALL_PATH" \ "-H$SRC_PATH" \ "-B$BUILD_DIR" cmake --build "$BUILD_DIR" --target install --config RelWithDebInfo echo "Build libmongocrypt, static linking against libbson2 ... done" echo "Test case: Model libmongoc's use ..." # app links against libbson1.so # app links against libmongocrypt.so BUILD_DIR="$linker_tests_root/app-cmake-build" PREFIX_PATH="$LMC_INSTALL_PATH;$BSON1_INSTALL_PATH" SRC_PATH="$linker_tests_deps_root/app" cmake \ "${common_cmake_args[@]}" \ -DCMAKE_PREFIX_PATH="$PREFIX_PATH" \ "-H$SRC_PATH" \ "-B$BUILD_DIR" cmake --build "$BUILD_DIR" --target app --config RelWithDebInfo export PATH="$PATH:$BSON1_INSTALL_PATH/bin:$LMC_INSTALL_PATH/bin" APP_CMD="$BUILD_DIR/app" check_output () { output="$($APP_CMD)" if [[ "$output" != *"$1"* ]]; then printf " Got: %s\nExpected: %s\n" "$output" "$1" exit 1; fi echo "ok" } check_output ".calling bson_malloc0..from libbson1..calling mongocrypt_binary_new..from libbson2." echo "Test case: Model libmongoc's use ... done" exit 0 libmongocrypt-1.19.0/.evergreen/linker_tests_deps/000077500000000000000000000000001521103432300222455ustar00rootroot00000000000000libmongocrypt-1.19.0/.evergreen/linker_tests_deps/app/000077500000000000000000000000001521103432300230255ustar00rootroot00000000000000libmongocrypt-1.19.0/.evergreen/linker_tests_deps/app/CMakeLists.txt000066400000000000000000000004721521103432300255700ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.15...4.0) project (app C) add_executable (app app.c) find_package (bson REQUIRED) message ("-- libbson found version \"${bson_VERSION}\"") target_link_libraries (app PRIVATE bson::static) find_package (mongocrypt REQUIRED) target_link_libraries (app PRIVATE mongo::mongocrypt) libmongocrypt-1.19.0/.evergreen/linker_tests_deps/app/app.c000066400000000000000000000005111521103432300237460ustar00rootroot00000000000000#include #include int main () { char *a; mongocrypt_binary_t *b; printf(".calling bson_malloc0."); a = bson_malloc0 (1); printf(".calling mongocrypt_binary_new."); b = mongocrypt_binary_new (); bson_free (a); mongocrypt_binary_destroy (b); return 0; }libmongocrypt-1.19.0/.evergreen/linker_tests_deps/bson_patches/000077500000000000000000000000001521103432300247155ustar00rootroot00000000000000libmongocrypt-1.19.0/.evergreen/linker_tests_deps/bson_patches/libbson1.patch000066400000000000000000000012371521103432300274520ustar00rootroot00000000000000Adds a printf to bson_malloc0 to test linking scenarios with two forms of libbson. See linker-tests.sh. If this patch fails to apply, regenerate from libbson's source. diff --git a/src/libbson/src/bson/memory.c b/src/libbson/src/bson/memory.c index 0d84ea53e5..ad0f8eb0aa 100644 --- a/src/libbson/src/bson/memory.c +++ b/src/libbson/src/bson/memory.c @@ -140,6 +140,7 @@ bson_malloc0(size_t num_bytes) /* IN */ { void *mem = NULL; + printf(".from libbson1."); if (BSON_LIKELY(num_bytes)) { if (BSON_UNLIKELY(!(mem = gMemVtable.calloc(1, num_bytes)))) { fprintf(stderr, "Failure to allocate memory in bson_malloc0(). errno: %d.\n", errno); libmongocrypt-1.19.0/.evergreen/linker_tests_deps/bson_patches/libbson2.patch000066400000000000000000000012371521103432300274530ustar00rootroot00000000000000Adds a printf to bson_malloc0 to test linking scenarios with two forms of libbson. See linker-tests.sh. If this patch fails to apply, regenerate from libbson's source. diff --git a/src/libbson/src/bson/memory.c b/src/libbson/src/bson/memory.c index 0d84ea53e5..ad0f8eb0aa 100644 --- a/src/libbson/src/bson/memory.c +++ b/src/libbson/src/bson/memory.c @@ -140,6 +140,7 @@ bson_malloc0(size_t num_bytes) /* IN */ { void *mem = NULL; + printf(".from libbson2."); if (BSON_LIKELY(num_bytes)) { if (BSON_UNLIKELY(!(mem = gMemVtable.calloc(1, num_bytes)))) { fprintf(stderr, "Failure to allocate memory in bson_malloc0(). errno: %d.\n", errno); libmongocrypt-1.19.0/.evergreen/pkgconfig-tests.sh000077500000000000000000000162721521103432300222020ustar00rootroot00000000000000#!/bin/bash . "$(dirname "${BASH_SOURCE[0]}")/setup-env.sh" if ! have_command pkg-config; then echo "pkg-config not present on this platform; skipping test ..." exit 0 fi : "${CFLAGS:=}" pkgconfig_tests_root=$LIBMONGOCRYPT_DIR/_build/pkgconfig_tests rm -rf "$pkgconfig_tests_root" mongoc_src_dir="$pkgconfig_tests_root/mongo-c-driver" mkdir -p "$mongoc_src_dir" run_chdir "$pkgconfig_tests_root" "$EVG_DIR/prep_c_driver_source.sh" # Disable extra alignment in libbson and libmongocrypt to ensure agreement. # libmongocrypt disables by default, but may enable if a system install of libbson is detected with extra alignment. common_cmake_args=( -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_EXTRA_ALIGNMENT=OFF # Toggle building of tests -D BUILD_TESTING="${BUILD_TESTING-TRUE}" ) # Apply patches to fix compile on RHEL 6.2. TODO: try to remove once RHEL 6.2 is dropped (MONGOCRYPT-688). run_chdir "$mongoc_src_dir" git apply "$LIBMONGOCRYPT_DIR/etc/libbson-remove-GCC-diagnostic-pragma.patch" run_chdir "$mongoc_src_dir" git apply "$LIBMONGOCRYPT_DIR/etc/mongo-common-test-harness.patch" . "$(dirname "${BASH_SOURCE[0]}")/install-build-tools.sh" install_build_tools echo "Building libbson ..." libbson_install_dir="$pkgconfig_tests_root/install/libbson" build_dir="$mongoc_src_dir/_build" cmake -DENABLE_MONGOC=OFF \ "${common_cmake_args[@]}" \ -DCMAKE_INSTALL_PREFIX="$libbson_install_dir" \ -H"$mongoc_src_dir" \ -B"$build_dir" cmake --build "$build_dir" --target install --config RelWithDebInfo libbson_pkg_config_path="$(native_path "$(dirname "$(find "$libbson_install_dir" -name bson2.pc)")")" echo "Building libbson ... done" echo "Build libmongocrypt, static linking against libbson and configured for the PPA ..." mongocrypt_install_dir="$pkgconfig_tests_root/install/libmongocrypt" build_dir=$pkgconfig_tests_root/mongocrypt-build cmake -DUSE_SHARED_LIBBSON=OFF \ -DENABLE_BUILD_FOR_PPA=ON \ "${common_cmake_args[@]}" \ -DCMAKE_INSTALL_PREFIX="$mongocrypt_install_dir" \ -H"$LIBMONGOCRYPT_DIR" \ -B"$build_dir" cmake --build "$build_dir" --target install --config RelWithDebInfo echo "Build libmongocrypt, static linking against libbson and configured for the PPA ... done" # To validate the pkg-config scripts, we don't want the libbson script to be visible mongocrypt_pkg_config_path="$(native_path "$(dirname "$(find "$mongocrypt_install_dir" -name libmongocrypt.pc)")")" export PKG_CONFIG_PATH PKG_CONFIG_PATH="$mongocrypt_pkg_config_path:$libbson_pkg_config_path" echo "Validating pkg-config scripts ..." pkg-config --debug --print-errors --exists libmongocrypt-static pkg-config --debug --print-errors --exists libmongocrypt echo "Validating pkg-config scripts ... done" echo "Build example-state-machine, static linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt-static bson2-static) \ -o "$pkgconfig_tests_root/example-state-machine" \ "$LIBMONGOCRYPT_DIR/test/example-state-machine.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt-static) run_chdir "$LIBMONGOCRYPT_DIR" "$pkgconfig_tests_root/example-state-machine" echo "Build example-state-machine, static linking against libmongocrypt ... done" echo "Build example-no-bson, static linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt-static) \ -o "$pkgconfig_tests_root/example-no-bson" \ "$LIBMONGOCRYPT_DIR/test/example-no-bson.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt-static) command "$pkgconfig_tests_root/example-no-bson" echo "Build example-no-bson, static linking against libmongocrypt ... done" echo "Build example-state-machine, dynamic linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt bson2-static) \ -o "$pkgconfig_tests_root/example-state-machine" \ "$LIBMONGOCRYPT_DIR/test/example-state-machine.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt) run_chdir "$LIBMONGOCRYPT_DIR" \ env LD_LIBRARY_PATH="$mongocrypt_install_dir/lib:$mongocrypt_install_dir/lib64" \ "$pkgconfig_tests_root/example-state-machine" echo "Build example-state-machine, dynamic linking against libmongocrypt ... done" echo "Build example-no-bson, dynamic linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt) \ -o "$pkgconfig_tests_root/example-no-bson" \ "$LIBMONGOCRYPT_DIR/test/example-no-bson.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt) env LD_LIBRARY_PATH="$mongocrypt_install_dir/lib:$mongocrypt_install_dir/lib64" \ "$pkgconfig_tests_root/example-no-bson" echo "Build example-no-bson, dynamic linking against libmongocrypt ... done" # Clean up prior to next execution rm -r "$mongocrypt_install_dir" echo "Build libmongocrypt, dynamic linking against libbson ..." cmake -DUSE_SHARED_LIBBSON=ON \ -DENABLE_BUILD_FOR_PPA=OFF \ "${common_cmake_args[@]}" \ -DCMAKE_INSTALL_PREFIX="$mongocrypt_install_dir" \ -H"$LIBMONGOCRYPT_DIR" \ -B"$build_dir" cmake --build "$build_dir" --target install --config RelWithDebInfo echo "Build libmongocrypt, dynamic linking against libbson ... done" echo "Build example-state-machine, static linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt-static bson2-static) \ -o "$pkgconfig_tests_root/example-state-machine" \ "$LIBMONGOCRYPT_DIR/test/example-state-machine.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt-static) run_chdir "$LIBMONGOCRYPT_DIR" \ env LD_LIBRARY_PATH="$libbson_install_dir/lib:/$libbson_install_dir/lib64" \ "$pkgconfig_tests_root/example-state-machine" echo "Build example-state-machine, static linking against libmongocrypt ..." echo "Build example-no-bson, static linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt-static) \ -o "$pkgconfig_tests_root/example-no-bson" \ "$LIBMONGOCRYPT_DIR/test/example-no-bson.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt-static) env LD_LIBRARY_PATH="$libbson_install_dir/lib:/$libbson_install_dir/lib64" \ "$pkgconfig_tests_root/example-no-bson" echo "Build example-no-bson, static linking against libmongocrypt ... done" echo "Build example-state-machine, dynamic linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt bson2-static) \ -o "$pkgconfig_tests_root/example-state-machine" \ "$LIBMONGOCRYPT_DIR/test/example-state-machine.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt) run_chdir "$LIBMONGOCRYPT_DIR" \ env LD_LIBRARY_PATH="$mongocrypt_install_dir/lib:$mongocrypt_install_dir/lib64:$libbson_install_dir/lib:$libbson_install_dir/lib64" \ "$pkgconfig_tests_root/example-state-machine" echo "Build example-state-machine, dynamic linking against libmongocrypt ... done" echo "Build example-no-bson, dynamic linking against libmongocrypt ..." gcc $(pkg-config --cflags libmongocrypt) \ -o "$pkgconfig_tests_root/example-no-bson" \ "$LIBMONGOCRYPT_DIR/test/example-no-bson.c" \ $CFLAGS \ $(pkg-config --libs libmongocrypt) env LD_LIBRARY_PATH="$mongocrypt_install_dir/lib:$mongocrypt_install_dir/lib64:$libbson_install_dir/lib:$libbson_install_dir/lib64" \ "$pkgconfig_tests_root/example-no-bson" echo "Build example-no-bson, dynamic linking against libmongocrypt ... done" echo "pkg-config tests PASS" libmongocrypt-1.19.0/.evergreen/prep_c_driver_source.sh000077500000000000000000000005331521103432300232670ustar00rootroot00000000000000#!/bin/bash set -euxo pipefail # Clone mongo-c-driver and check out to a tagged version. MONGO_C_DRIVER_VERSION=2.3.0 # Force checkout with lf endings since .sh must have lf, not crlf on Windows git clone https://github.com/mongodb/mongo-c-driver.git --config core.eol=lf --config core.autocrlf=false --depth=1 --branch $MONGO_C_DRIVER_VERSION libmongocrypt-1.19.0/.evergreen/print-env-info.sh000077500000000000000000000003441521103432300217370ustar00rootroot00000000000000#!/bin/bash # Print information about the environment. # set -o xtrace evergreen_root=$(pwd) git --version openssl version python --version if which gcc; then gcc --version fi if which clang; then clang --version filibmongocrypt-1.19.0/.evergreen/requirements.txt000077500000000000000000000000121521103432300220040ustar00rootroot00000000000000virtualenvlibmongocrypt-1.19.0/.evergreen/sbom.sh000077500000000000000000000026371521103432300200330ustar00rootroot00000000000000#!/usr/bin/env bash set -o errexit set -o pipefail : "${artifactory_username:?}" : "${artifactory_password:?}" : "${branch_name:?}" : "${KONDUKTO_TOKEN:?}" command -v podman >/dev/null || { echo "missing required program podman" 1>&2 exit 1 } podman login --password-stdin --username "${artifactory_username:?}" artifactory.corp.mongodb.com <<<"${artifactory_password:?}" silkbomb="artifactory.corp.mongodb.com/release-tools-container-registry-public-local/silkbomb:2.0" # Ensure latest version of SilkBomb is being used. podman pull "${silkbomb:?}" silkbomb_augment_flags=( --repo mongodb/libmongocrypt --branch "${branch_name:?}" --sbom-in /pwd/etc/cyclonedx.sbom.json --sbom-out /pwd/cyclonedx.augmented.sbom.json # Any notable updates to the Augmented SBOM version should be done manually after careful inspection. # Otherwise, it should be equal to the SBOM Lite version, which should normally be `1`. --no-update-sbom-version ) # First validate the SBOM Lite. podman run -it --rm -v "$(pwd):/pwd" "${silkbomb:?}" \ validate --purls /pwd/etc/purls.txt --sbom-in /pwd/etc/cyclonedx.sbom.json --exclude jira # Then download the Augmented SBOM. Allow the timestamp to be updated. podman run -it --rm -v "$(pwd):/pwd" --env 'KONDUKTO_TOKEN' "${silkbomb:?}" \ augment "${silkbomb_augment_flags[@]:?}" [[ -f ./cyclonedx.augmented.sbom.json ]] || { echo "failed to download Augmented SBOM" 1>&2 exit 1 } libmongocrypt-1.19.0/.evergreen/setup-env.sh000066400000000000000000000014651521103432300210140ustar00rootroot00000000000000#!/bin/bash . "$(dirname "${BASH_SOURCE[0]}")/init.sh" : "${PPA_BUILD_ONLY:=}" : "${WINDOWS_32BIT:=}" : "${OS:=unspecified}" evergreen_root="$(dirname "$LIBMONGOCRYPT_DIR")" : "${MONGOCRYPT_INSTALL_PREFIX:="$evergreen_root/install/libmongocrypt"}" MONGOCRYPT_INSTALL_PREFIX="$(native_path "$MONGOCRYPT_INSTALL_PREFIX")" mkdir -p "$MONGOCRYPT_INSTALL_PREFIX" if test -f /proc/cpuinfo; then # Count the number of lines beginning with "processor" in the cpuinfo jobs="$(grep -c '^processor' /proc/cpuinfo)" if have_command bc; then # Add two (hueristic to compensate for I/O latency) jobs="$(echo "$jobs+2" | bc)" fi export CMAKE_BUILD_PARALLEL_LEVEL="$jobs" else # Cannot tell the best number of jobs. Provide a reasonable default. export CMAKE_BUILD_PARALLEL_LEVEL="8" fi libmongocrypt-1.19.0/.evergreen/uv-installer.sh000077500000000000000000002130711521103432300215140ustar00rootroot00000000000000#!/bin/sh # shellcheck shell=dash # shellcheck disable=SC2039 # local is non-POSIX # shellcheck disable=SC2268 # no harm in supporting older shells # # Licensed under the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. # This runs on Unix shells like bash/dash/ksh/zsh. It uses the common `local` # extension. Note: Most shells limit `local` to 1 var per line, contra bash. # Some versions of ksh have no `local` keyword. Alias it to `typeset`, but # beware this makes variables global with f()-style function syntax in ksh93. # mksh has this alias by default. has_local() { # shellcheck disable=SC2034 # deliberately unused local _has_local } has_local 2>/dev/null || alias local=typeset set -u APP_NAME="uv" APP_VERSION="0.11.8" if [ -n "${UV_DOWNLOAD_URL:-}" ]; then ARTIFACT_DOWNLOAD_URLS="$UV_DOWNLOAD_URL" elif [ -n "${INSTALLER_DOWNLOAD_URL:-}" ]; then ARTIFACT_DOWNLOAD_URLS="$INSTALLER_DOWNLOAD_URL" elif [ -n "${UV_INSTALLER_GHE_BASE_URL:-}" ]; then INSTALLER_BASE_URL="$UV_INSTALLER_GHE_BASE_URL" ARTIFACT_DOWNLOAD_URLS="${INSTALLER_BASE_URL}/astral-sh/uv/releases/download/0.11.8" elif [ -n "${UV_INSTALLER_GITHUB_BASE_URL:-}" ]; then INSTALLER_BASE_URL="$UV_INSTALLER_GITHUB_BASE_URL" ARTIFACT_DOWNLOAD_URLS="${INSTALLER_BASE_URL}/astral-sh/uv/releases/download/0.11.8" else ARTIFACT_DOWNLOAD_URLS="https://releases.astral.sh/github/uv/releases/download/0.11.8 https://github.com/astral-sh/uv/releases/download/0.11.8" fi if [ -n "${UV_PRINT_VERBOSE:-}" ]; then PRINT_VERBOSE="$UV_PRINT_VERBOSE" else PRINT_VERBOSE=${INSTALLER_PRINT_VERBOSE:-0} fi if [ -n "${UV_PRINT_QUIET:-}" ]; then PRINT_QUIET="$UV_PRINT_QUIET" else PRINT_QUIET=${INSTALLER_PRINT_QUIET:-0} fi if [ -n "${UV_NO_MODIFY_PATH:-}" ]; then NO_MODIFY_PATH="$UV_NO_MODIFY_PATH" else NO_MODIFY_PATH=${INSTALLER_NO_MODIFY_PATH:-0} fi if [ "${UV_DISABLE_UPDATE:-0}" = "1" ]; then INSTALL_UPDATER=0 else INSTALL_UPDATER=1 fi UNMANAGED_INSTALL="${UV_UNMANAGED_INSTALL:-}" if [ -n "${UNMANAGED_INSTALL}" ]; then NO_MODIFY_PATH=1 INSTALL_UPDATER=0 fi AUTH_TOKEN="${UV_GITHUB_TOKEN:-}" read -r RECEIPT <&2 for _base_url in $ARTIFACT_DOWNLOAD_URLS; do if [ "$_is_first_url" = "0" ]; then say "trying alternative download URL" 1>&2 fi _is_first_url=0 # download the archive local _url="$_base_url/$_artifact_name" _dir="$(ensure mktemp -d)" || return 1 local _file="$_dir/input$_zip_ext" say_verbose " from $_url" 1>&2 say_verbose " to $_file" 1>&2 ensure mkdir -p "$_dir" if ! downloader "$_url" "$_file"; then say "failed to download $_url" 1>&2 continue fi if [ -n "${_checksum_style:-}" ]; then verify_checksum "$_file" "$_checksum_style" "$_checksum_value" else say "no checksums to verify" 1>&2 fi # ...and then the updater, if it exists if [ -n "$_updater_name" ] && [ "$INSTALL_UPDATER" = "1" ]; then local _updater_url="$_base_url/$_updater_name" # This renames the artifact while doing the download, removing the # target triple and leaving just the appname-update format local _updater_file="$_dir/$APP_NAME-update" if ! downloader "$_updater_url" "$_updater_file"; then say "failed to download $_updater_url" continue fi # Add the updater to the list of binaries to install _bins="$_bins $APP_NAME-update" fi _download_result=1 break done if [ "$_download_result" = "0" ]; then say "this may be a standard network error, but it may also indicate" 1>&2 say "that $APP_NAME's release process is not working. When in doubt" 1>&2 say "please feel free to open an issue!" 1>&2 exit 1 fi # unpack the archive case "$_zip_ext" in ".zip") ensure unzip -q "$_file" -d "$_dir" ;; ".tar."*) ensure tar xf "$_file" --no-same-owner --strip-components 1 -C "$_dir" ;; *) err "unknown archive format: $_zip_ext" ;; esac install "$_dir" "$_bins" "$_libs" "$_staticlibs" "$_arch" "$@" local _retval=$? if [ "$_retval" != 0 ]; then return "$_retval" fi ignore rm -rf "$_dir" # Install the install receipt if [ "$INSTALL_UPDATER" = "1" ]; then if ! mkdir -p "$RECEIPT_HOME"; then err "unable to create receipt directory at $RECEIPT_HOME" else echo "$RECEIPT" > "$RECEIPT_HOME/$APP_NAME-receipt.json" # shellcheck disable=SC2320 local _retval=$? fi else local _retval=0 fi return "$_retval" } # Replaces $HOME with the variable name for display to the user, # only if $HOME is defined. replace_home() { local _str="$1" if [ -n "${HOME:-}" ]; then echo "$_str" | sed "s,$HOME,\$HOME," else echo "$_str" fi } json_binary_aliases() { local _arch="$1" case "$_arch" in "aarch64-apple-darwin") echo '{}' ;; "aarch64-pc-windows-gnu") echo '{}' ;; "aarch64-unknown-linux-gnu") echo '{}' ;; "aarch64-unknown-linux-musl-dynamic") echo '{}' ;; "aarch64-unknown-linux-musl-static") echo '{}' ;; "arm-unknown-linux-gnueabihf") echo '{}' ;; "arm-unknown-linux-musl-dynamiceabihf") echo '{}' ;; "arm-unknown-linux-musl-staticeabihf") echo '{}' ;; "armv7-unknown-linux-gnueabihf") echo '{}' ;; "armv7-unknown-linux-musl-dynamiceabihf") echo '{}' ;; "armv7-unknown-linux-musl-staticeabihf") echo '{}' ;; "i686-pc-windows-gnu") echo '{}' ;; "i686-unknown-linux-gnu") echo '{}' ;; "i686-unknown-linux-musl-dynamic") echo '{}' ;; "i686-unknown-linux-musl-static") echo '{}' ;; "powerpc64le-unknown-linux-gnu") echo '{}' ;; "riscv64gc-unknown-linux-gnu") echo '{}' ;; "riscv64gc-unknown-linux-musl-dynamic") echo '{}' ;; "riscv64gc-unknown-linux-musl-static") echo '{}' ;; "s390x-unknown-linux-gnu") echo '{}' ;; "x86_64-apple-darwin") echo '{}' ;; "x86_64-pc-windows-gnu") echo '{}' ;; "x86_64-unknown-linux-gnu") echo '{}' ;; "x86_64-unknown-linux-musl-dynamic") echo '{}' ;; "x86_64-unknown-linux-musl-static") echo '{}' ;; *) echo '{}' ;; esac } aliases_for_binary() { local _bin="$1" local _arch="$2" case "$_arch" in "aarch64-apple-darwin") case "$_bin" in *) echo "" ;; esac ;; "aarch64-pc-windows-gnu") case "$_bin" in *) echo "" ;; esac ;; "aarch64-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "aarch64-unknown-linux-musl-dynamic") case "$_bin" in *) echo "" ;; esac ;; "aarch64-unknown-linux-musl-static") case "$_bin" in *) echo "" ;; esac ;; "arm-unknown-linux-gnueabihf") case "$_bin" in *) echo "" ;; esac ;; "arm-unknown-linux-musl-dynamiceabihf") case "$_bin" in *) echo "" ;; esac ;; "arm-unknown-linux-musl-staticeabihf") case "$_bin" in *) echo "" ;; esac ;; "armv7-unknown-linux-gnueabihf") case "$_bin" in *) echo "" ;; esac ;; "armv7-unknown-linux-musl-dynamiceabihf") case "$_bin" in *) echo "" ;; esac ;; "armv7-unknown-linux-musl-staticeabihf") case "$_bin" in *) echo "" ;; esac ;; "i686-pc-windows-gnu") case "$_bin" in *) echo "" ;; esac ;; "i686-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "i686-unknown-linux-musl-dynamic") case "$_bin" in *) echo "" ;; esac ;; "i686-unknown-linux-musl-static") case "$_bin" in *) echo "" ;; esac ;; "powerpc64le-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "riscv64gc-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "riscv64gc-unknown-linux-musl-dynamic") case "$_bin" in *) echo "" ;; esac ;; "riscv64gc-unknown-linux-musl-static") case "$_bin" in *) echo "" ;; esac ;; "s390x-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "x86_64-apple-darwin") case "$_bin" in *) echo "" ;; esac ;; "x86_64-pc-windows-gnu") case "$_bin" in *) echo "" ;; esac ;; "x86_64-unknown-linux-gnu") case "$_bin" in *) echo "" ;; esac ;; "x86_64-unknown-linux-musl-dynamic") case "$_bin" in *) echo "" ;; esac ;; "x86_64-unknown-linux-musl-static") case "$_bin" in *) echo "" ;; esac ;; *) echo "" ;; esac } select_archive_for_arch() { local _true_arch="$1" local _archive # try each archive, checking runtime conditions like libc versions # accepting the first one that matches, as it's the best match case "$_true_arch" in "aarch64-apple-darwin") _archive="uv-aarch64-apple-darwin.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-x86_64-apple-darwin.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "aarch64-pc-windows-gnu") _archive="uv-aarch64-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "aarch64-pc-windows-msvc") _archive="uv-aarch64-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-x86_64-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-i686-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "aarch64-unknown-linux-gnu") _archive="uv-aarch64-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "28"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-aarch64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "aarch64-unknown-linux-musl-dynamic") _archive="uv-aarch64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "aarch64-unknown-linux-musl-static") _archive="uv-aarch64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "arm-unknown-linux-gnueabihf") _archive="uv-arm-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "arm-unknown-linux-musl-dynamiceabihf") _archive="uv-arm-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "arm-unknown-linux-musl-staticeabihf") _archive="uv-arm-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "armv7-unknown-linux-gnueabihf") _archive="uv-armv7-unknown-linux-gnueabihf.tar.gz" if ! check_glibc "2" "17"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-armv7-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "armv7-unknown-linux-musl-dynamiceabihf") _archive="uv-armv7-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "armv7-unknown-linux-musl-staticeabihf") _archive="uv-armv7-unknown-linux-musleabihf.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "i686-pc-windows-gnu") _archive="uv-i686-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "i686-pc-windows-msvc") _archive="uv-i686-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "i686-unknown-linux-gnu") _archive="uv-i686-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "17"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-i686-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "i686-unknown-linux-musl-dynamic") _archive="uv-i686-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "i686-unknown-linux-musl-static") _archive="uv-i686-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "powerpc64le-unknown-linux-gnu") _archive="uv-powerpc64le-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "17"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "riscv64gc-unknown-linux-gnu") _archive="uv-riscv64gc-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "31"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-riscv64gc-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "riscv64gc-unknown-linux-musl-dynamic") _archive="uv-riscv64gc-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "riscv64gc-unknown-linux-musl-static") _archive="uv-riscv64gc-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "s390x-unknown-linux-gnu") _archive="uv-s390x-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "17"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-apple-darwin") _archive="uv-x86_64-apple-darwin.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-pc-windows-gnu") _archive="uv-x86_64-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-pc-windows-msvc") _archive="uv-x86_64-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-i686-pc-windows-msvc.zip" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-unknown-linux-gnu") _archive="uv-x86_64-unknown-linux-gnu.tar.gz" if ! check_glibc "2" "17"; then _archive="" fi if [ -n "$_archive" ]; then echo "$_archive" return 0 fi _archive="uv-x86_64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-unknown-linux-musl-dynamic") _archive="uv-x86_64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; "x86_64-unknown-linux-musl-static") _archive="uv-x86_64-unknown-linux-musl.tar.gz" if [ -n "$_archive" ]; then echo "$_archive" return 0 fi ;; *) err "there isn't a download for your platform $_true_arch" ;; esac err "no compatible downloads were found for your platform $_true_arch" } check_glibc() { local _min_glibc_major="$1" local _min_glibc_series="$2" # Parsing version out from line 1 like: # ldd (Ubuntu GLIBC 2.35-0ubuntu3.1) 2.35 _local_glibc="$(ldd --version | awk -F' ' '{ if (FNR<=1) print $NF }')" if [ "$(echo "${_local_glibc}" | awk -F. '{ print $1 }')" = "$_min_glibc_major" ] && [ "$(echo "${_local_glibc}" | awk -F. '{ print $2 }')" -ge "$_min_glibc_series" ]; then return 0 else say "System glibc version (\`${_local_glibc}') is too old; checking alternatives" >&2 return 1 fi } # See discussion of late-bound vs early-bound for why we use single-quotes with env vars # shellcheck disable=SC2016 install() { # This code needs to both compute certain paths for itself to write to, and # also write them to shell/rc files so that they can look them up to e.g. # add them to PATH. This requires an active distinction between paths # and expressions that can compute them. # # The distinction lies in when we want env-vars to be evaluated. For instance # if we determine that we want to install to $HOME/.myapp, which do we add # to e.g. $HOME/.profile: # # * early-bound: export PATH="/home/myuser/.myapp:$PATH" # * late-bound: export PATH="$HOME/.myapp:$PATH" # # In this case most people would prefer the late-bound version, but in other # cases the early-bound version might be a better idea. In particular when using # other env-vars than $HOME, they are more likely to be only set temporarily # for the duration of this install script, so it's more advisable to erase their # existence with early-bounding. # # This distinction is handled by "double-quotes" (early) vs 'single-quotes' (late). # # However if we detect that "$SOME_VAR/..." is a subdir of $HOME, we try to rewrite # it to be '$HOME/...' to get the best of both worlds. # # This script has a few different variants, the most complex one being the # CARGO_HOME version which attempts to install things to Cargo's bin dir, # potentially setting up a minimal version if the user hasn't ever installed Cargo. # # In this case we need to: # # * Install to $HOME/.cargo/bin/ # * Create a shell script at $HOME/.cargo/env that: # * Checks if $HOME/.cargo/bin/ is on PATH # * and if not prepends it to PATH # * Edits $INFERRED_HOME/.profile to run $HOME/.cargo/env (if the line doesn't exist) # # To do this we need these 4 values: # The actual path we're going to install to local _install_dir # The directory C dynamic/static libraries install to local _lib_install_dir # The install prefix we write to the receipt. # For organized install methods like CargoHome, which have # subdirectories, this is the root without `/bin`. For other # methods, this is the same as `_install_dir`. local _receipt_install_dir # Path to the an shell script that adds install_dir to PATH local _env_script_path # Potentially-late-bound version of install_dir to write env_script local _install_dir_expr # Potentially-late-bound version of env_script_path to write to rcfiles like $HOME/.profile local _env_script_path_expr # Forces the install to occur at this path, not the default local _force_install_dir # Which install layout to use - "flat" or "hierarchical" local _install_layout="unspecified" # A list of binaries which are shadowed in the PATH local _shadowed_bins="" # Check the newer app-specific variable before falling back # to the older generic one if [ -n "${UV_INSTALL_DIR:-}" ]; then _force_install_dir="$UV_INSTALL_DIR" _install_layout="flat" elif [ -n "${CARGO_DIST_FORCE_INSTALL_DIR:-}" ]; then _force_install_dir="$CARGO_DIST_FORCE_INSTALL_DIR" _install_layout="flat" elif [ -n "$UNMANAGED_INSTALL" ]; then _force_install_dir="$UNMANAGED_INSTALL" _install_layout="flat" fi # Check if the install layout should be changed from `flat` to `cargo-home` # for backwards compatible updates of applications that switched layouts. if [ -n "${_force_install_dir:-}" ]; then if [ "$_install_layout" = "flat" ]; then # If the install directory is targeting the Cargo home directory, then # we assume this application was previously installed that layout if [ "$_force_install_dir" = "${CARGO_HOME:-${INFERRED_HOME:-}/.cargo}" ]; then _install_layout="cargo-home" fi fi fi # Before actually consulting the configured install strategy, see # if we're overriding it. if [ -n "${_force_install_dir:-}" ]; then case "$_install_layout" in "hierarchical") _install_dir="$_force_install_dir/bin" _lib_install_dir="$_force_install_dir/lib" _receipt_install_dir="$_force_install_dir" _env_script_path="$_force_install_dir/env" _install_dir_expr="$(replace_home "$_force_install_dir/bin")" _env_script_path_expr="$(replace_home "$_force_install_dir/env")" ;; "cargo-home") _install_dir="$_force_install_dir/bin" _lib_install_dir="$_force_install_dir/bin" _receipt_install_dir="$_force_install_dir" _env_script_path="$_force_install_dir/env" _install_dir_expr="$(replace_home "$_force_install_dir/bin")" _env_script_path_expr="$(replace_home "$_force_install_dir/env")" ;; "flat") _install_dir="$_force_install_dir" _lib_install_dir="$_force_install_dir" _receipt_install_dir="$_install_dir" _env_script_path="$_force_install_dir/env" _install_dir_expr="$(replace_home "$_force_install_dir")" _env_script_path_expr="$(replace_home "$_force_install_dir/env")" ;; *) err "Unrecognized install layout: $_install_layout" ;; esac fi if [ -z "${_install_dir:-}" ]; then _install_layout="flat" # Install to $XDG_BIN_HOME if [ -n "${XDG_BIN_HOME:-}" ]; then _install_dir="$XDG_BIN_HOME" _lib_install_dir="$_install_dir" _receipt_install_dir="$_install_dir" _env_script_path="$XDG_BIN_HOME/env" _install_dir_expr="$(replace_home "$_install_dir")" _env_script_path_expr="$(replace_home "$_env_script_path")" fi fi if [ -z "${_install_dir:-}" ]; then _install_layout="flat" # Install to $XDG_DATA_HOME/../bin if [ -n "${XDG_DATA_HOME:-}" ]; then _install_dir="$XDG_DATA_HOME/../bin" _lib_install_dir="$_install_dir" _receipt_install_dir="$_install_dir" _env_script_path="$XDG_DATA_HOME/../bin/env" _install_dir_expr="$(replace_home "$_install_dir")" _env_script_path_expr="$(replace_home "$_env_script_path")" fi fi if [ -z "${_install_dir:-}" ]; then _install_layout="flat" # Install to $HOME/.local/bin if [ -n "${INFERRED_HOME:-}" ]; then _install_dir="$INFERRED_HOME/.local/bin" _lib_install_dir="$INFERRED_HOME/.local/bin" _receipt_install_dir="$_install_dir" _env_script_path="$INFERRED_HOME/.local/bin/env" _install_dir_expr="$INFERRED_HOME_EXPRESSION/.local/bin" _env_script_path_expr="$INFERRED_HOME_EXPRESSION/.local/bin/env" fi fi if [ -z "$_install_dir_expr" ]; then err "could not find a valid path to install to!" fi # Identical to the sh version, just with a .fish file extension # We place it down here to wait until it's been assigned in every # path. _fish_env_script_path="${_env_script_path}.fish" _fish_env_script_path_expr="${_env_script_path_expr}.fish" # Replace the temporary cargo home with the calculated one RECEIPT=$(echo "$RECEIPT" | sed "s,AXO_INSTALL_PREFIX,$(convert_path_for_receipt "$_receipt_install_dir"),") # Also replace the aliases with the arch-specific one RECEIPT=$(echo "$RECEIPT" | sed "s'\"binary_aliases\":{}'\"binary_aliases\":$(json_binary_aliases "$_arch")'") # And replace the install layout RECEIPT=$(echo "$RECEIPT" | sed "s'\"install_layout\":\"unspecified\"'\"install_layout\":\"$_install_layout\"'") if [ "$NO_MODIFY_PATH" = "1" ]; then RECEIPT=$(echo "$RECEIPT" | sed "s'\"modify_path\":true'\"modify_path\":false'") fi say "installing to $_install_dir" ensure mkdir -p "$_install_dir" ensure mkdir -p "$_lib_install_dir" _install_temp=$(mktemp -d "$_install_dir/tmp.XXXXXXXXXX") _lib_install_temp=$(mktemp -d "$_lib_install_dir/tmp.XXXXXXXXXX") # First move all the binaries and libraries to temporary directories within # the target installation directories. This is done because those # directories may be on a different filesystem to the temporary directory # and as such this process might take time. This in turn increases the # chance of an interruption leading to a broken installation. local _src_dir="$1" local _bins="$2" local _libs="$3" local _staticlibs="$4" local _arch="$5" for _bin_name in $_bins; do ensure mv "$_src_dir/$_bin_name" "$_install_temp" # unzip seems to need this chmod ensure chmod +x "$_install_temp/$_bin_name" say " $_bin_name" done # Like the above, but no aliases for _lib_name in $_libs $_staticlibs; do ensure mv "$_src_dir/$_lib_name" "$_lib_install_temp" # unzip seems to need this chmod ensure chmod +x "$_lib_install_dir/$_lib_name" say " $_lib_name" done # Now move all the binaries and libraries into their final locations with # plain mv. There's still a possibility of interruption here, but we've # already written everything to the target filesystem (if it was ever # different from the source) which means that this operation should be very # fast, and we've already created directories within the target # directories, so it's unlikely for anything here to fail due to missing # permissions. for _bin_name in $_bins; do ensure mv "$_install_temp/$_bin_name" "$_install_dir" for _dest in $(aliases_for_binary "$_bin_name" "$_arch"); do ln -sf "$_install_dir/$_bin_name" "$_install_dir/$_dest" done done for _lib_name in $_libs $_staticlibs; do ensure mv "$_lib_install_temp/$_lib_name" "$_lib_install_dir" done ignore rm -rf "$_install_temp" "$_lib_install_temp" say "everything's installed!" # Avoid modifying the users PATH if they are managing their PATH manually case :$PATH: in *:$_install_dir:*) NO_MODIFY_PATH=1 ;; *) ;; esac if [ "0" = "$NO_MODIFY_PATH" ]; then add_install_dir_to_ci_path "$_install_dir" add_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr" ".profile" "sh" exit1=$? shotgun_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr" ".profile .bashrc .bash_profile .bash_login" "sh" exit2=$? add_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr" ".zshrc .zshenv" "sh" exit3=$? # This path may not exist by default ensure mkdir -p "$INFERRED_HOME/.config/fish/conf.d" exit4=$? add_install_dir_to_path "$_install_dir_expr" "$_fish_env_script_path" "$_fish_env_script_path_expr" ".config/fish/conf.d/$APP_NAME.env.fish" "fish" exit5=$? if [ "${exit1:-0}" = 1 ] || [ "${exit2:-0}" = 1 ] || [ "${exit3:-0}" = 1 ] || [ "${exit4:-0}" = 1 ] || [ "${exit5:-0}" = 1 ]; then say "" say "To add $_install_dir_expr to your PATH, either restart your shell or run:" say "" say " source $_env_script_path_expr (sh, bash, zsh)" say " source $_fish_env_script_path_expr (fish)" fi fi _shadowed_bins="$(check_for_shadowed_bins "$_install_dir" "$_bins")" if [ -n "$_shadowed_bins" ]; then warn "The following commands are shadowed by other commands in your PATH:$_shadowed_bins" fi } check_for_shadowed_bins() { local _install_dir="$1" local _bins="$2" local _shadow for _bin_name in $_bins; do _shadow="$(command -v "$_bin_name")" if [ -n "$_shadow" ] && [ "$_shadow" != "$_install_dir/$_bin_name" ]; then _shadowed_bins="$_shadowed_bins $_bin_name" fi done echo "$_shadowed_bins" } print_home_for_script() { local script="$1" local _home case "$script" in # zsh has a special ZDOTDIR directory, which if set # should be considered instead of $HOME .zsh*) if [ -n "${ZDOTDIR:-}" ]; then _home="$ZDOTDIR" else _home="$INFERRED_HOME" fi ;; *) _home="$INFERRED_HOME" ;; esac echo "$_home" } add_install_dir_to_ci_path() { # Attempt to do CI-specific rituals to get the install-dir on PATH faster local _install_dir="$1" # If GITHUB_PATH is present, then write install_dir to the file it refs. # After each GitHub Action, the contents will be added to PATH. # So if you put a curl | sh for this script in its own "run" step, # the next step will have this dir on PATH. # # Note that GITHUB_PATH will not resolve any variables, so we in fact # want to write install_dir and not install_dir_expr if [ -n "${GITHUB_PATH:-}" ]; then ensure echo "$_install_dir" >> "$GITHUB_PATH" fi } add_install_dir_to_path() { # Edit rcfiles ($HOME/.profile) to add install_dir to $PATH # # We do this slightly indirectly by creating an "env" shell script which checks if install_dir # is on $PATH already, and prepends it if not. The actual line we then add to rcfiles # is to just source that script. This allows us to blast it into lots of different rcfiles and # have it run multiple times without causing problems. It's also specifically compatible # with the system rustup uses, so that we don't conflict with it. local _install_dir_expr="$1" local _env_script_path="$2" local _env_script_path_expr="$3" local _rcfiles="$4" local _shell="$5" if [ -n "${INFERRED_HOME:-}" ]; then local _target local _home # Find the first file in the array that exists and choose # that as our target to write to for _rcfile_relative in $_rcfiles; do _home="$(print_home_for_script "$_rcfile_relative")" local _rcfile="$_home/$_rcfile_relative" if [ -f "$_rcfile" ]; then _target="$_rcfile" break fi done # If we didn't find anything, pick the first entry in the # list as the default to create and write to if [ -z "${_target:-}" ]; then local _rcfile_relative _rcfile_relative="$(echo "$_rcfiles" | awk '{ print $1 }')" _home="$(print_home_for_script "$_rcfile_relative")" _target="$_home/$_rcfile_relative" fi # `source x` is an alias for `. x`, and the latter is more portable/actually-posix. # This apparently comes up a lot on freebsd. It's easy enough to always add # the more robust line to rcfiles, but when telling the user to apply the change # to their current shell ". x" is pretty easy to misread/miscopy, so we use the # prettier "source x" line there. Hopefully people with Weird Shells are aware # this is a thing and know to tweak it (or just restart their shell). local _robust_line=". \"$_env_script_path_expr\"" local _pretty_line="source \"$_env_script_path_expr\"" # Add the env script if it doesn't already exist if [ ! -f "$_env_script_path" ]; then say_verbose "creating $_env_script_path" if [ "$_shell" = "sh" ]; then write_env_script_sh "$_install_dir_expr" "$_env_script_path" else write_env_script_fish "$_install_dir_expr" "$_env_script_path" fi else say_verbose "$_env_script_path already exists" fi # Check if the line is already in the rcfile # grep: 0 if matched, 1 if no match, and 2 if an error occurred # # Ideally we could use quiet grep (-q), but that makes "match" and "error" # have the same behaviour, when we want "no match" and "error" to be the same # (on error we want to create the file, which >> conveniently does) # # We search for both kinds of line here just to do the right thing in more cases. if ! grep -F "$_robust_line" "$_target" > /dev/null 2>/dev/null && \ ! grep -F "$_pretty_line" "$_target" > /dev/null 2>/dev/null then # If the script now exists, add the line to source it to the rcfile # (This will also create the rcfile if it doesn't exist) if [ -f "$_env_script_path" ]; then local _line # Fish has deprecated `.` as an alias for `source` and # it will be removed in a later version. # https://fishshell.com/docs/current/cmds/source.html # By contrast, `.` is the traditional syntax in sh and # `source` isn't always supported in all circumstances. if [ "$_shell" = "fish" ]; then _line="$_pretty_line" else _line="$_robust_line" fi say_verbose "adding $_line to $_target" # prepend an extra newline in case the user's file is missing a trailing one ensure echo "" >> "$_target" ensure echo "$_line" >> "$_target" return 1 fi else say_verbose "$_install_dir already on PATH" fi fi } shotgun_install_dir_to_path() { # Edit rcfiles ($HOME/.profile) to add install_dir to $PATH # (Shotgun edition - write to all provided files that exist rather than just the first) local _install_dir_expr="$1" local _env_script_path="$2" local _env_script_path_expr="$3" local _rcfiles="$4" local _shell="$5" if [ -n "${INFERRED_HOME:-}" ]; then local _found=false local _home for _rcfile_relative in $_rcfiles; do _home="$(print_home_for_script "$_rcfile_relative")" local _rcfile_abs="$_home/$_rcfile_relative" if [ -f "$_rcfile_abs" ]; then _found=true add_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr" "$_rcfile_relative" "$_shell" fi done # Fall through to previous "create + write to first file in list" behavior if [ "$_found" = false ]; then add_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr" "$_rcfiles" "$_shell" fi fi } write_env_script_sh() { # write this env script to the given path (this cat/EOF stuff is a "heredoc" string) local _install_dir_expr="$1" local _env_script_path="$2" ensure cat < "$_env_script_path" #!/bin/sh # add binaries to PATH if they aren't added yet # affix colons on either side of \$PATH to simplify matching case ":\${PATH}:" in *:"$_install_dir_expr":*) ;; *) # Prepending path in case a system-installed binary needs to be overridden export PATH="$_install_dir_expr:\$PATH" ;; esac EOF } write_env_script_fish() { # write this env script to the given path (this cat/EOF stuff is a "heredoc" string) local _install_dir_expr="$1" local _env_script_path="$2" ensure cat < "$_env_script_path" if not contains "$_install_dir_expr" \$PATH # Prepending path in case a system-installed binary needs to be overridden set -x PATH "$_install_dir_expr" \$PATH end EOF } get_current_exe() { # Returns the executable used for system architecture detection # This is only run on Linux local _current_exe if test -L /proc/self/exe ; then _current_exe=/proc/self/exe else warn "Unable to find /proc/self/exe. System architecture detection might be inaccurate." if test -n "$SHELL" ; then _current_exe=$SHELL else need_cmd /bin/sh _current_exe=/bin/sh fi warn "Falling back to $_current_exe." fi echo "$_current_exe" } get_bitness() { need_cmd head # Architecture detection without dependencies beyond coreutils. # ELF files start out "\x7fELF", and the following byte is # 0x01 for 32-bit and # 0x02 for 64-bit. # The printf builtin on some shells like dash only supports octal # escape sequences, so we use those. local _current_exe=$1 local _current_exe_head _current_exe_head=$(head -c 5 "$_current_exe") if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then echo 32 elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then echo 64 else err "unknown platform bitness" fi } is_host_amd64_elf() { local _current_exe=$1 need_cmd head need_cmd tail # ELF e_machine detection without dependencies beyond coreutils. # Two-byte field at offset 0x12 indicates the CPU, # but we're interested in it being 0x3E to indicate amd64, or not that. local _current_exe_machine _current_exe_machine=$(head -c 19 "$_current_exe" | tail -c 1) [ "$_current_exe_machine" = "$(printf '\076')" ] } get_endianness() { local _current_exe=$1 local cputype=$2 local suffix_eb=$3 local suffix_el=$4 # detect endianness without od/hexdump, like get_bitness() does. need_cmd head need_cmd tail local _current_exe_endianness _current_exe_endianness="$(head -c 6 "$_current_exe" | tail -c 1)" if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then echo "${cputype}${suffix_el}" elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then echo "${cputype}${suffix_eb}" else err "unknown platform endianness" fi } # Detect the Linux/LoongArch UAPI flavor, with all errors being non-fatal. # Returns 0 or 234 in case of successful detection, 1 otherwise (/tmp being # noexec, or other causes). check_loongarch_uapi() { need_cmd base64 local _tmp if ! _tmp="$(ensure mktemp)"; then return 1 fi # Minimal Linux/LoongArch UAPI detection, exiting with 0 in case of # upstream ("new world") UAPI, and 234 (-EINVAL truncated) in case of # old-world (as deployed on several early commercial Linux distributions # for LoongArch). # # See https://gist.github.com/xen0n/5ee04aaa6cecc5c7794b9a0c3b65fc7f for # source to this helper binary. ignore base64 -d > "$_tmp" <&1 | grep -q 'musl'; then _clibtype="musl-dynamic" else # Assume all other linuxes are glibc (even if wrong, static libc fallback will apply) _clibtype="gnu" fi fi if [ "$_ostype" = Darwin ]; then # Darwin `uname -m` can lie due to Rosetta shenanigans. If you manage to # invoke a native shell binary and then a native uname binary, you can # get the real answer, but that's hard to ensure, so instead we use # `sysctl` (which doesn't lie) to check for the actual architecture. if [ "$_cputype" = i386 ]; then # Handling i386 compatibility mode in older macOS versions (<10.15) # running on x86_64-based Macs. # Starting from 10.15, macOS explicitly bans all i386 binaries from running. # See: # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code. if sysctl hw.optional.x86_64 2> /dev/null || true | grep -q ': 1'; then _cputype=x86_64 fi elif [ "$_cputype" = x86_64 ]; then # Handling x86-64 compatibility mode (a.k.a. Rosetta 2) # in newer macOS versions (>=11) running on arm64-based Macs. # Rosetta 2 is built exclusively for x86-64 and cannot run i386 binaries. # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code. if sysctl hw.optional.arm64 2> /dev/null || true | grep -q ': 1'; then _cputype=arm64 fi fi fi if [ "$_ostype" = SunOS ]; then # Both Solaris and illumos presently announce as "SunOS" in "uname -s" # so use "uname -o" to disambiguate. We use the full path to the # system uname in case the user has coreutils uname first in PATH, # which has historically sometimes printed the wrong value here. if [ "$(/usr/bin/uname -o)" = illumos ]; then _ostype=illumos fi # illumos systems have multi-arch userlands, and "uname -m" reports the # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 # systems. Check for the native (widest) instruction set on the # running kernel: if [ "$_cputype" = i86pc ]; then _cputype="$(isainfo -n)" fi fi local _current_exe case "$_ostype" in Android) _ostype=linux-android ;; Linux) _current_exe=$(get_current_exe) _ostype=unknown-linux-$_clibtype _bitness=$(get_bitness "$_current_exe") ;; FreeBSD) _ostype=unknown-freebsd ;; NetBSD) _ostype=unknown-netbsd ;; DragonFly) _ostype=unknown-dragonfly ;; Darwin) _ostype=apple-darwin ;; illumos) _ostype=unknown-illumos ;; MINGW* | MSYS* | CYGWIN* | Windows_NT) _ostype=pc-windows-gnu ;; *) err "unrecognized OS type: $_ostype" ;; esac case "$_cputype" in i386 | i486 | i686 | i786 | x86) _cputype=i686 ;; xscale | arm) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi fi ;; armv6l) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; armv7l | armv8l) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; aarch64 | arm64) _cputype=aarch64 ;; x86_64 | x86-64 | x64 | amd64) _cputype=x86_64 ;; mips) _cputype=$(get_endianness "$_current_exe" mips '' el) ;; mips64) if [ "$_bitness" -eq 64 ]; then # only n64 ABI is supported for now _ostype="${_ostype}abi64" _cputype=$(get_endianness "$_current_exe" mips64 '' el) fi ;; ppc) _cputype=powerpc ;; ppc64) _cputype=powerpc64 ;; ppc64le) _cputype=powerpc64le ;; s390x) _cputype=s390x ;; riscv64) _cputype=riscv64gc ;; loongarch64) _cputype=loongarch64 ensure_loongarch_uapi ;; *) err "unknown CPU type: $_cputype" esac # Detect 64-bit linux with 32-bit userland if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then case $_cputype in x86_64) # 32-bit executable for amd64 = x32 if is_host_amd64_elf "$_current_exe"; then { err "x32 linux unsupported" }; else _cputype=i686 fi ;; mips64) _cputype=$(get_endianness "$_current_exe" mips '' el) ;; powerpc64) _cputype=powerpc ;; aarch64) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; riscv64gc) err "riscv64 with 32-bit userland unsupported" ;; esac fi # Detect armv7 but without the CPU features Rust needs in that build, # and fall back to arm. if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then if ! (ensure grep '^Features' /proc/cpuinfo | grep -E -q 'neon|simd') ; then # Either `/proc/cpuinfo` is malformed or unavailable, or # at least one processor does not have NEON (which is asimd on armv8+). _cputype=arm fi fi _arch="${_cputype}-${_ostype}" RETVAL="$_arch" } say() { if [ "0" = "$PRINT_QUIET" ]; then echo "$1" fi } say_verbose() { if [ "1" = "$PRINT_VERBOSE" ]; then echo "$1" fi } warn() { if [ "0" = "$PRINT_QUIET" ]; then local red local reset red=$(tput setaf 1 2>/dev/null || echo '') reset=$(tput sgr0 2>/dev/null || echo '') say "${red}WARN${reset}: $1" >&2 fi } err() { if [ "0" = "$PRINT_QUIET" ]; then local red local reset red=$(tput setaf 1 2>/dev/null || echo '') reset=$(tput sgr0 2>/dev/null || echo '') say "${red}ERROR${reset}: $1" >&2 fi exit 1 } need_cmd() { if ! check_cmd "$1" then err "need '$1' (command not found)" fi } check_cmd() { command -v "$1" > /dev/null 2>&1 return $? } assert_nz() { if [ -z "$1" ]; then err "assert_nz $2"; fi } # Run a command that should never fail. If the command fails execution # will immediately terminate with an error showing the failing # command. ensure() { if ! "$@"; then err "command failed: $*"; fi } # This is just for indicating that commands' results are being # intentionally ignored. Usually, because it's being executed # as part of error handling. ignore() { "$@" } # This wraps curl or wget. Try curl first, if not installed, # use wget instead. downloader() { # Check if we have a broken snap curl # https://github.com/boukendesho/curl-snap/issues/1 _snap_curl=0 if command -v curl > /dev/null 2>&1; then _curl_path=$(command -v curl) if echo "$_curl_path" | grep "/snap/" > /dev/null 2>&1; then _snap_curl=1 fi fi # Check if we have a working (non-snap) curl if check_cmd curl && [ "$_snap_curl" = "0" ] then _dld=curl # Try wget for both no curl and the broken snap curl elif check_cmd wget then _dld=wget # If we can't fall back from broken snap curl to wget, report the broken snap curl elif [ "$_snap_curl" = "1" ] then say "curl installed with snap cannot be used to install $APP_NAME" say "due to missing permissions. Please uninstall it and" say "reinstall curl with a different package manager (e.g., apt)." say "See https://github.com/boukendesho/curl-snap/issues/1" exit 1 else _dld='curl or wget' # to be used in error message of need_cmd fi if [ "$1" = --check ] then need_cmd "$_dld" elif [ "$_dld" = curl ]; then if [ -n "${AUTH_TOKEN:-}" ]; then curl -sSfL --header "Authorization: Bearer ${AUTH_TOKEN}" "$1" -o "$2" else curl -sSfL "$1" -o "$2" fi elif [ "$_dld" = wget ]; then if [ -n "${AUTH_TOKEN:-}" ]; then wget --header "Authorization: Bearer ${AUTH_TOKEN}" "$1" -O "$2" else wget "$1" -O "$2" fi else err "Unknown downloader" # should not reach here fi } verify_checksum() { local _file="$1" local _checksum_style="$2" local _checksum_value="$3" local _calculated_checksum if [ -z "$_checksum_value" ]; then return 0 fi case "$_checksum_style" in sha256) if ! check_cmd sha256sum; then say "skipping sha256 checksum verification (it requires the 'sha256sum' command)" return 0 fi _calculated_checksum="$(sha256sum -b "$_file" | awk '{printf $1}')" ;; sha512) if ! check_cmd sha512sum; then say "skipping sha512 checksum verification (it requires the 'sha512sum' command)" return 0 fi _calculated_checksum="$(sha512sum -b "$_file" | awk '{printf $1}')" ;; sha3-256) if ! check_cmd openssl; then say "skipping sha3-256 checksum verification (it requires the 'openssl' command)" return 0 fi _calculated_checksum="$(openssl dgst -sha3-256 "$_file" | awk '{printf $NF}')" ;; sha3-512) if ! check_cmd openssl; then say "skipping sha3-512 checksum verification (it requires the 'openssl' command)" return 0 fi _calculated_checksum="$(openssl dgst -sha3-512 "$_file" | awk '{printf $NF}')" ;; blake2s) if ! check_cmd b2sum; then say "skipping blake2s checksum verification (it requires the 'b2sum' command)" return 0 fi # Test if we have official b2sum with blake2s support local _well_known_blake2s_checksum="93314a61f470985a40f8da62df10ba0546dc5216e1d45847bf1dbaa42a0e97af" local _test_blake2s _test_blake2s="$(printf "can do blake2s" | b2sum -a blake2s | awk '{printf $1}')" || _test_blake2s="" if [ "X$_test_blake2s" = "X$_well_known_blake2s_checksum" ]; then _calculated_checksum="$(b2sum -a blake2s "$_file" | awk '{printf $1}')" || _calculated_checksum="" else say "skipping blake2s checksum verification (installed b2sum doesn't support blake2s)" return 0 fi ;; blake2b) if ! check_cmd b2sum; then say "skipping blake2b checksum verification (it requires the 'b2sum' command)" return 0 fi _calculated_checksum="$(b2sum "$_file" | awk '{printf $1}')" ;; false) ;; *) say "skipping unknown checksum style: $_checksum_style" return 0 ;; esac if [ "$_calculated_checksum" != "$_checksum_value" ]; then err "checksum mismatch want: $_checksum_value got: $_calculated_checksum" fi } download_binary_and_run_installer "$@" || exit 1 libmongocrypt-1.19.0/.evergreen/vs-env-run.ps1000066400000000000000000000221061521103432300211720ustar00rootroot00000000000000<# .SYNOPSIS Execute a command with a Visual Studio environment loaded .DESCRIPTION This script will load the specified Visual Studio environment with the specified options set, and then execute the given program This script makes use of vswhere.exe, which is installed with Visual Studio 2017 or later, but supports all Visual Studio versions. Only the -Version and -TargetArch parameters are required. The command should be given as a script block or executable string. .EXAMPLE PS C:\> vs-env-run.ps1 -Version 14.* -TargetArch amd64 { build_all.ps1 } This will load the Visual Studio 14 environment targetting amd64 processors and then run 'build_all.ps1' #> [CmdletBinding(PositionalBinding = $false)] param ( # Select a version of Visual Studio to activate. Accepts wildcards. # # Major versions by year release: # # - 14.* => VS 2015 # - 15.* => VS 2017 # - 16.* => VS 2019 # - 17.* => VS 2022 # # Use of a wildcard pattern in scripts is recommended for portability. # # Supports tab-completion if vswhere.exe is present. [Parameter(Mandatory)] # xxx: This requires PowerShell 5+, which some build hosts don't have: # [ArgumentCompleter({ # param($commandName, $paramName, $wordToComplete, $commandAst, $fakeBoundParameters) # $vswhere_found = @(Get-ChildItem -Filter vswhere.exe ` # -Path 'C:\Program Files*\Microsoft Visual Studio\Installer\' ` # -Recurse)[0] # if ($null -eq $vswhere_found) { # Write-Host "No vswhere found" # return $null # } # return & $vswhere_found -utf8 -nologo -format json -all -legacy -prerelease -products * ` # | ConvertFrom-Json ` # | ForEach-Object { $_.installationVersion } ` # | Where-Object { $_ -like "$wordToComplete*" } # })] [string] $Version, # The target architecture for the build [Parameter(Mandatory)] [ValidateSet("x86", "amd64", "arm", "arm64", IgnoreCase = $false)] [string] $TargetArch, # Select a specific Windows SDK version. [string] # xxx: This requires PowerShell 5+, which some build hosts don't have: # [ArgumentCompleter({ # param($commandName, $paramName, $wordToComplete, $commandAst, $fakeBoundParameters) # $found = @() # if (Test-Path "${env:ProgramFiles(x86)}\Windows Kits\10\Include") { # $found += $( # Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\Include"` # | Where-Object { Test-Path "$($_.FullName)\um\Windows.h" }` # | ForEach-Object { Split-Path -Leaf $_.FullName } # ) # } # if (Test-Path "${env:ProgramFiles(x86)}\Windows Kits\8.1\Include") { # $found += $( # Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\8.1\Include"` # | Where-Object { Test-Path "$($_.FullName)\um\Windows.h" }` # | ForEach-Object { Split-Path -Leaf $_.FullName } # ) # } # return $found | Where-Object { $_ -like "$wordToComplete*" } # })] $WinSDKVersion, # The host architecture to use. Not usually needed. Defaults to x86. [ValidateSet("x86", "amd64", IgnoreCase = $false)] [string] $HostArch = "x86", # The Visual C++ toolset to load [string] $VCToolsetVersion, # Prefer Visual C++ libraries with Spectre mitigations [switch] $UseSpectreMitigationLibraries, # The app platform to load. Default is "Desktop" [ValidateSet("Desktop", "UWP", IgnoreCase = $false)] [string] $AppPlatform = "Desktop", # The directory to store ephemeral files [string] $ScratchDir, # The command to execute within the VS environment. May be any invocable object. [Parameter(Mandatory, Position = 1)] $Command ) $ErrorActionPreference = 'Stop' $this_dir = $PSScriptRoot if ([string]::IsNullOrEmpty($ScratchDir)) { $ScratchDir = Join-Path (Split-Path -Parent $this_dir) "_build" } New-Item $ScratchDir -ItemType Directory -ErrorAction Ignore $vswhere = Join-Path $ScratchDir "vswhere.exe" if (!(Test-Path $vswhere)) { $ProgressPreference = "SilentlyContinue" [Net.ServicePointManager]::SecurityProtocol = 'tls12, tls11' Invoke-WebRequest ` -UseBasicParsing ` -Uri "https://github.com/microsoft/vswhere/releases/download/3.0.3/vswhere.exe" ` -OutFile $vswhere } # Ask vswhere for all the installed products: $vswhere_json = & $vswhere -utf8 -nologo -format json -all -legacy -prerelease -products * | Out-String $vs_versions = $vswhere_json | ConvertFrom-Json Write-Debug "Detected VS versions: $vs_versions" # Pick the product that matches the pattern $selected = @($vs_versions | Where-Object { $_.installationVersion -like $Version }) if ($selected.Length -eq 0) { throw "No Visual Studio was found with a version matching '$Version'" } $selected = $selected[0] Write-Host "Selected Visual Studio version $($selected.installationVersion) [$($selected.installationPath)]" # Find Windows SDK version if (-not [String]::IsNullOrEmpty($WinSDKVersion)) { $sdk_avail = @() if (Test-Path "${env:ProgramFiles(x86)}\Windows Kits\10\Include") { $sdk_avail += $( Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\Include" ` | Where-Object { Test-Path "$($_.FullName)\um\Windows.h" } ` | ForEach-Object { Split-Path -Leaf $_.FullName } ) } if (Test-Path "${env:ProgramFiles(x86)}\Windows Kits\8.1\Include") { $sdk_avail += $( Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\8.1\Include" ` | Where-Object { Test-Path "$($_.FullName)\um\Windows.h" } ` | ForEach-Object { Split-Path -Leaf $_.FullName } ) } Write-Debug "Detected Windows SDK versions: $sdk_avail" $sdk_selected = @($sdk_avail | Where-Object { $_ -like $WinSDKVersion })[0] if ($null -eq $sdk_selected) { throw "No Windows SDK version was found matching '$WinSDKVersion' (Of $sdk_avail)" } $WinSDKVersion = $sdk_selected } # Find the environment-activation script for the chosen VS $vsdevcmd_bat = @(Get-ChildItem ` -Path $selected.installationPath ` -Filter "VsDevCmd.bat" -Recurse)[0] $env_script_content = "" # Use batch and the 'set' command to get the required environment variables out. if ($null -eq $vsdevcmd_bat) { Write-Warning "No VsDevCmd.bat found for the requested VS version. Falling back to vcvarsall.bat" Write-Warning "Additional platform selection functionality will be limited" $vcvarsall_bat = @(Get-ChildItem -Path $selected.installationPath -Filter "vcvarsall.bat" -Recurse)[0] if ($null -eq $vcvarsall_bat) { throw "No VsDevCmd.bat nor vcvarsall.bat file found for requested Visual Studio version '$($selected.installationVersion)'" } Write-Debug "Using vcvarsall: [$($vcvarsall_bat.FullName)]" $env_script_content = @" @echo off call "$($vcvarsall_bat.FullName)" $TargetArch $WinSDKVersion set _rc=%ERRORLEVEL% set exit /b %_rc% "@ } else { # Build up the argument string to load the appropriate environment $argstr = "-no_logo -arch=$TargetArch -host_arch=$HostArch -app_platform=$AppPlatform" if ($UseSpectreMitigationLibraries) { $argstr += " -vcvars_spectre_libs=spectre" } if ($WinSDKVersion) { $argstr += " -winsdk=$WinSDKVersion" } if ($VCToolsetVersion) { $argstr += " -vcvars_ver=$VCToolsetVersion" } Write-Debug "Using VsDevCmd: [${vsdevcmd_bat.FullName}]" $env_script_content = @" @echo off call "$($vsdevcmd_bat.FullName)" $argstr set _rc=%ERRORLEVEL% set exit /b %_rc% "@ } # Write the script and then execute it, capturing its output Set-Content "$ScratchDir/.env.bat" $env_script_content Write-Host "Loading VS environment..." $output = & cmd.exe /c "$ScratchDir/.env.bat" if ($LASTEXITCODE -ne 0) { throw "Loading the environment failed [$LASTEXITCODE]:`n$output" } # The plain 'set' command emits VAR=VALUE lines for each loaded environment # variable. Parse those out and set them in our own environment. $prior_env = @{} foreach ($line in $output.Split("`r`n")) { if ($line -match "(\w+)=(.+)") { $varname = $Matches[1] $value = $Matches[2] # Set the environment value (may be null): $prior_env[$varname] = [System.Environment]::GetEnvironmentVariable($varname) [System.Environment]::SetEnvironmentVariable($varname, $value) } } Write-Debug "Running command: $(ConvertTo-Json $Command)" try { # Now invoke the external command. & $Command if ($LASTEXITCODE -ne 0) { # If it was a process that returned non-zero, throw an error. throw "Subcommand failed [$LASTEXITCODE]" } } finally { # Restore the prior environment foreach ($key in $prior_env.Keys) { [System.Environment]::SetEnvironmentVariable($key, $prior_env[$key]) } } libmongocrypt-1.19.0/.git-blame-ignore-revs000066400000000000000000000002021521103432300205560ustar00rootroot00000000000000# Mass reformat: d60041a1a863c27bb08c43fe56c2e9588f56d920 # Python pre-commit formatting c03df9ecc60b9d26aa67b72378f0945c1b577b72libmongocrypt-1.19.0/.gitattributes000066400000000000000000000000771521103432300173630ustar00rootroot00000000000000# Shell scripts require lf endings *.sh text eol=lf libmongocrypt-1.19.0/.github/000077500000000000000000000000001521103432300160245ustar00rootroot00000000000000libmongocrypt-1.19.0/.github/dependabot.yml000066400000000000000000000005331521103432300206550ustar00rootroot00000000000000version: 2 updates: # GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 groups: actions: patterns: - "*" # Python - package-ecosystem: "pip" directory: "/bindings/python" schedule: interval: "weekly" libmongocrypt-1.19.0/.github/workflows/000077500000000000000000000000001521103432300200615ustar00rootroot00000000000000libmongocrypt-1.19.0/.github/workflows/codeql-actions.yml000066400000000000000000000027271521103432300235210ustar00rootroot00000000000000name: "CodeQL GitHub Actions" on: push: branches: [ "master"] tags: ['*'] pull_request: paths: - .github/workflows/*.yml workflow_dispatch: schedule: - cron: '17 10 * * 2' workflow_call: inputs: ref: required: true type: string jobs: analyze-python: name: Analyze GitHub Actions if: github.repository_owner == 'mongodb' || github.event_name == 'workflow_dispatch' runs-on: "ubuntu-latest" timeout-minutes: 360 permissions: # required for all workflows security-events: write # required to fetch internal or private CodeQL packs packages: read steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 ref: ${{ inputs.ref }} persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4 with: languages: actions build-mode: none # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs queries: security-extended - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4 with: category: "/language:actions" libmongocrypt-1.19.0/.github/workflows/codeql-python.yml000066400000000000000000000037161521103432300234010ustar00rootroot00000000000000name: "CodeQL Python" on: push: branches: [ "master"] tags: ['*'] pull_request: paths: - bindings/python/* - bindings/python/**/*.py - .github/workflows/*python.yml schedule: - cron: '17 10 * * 2' workflow_dispatch: workflow_call: inputs: ref: required: true type: string jobs: analyze-python: name: Analyze Python if: github.repository_owner == 'mongodb' || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') runs-on: "macos-latest" timeout-minutes: 360 permissions: # required for all workflows security-events: write # required to fetch internal or private CodeQL packs packages: read steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 ref: ${{ inputs.ref }} persist-credentials: false - uses: actions/setup-python@v6 with: python-version: 3.x # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4 with: languages: python build-mode: none # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs queries: security-extended config: | paths: - bindings/python/pymongocrypt - name: Install package run: | cd bindings/python export LIBMONGOCRYPT_VERSION=$(cat ./scripts/libmongocrypt-version.txt) git fetch origin $LIBMONGOCRYPT_VERSION bash ./scripts/release.sh pip install dist/*.whl - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4 with: category: "/language:python" libmongocrypt-1.19.0/.github/workflows/dist-python.yml000066400000000000000000000042561521103432300230750ustar00rootroot00000000000000name: Python Dist on: push: tags: - "pymongocrypt-[0-9]+.[0-9]+.[0-9]+" - "pymongocrypt-[0-9]+.[0-9]+.[0-9]+.post[0-9]+" - "pymongocrypt-[0-9]+.[0-9]+.[0-9]+[a-b][0-9]+" - "pymongocrypt-[0-9]+.[0-9]+.[0-9]+rc[0-9]+" pull_request: paths: - bindings/python/* - bindings/python/**/*.py - .github/workflows/*python.yml workflow_dispatch: workflow_call: inputs: ref: required: true type: string concurrency: group: python-dist-${{ github.ref }} cancel-in-progress: true defaults: run: working-directory: ./bindings/python shell: bash -eux {0} jobs: build_dist: if: github.repository_owner == 'mongodb' || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] fail-fast: false steps: - name: Checkout libmongocrypt uses: actions/checkout@v5 with: fetch-depth: 0 ref: ${{ inputs.ref }} persist-credentials: false - uses: actions/setup-python@v6 with: python-version: "3.9" cache: 'pip' cache-dependency-path: 'bindings/python/pyproject.toml' allow-prereleases: true - name: Build and test dist files run: | export LIBMONGOCRYPT_VERSION=$(cat ./scripts/libmongocrypt-version.txt) git fetch origin $LIBMONGOCRYPT_VERSION bash ./scripts/release.sh - uses: actions/upload-artifact@v5 with: name: dist-${{ matrix.os }} path: ./bindings/python/dist/*.* if-no-files-found: error collect_dist: runs-on: ubuntu-latest needs: [build_dist] name: Collect dist files steps: - name: Download all workflow run artifacts uses: actions/download-artifact@v6 - name: Flatten directory working-directory: . run: | find . -mindepth 2 -type f -exec mv {} . \; find . -type d -empty -delete - uses: actions/upload-artifact@v5 with: name: all-dist-${{ github.run_id }} path: "./*" libmongocrypt-1.19.0/.github/workflows/release-python.yml000066400000000000000000000100411521103432300235370ustar00rootroot00000000000000name: Python Release on: workflow_dispatch: inputs: following_version: description: "The post (dev) version to set" dry_run: description: "Dry Run?" default: false type: boolean schedule: - cron: '30 5 * * *' env: # Changes per repo PRODUCT_NAME: PyMongoCrypt EVERGREEN_PROJECT: libmongocrypt # Constant # inputs will be empty on a scheduled run. so, we only set dry_run # to 'false' when the input is set to 'false'. DRY_RUN: ${{ ! contains(inputs.dry_run, 'false') }} FOLLOWING_VERSION: ${{ inputs.following_version || '' }} defaults: run: shell: bash -eux {0} jobs: pre-publish: environment: release-python if: github.repository_owner == 'mongodb' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: id-token: write contents: write outputs: version: ${{ steps.pre-publish.outputs.version }} steps: - uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 with: app_id: ${{ vars.APP_ID }} private_key: ${{ secrets.APP_PRIVATE_KEY }} - uses: mongodb-labs/drivers-github-tools/setup@v3 with: aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} aws_region_name: ${{ vars.AWS_REGION_NAME }} aws_secret_id: ${{ secrets.AWS_SECRET_ID }} - uses: mongodb-labs/drivers-github-tools/python/pre-publish@v3 id: pre-publish with: working_directory: ./bindings/python dry_run: ${{ env.DRY_RUN }} tag_template: "pymongocrypt-${VERSION}" tag_message_template: "Release PyMongoCrypt ${VERSION}" build-dist: needs: [pre-publish] uses: ./.github/workflows/dist-python.yml with: ref: ${{ needs.pre-publish.outputs.version }} static-python: needs: [pre-publish] uses: ./.github/workflows/codeql-python.yml with: ref: ${{ needs.pre-publish.outputs.version }} static-actions: needs: [pre-publish] uses: ./.github/workflows/codeql-actions.yml with: ref: ${{ needs.pre-publish.outputs.version }} publish: needs: [build-dist, static-python, static-actions] name: Upload release to PyPI runs-on: ubuntu-latest environment: release-python permissions: id-token: write steps: - name: Download all the dists uses: actions/download-artifact@v6 with: name: all-dist-${{ github.run_id }} path: dist/ - name: Publish package distributions to TestPyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 with: repository-url: https://test.pypi.org/legacy/ skip-existing: true attestations: ${{ env.DRY_RUN }} - name: Publish package distributions to PyPI if: startsWith(env.DRY_RUN, 'false') uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 post-publish: needs: [publish] runs-on: ubuntu-latest environment: release-python permissions: id-token: write contents: write attestations: write security-events: write steps: - uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 with: app_id: ${{ vars.APP_ID }} private_key: ${{ secrets.APP_PRIVATE_KEY }} - uses: mongodb-labs/drivers-github-tools/setup@v3 with: aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} aws_region_name: ${{ vars.AWS_REGION_NAME }} aws_secret_id: ${{ secrets.AWS_SECRET_ID }} - uses: mongodb-labs/drivers-github-tools/python/post-publish@v3 with: following_version: ${{ env.FOLLOWING_VERSION }} working_directory: ./bindings/python product_name: ${{ env.PRODUCT_NAME }} sbom_in_path: bindings/python/sbom.json kondukto_sub_project: pymongocrypt evergreen_project: ${{ env.EVERGREEN_PROJECT }} tag_template: "pymongocrypt-${VERSION}" token: ${{ github.token }} dry_run: ${{ env.DRY_RUN }}libmongocrypt-1.19.0/.github/workflows/test-python.yml000066400000000000000000000032111521103432300230770ustar00rootroot00000000000000name: Python Tests on: push: pull_request: paths: - bindings/python/* - bindings/python/**/*.py - .github/workflows/*python.yml concurrency: group: python-test-${{ github.ref }} cancel-in-progress: true defaults: run: working-directory: ./bindings/python shell: bash -eux {0} jobs: static: if: github.repository_owner == 'mongodb' runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v6 - name: "Run pre-commit" working-directory: . run: | pip install -U -q pre-commit pre-commit run --all-files --hook-stage manual - run: | pip install check-manifest check-manifest -v build: if: github.repository_owner == 'mongodb' runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ["3.9", "3.13", "3.14"] steps: - uses: actions/checkout@v5 with: fetch-depth: 0 persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: allow-prereleases: true python-version: ${{ matrix.python-version }} - name: Build and test dist files run: | if [ "${{ matrix.python-version }}" == "3.14" ]; then export PIP_PRE=1 fi export LIBMONGOCRYPT_VERSION=$(cat ./scripts/libmongocrypt-version.txt) git fetch origin $LIBMONGOCRYPT_VERSION bash ./scripts/release.sh libmongocrypt-1.19.0/.github/workflows/zizmor.yml000066400000000000000000000007551521103432300221450ustar00rootroot00000000000000name: GitHub Actions Security Analysis with zizmor on: push: branches: ["master"] pull_request: branches: ["**"] jobs: zizmor: name: zizmor latest via Cargo runs-on: ubuntu-latest permissions: security-events: write steps: - name: Checkout repository uses: actions/checkout@v5 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@135698455da5c3b3e55f73f4419e481ab68cdd95 # v0.4.1libmongocrypt-1.19.0/.github/zizmor.yml000066400000000000000000000002721521103432300201020ustar00rootroot00000000000000rules: unpinned-uses: config: policies: actions/*: ref-pin mongodb-labs/drivers-github-tools/*: ref-pin mongodb-labs/drivers-evergreen-tools: ref-pin libmongocrypt-1.19.0/.gitignore000066400000000000000000000010741521103432300164560ustar00rootroot00000000000000doc/html doc/latex test/schema.json .vscode # Default build directories cmake-build cmake-build-nocrypto _build/ # CMake generated files *.sln *.vcxproj *.vcxproj.filters *.xcodeproj CMakeCache.txt CTestTestFile.cmake CMakeFiles CMakeScripts Debug/* Makefile Win32/* cmake_install.cmake x64/* Testing/ CPackConfig.cmake CPackSourceConfig.cmake dist_manifest.txt *.sdf .vs install_manifest.txt # Build artifacts. *.a *.dylib *.gcda *.gcno *.gz *.lo *.o *.pc *.so *.so.* # Generated header src/mongocrypt-export.h # IDEs .idea/ VERSION_CURRENT .csfle *.pyc .DS_Store libmongocrypt-1.19.0/.lsan-suppressions000066400000000000000000000002401521103432300201710ustar00rootroot00000000000000leak:ccrng_cryptographic_init_once leak:ccrng_cryptographic_generate leak:CRYPTO_zalloc # Ignore leak reported in dlopen error. leak:_dlerror_run leak:_dlerror libmongocrypt-1.19.0/.pre-commit-config.yaml000066400000000000000000000033451521103432300207520ustar00rootroot00000000000000files: '^bindings/python' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict - id: check-toml - id: check-yaml - id: check-json exclude: '.evergreen/atlas_data_lake/test.driverdata.json' - id: debug-statements - id: end-of-file-fixer exclude: '.*\.txt' - id: forbid-new-submodules - id: trailing-whitespace - id: check-executables-have-shebangs # We use the Python version instead of the original version which seems to require Docker # https://github.com/koalaman/shellcheck-precommit - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.9.0.6 hooks: - id: shellcheck name: shellcheck args: ["--severity=error"] - repo: https://github.com/sirosen/check-jsonschema rev: 0.29.0 hooks: - id: check-github-workflows - id: check-github-actions - id: check-dependabot - repo: https://github.com/codespell-project/codespell rev: "v2.2.6" hooks: - id: codespell exclude: | (?x)( .evergreen/github_app/package-lock.json| .evergreen/csfle/bottle.py| .pem$ ) args: ["-L", "fle"] - repo: local hooks: - id: executable-shell name: executable-shell entry: chmod +x language: system types: [shell] - repo: local hooks: - id: synchro name: synchro entry: bash ./bindings/python/scripts/synchro.sh language: python require_serial: true fail_fast: true additional_dependencies: - ruff==0.1.3 - unasync - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. rev: v0.1.3 hooks: - id: ruff args: [ "--fix", "--show-fixes" ] - id: ruff-format libmongocrypt-1.19.0/CHANGELOG.md000066400000000000000000000322571521103432300163060ustar00rootroot00000000000000# ChangeLog ## 1.19.0 ### Added - Stable support for prefix and suffix queries: - The `prefixPreview` query type is replaced with `prefix`. - The `suffixPreview` query type is replaced with `suffix`. - Use the `string` algorithm (formerly `textPreview`) for `prefix`, `suffix`, and `substringPreview` query types. ### Removed - Support for the experimental `prefixPreview` and `suffixPreview` query types. ## 1.18.1 ### Fixed - Fix QE text explicit encryption handling of `caseSensitive` and `diacriticSensitive` options. - This is a backwards breaking bug fix, but only applies to the experimental QE text algorithm "TextPreview". - Fix handling of malformed KMS replies. ## 1.18.0 ### Added - Signed binaries for macOS and Linux are now available on the GitHub release. - Linux binaries including `nocrypto` in the name have no dependency on OpenSSL. Drivers using the `nocrypto` variant are expected to set crypto callbacks (e.g. call `mongocrypt_setopt_crypto_hooks`) to do operations requiring crypto to avoid an error. - Drivers that package libmongocrypt binaries are encouraged to migrate release scripts to use these binaries. - No reduction in platform support is expected. glibc dependencies were checked against existing builds on RHEL 6.2 and Ubuntu 16.04. - Support referencing keys by `keyAltName` in `encryptedFieldsMap`. ### Changed - Final release packages in the PPA are now available by specifying `release` in the repository configuration in place of the major/minor version (e.g., `1.17`). Details in `README.md`. - Bump downloaded libbson version from 2.1.0 to 2.3.0. ### Deprecated - RHEL 6.2 builds are deprecated and may be removed in the future. The `linux-x86_64-glibc_2_7-nocrypto` release build may be used instead and has equivalent glibc requirements. ### Removed - The configure-time CMake parameter `ENABLE_WINDOWS_STATIC_RUNTIME` has been removed. Users that need the static MSVCRT library should instead set the [`CMAKE_MSVC_RUNTIME_LIBRARY`][msvcrt] built-in CMake parameter when configuring libmongocrypt. - Packages for Debian 9 and Debian 10. [msvcrt]: https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html ## 1.17.3 ### Fixed - Fix check in KMIP parser. ## 1.17.2 ### Fixed - Fix build warning with GCC 16 ## 1.17.1 Add package for Debian Trixie. ## 1.17.0 ### New features - Support mixing QE and unencrypted JSON schemas. ### Fixed - Comply with CMake policy CMP0148 (use `FindPython` instead of `FindPythonInterp` and `FindPythonLibs`). - Fix possible resource leak in Queryable Encryption. ## 1.16.0 ### Changed - Set CMake minimum required version to `3.15...4.0` (with maximum policy version set to `4.0`). - `FetchContent_MakeAvailable()` is used to populate dependencies instead of `FetchContent_Populate()`. - This applies to MongoDB C Driver when `MONGOCRYPT_MONGOC_DIR` is not set to `USE-SYSTEM`. - This applies to IntelDFP when `MONGOCRYPT_DFP_DIR` is not set to `USE-SYSTEM`. - Note: `FetchContent_Populate()` is still used for CMake versions prior to 3.18 to avoid `add_subdirectory()` behavior. - Bump downloaded libbson version from 1.30.3 to 2.1.0. ### New features - Support in-place retry on KMS requests. ### Fixed - Do not propagate `-fPIC` in CMake targets. ## 1.15.2 ### Fixed - Rename internal headers to avoid conflicts building libmongocrypt and C driver together. ## 1.15.1 ### Fixed - Fix possible error when text options include multiple query types and are used for a find payload: `Text search query specification cannot contain multiple query type specifications`. - Require setting contention for text options. - Improve error message if text options are unset when using text algorithm. ## 1.15.0 ### New features - Support experimental Queryable Encryption text indexes with `cleanupStructuredEncryptionData` and `compactStructuredEncryptionData`. - Support experimental explicit encryption for algorithm type: `textPreview` and query types: `prefixPreview`, `suffixPreview` and `substringPreview` - Add `mongocrypt_setopt_algorithm_text` to apply options for explicit encryption. ### Fixed - Bypass command `buildinfo` (previously only `buildInfo` was bypassed). - Bypass command `serverStatus`. ### Removed - Support for building with Visual Studio 2015. Use Visual Studio 2017 or newer. ## 1.14.1 ### Fixed - Fix possible missing error state on `mongocrypt_ctx_finalize`. ### Improvements - Add Ubuntu 24.04 package. ## 1.14.0 ### Fixed - Fix building against libbson with extra alignment enabled (`ENABLE_EXTRA_ALIGNMENT=ON`). - Retry KMS encrypt request for context created by `mongocrypt_ctx_rewrap_many_datakey_init`. ### Improvements - Improve performance of OpenSSL crypto operations. - Improve error for incorrect path to crypt_shared library. ### New features - Support experimental Queryable Encryption text indexes for automatic encryption. ## 1.13.2 ### Notes - Bump downloaded libbson version from 1.28.1 to 1.30.3. Fixes a CMake configure error on macOS with CMake 4. ## 1.13.1 ### Fixed - Fix possible double free on parse error of malformed payload. - Fix build failure when configuring with `ENABLE_TRACE=ON`. - Fix possible redefinition of `_GNU_SOURCE`. ## 1.13.0 ### New features - Support automatic encryption for `$lookup` stages in `aggregate` pipelines on MongoDB server 8.1+. ### Fixed - Restore default behavior to disable extra alignment when importing libbson. This was the default behavior in 1.11. This can be overridden by setting the CMake option `ENABLE_EXTRA_ALIGNMENT=ON`. ### Removed - Support for macOS versions older than 11. libmongocrypt is supported and tested with macOS 11+. ## 1.12.0 ### New features - Add option to configure Data Encryption Key cache lifetime (`mongocrypt_setopt_key_expiration`) - Add opt-in retry behavior for KMS operations (`mongocrypt_setopt_retry_kms`) ### Removed - libmongocrypt is no longer published in the MongoDB package repository for RHEL 6. libmongocrypt may instead be built from source on RHEL 6, but support for RHEL 6 will be dropped in a future release. ### Notes - This release unintentionally changes the default behavior of extra alignment with importing libbson. See 1.13.0 release notes. ## 1.11.0 ### New features - Support `range` algorithm as stable. ### Deprecated - The Windows download URLs for [stable](https://s3.amazonaws.com/mciuploads/libmongocrypt/windows/latest_release/libmongocrypt.tar.gz) and [unstable](https://s3.amazonaws.com/mciuploads/libmongocrypt/windows/latest_release/libmongocrypt_unstable.tar.gz) are now deprecated. See the GitHub Release page for Windows downloads. ## 1.10.1 ## Fixed - Document `range` algorithm as unstable. ## 1.10.0 ### New features - Support KMIP `delegated` option. - Support processing `bulkWrite` command. - Support `range` algorithm. ## 1.9.1 ### New features - Add Debian 12 packages ## 1.9.0 ### New features - Support named KMS providers. - Add `arm64` Debian packages ## Fixed - Fix `arm64` Alpine build. ## 1.8.4 ### Fixed - Fix `aarch64` packages for RHEL 8, RHEL 9, Amazon 2023, and Amazon 2 ## 1.8.3 ### Improvements - Include packages for RHEL 8, RHEL 9, and Amazon 2023 ## 1.8.2 ### Fixed - Fix possible leaks in Queryable Encryption in errors on malformed data. ## 1.8.1 - Bypass search index management commands in automatic encryption ## 1.8.0 This release adds stable support of the Queryable Encryption (QE) feature for the "Indexed" and "Unindexed" algorithms. ## 1.8.0-alpha1 This release makes backwards breaking changes to Queryable Encryption (QE) behavior added in the 1.8.0-alpha0 release: - Do not apply default to min/max values for int/long/date. - Enable the QEv2 protocol by default. Remove function to enable QEv2. ## 1.8.0-alpha0 ### Improvements - Support Queryable Encryption v2 protocol. ## 1.7.2 ### Improvements - Add toggle for Decimal128 Range Support. ### Fixed - Fix i686 (32-bit) build. - Fix 32-bit ARM build. ## 1.7.1 ### Improvements - Vendor Intel DFP library and allow using system DFP. ### Fixed - Fix possible abort on base64 decode error of KMS messages. - Fix ILP32-target builds. - Fix LTO build. - Fix IntelDFP to not require Git. ## 1.7.0 ### New Features - Add encryptExpression helper - Support for range index. NOTE: The Range algorithm is experimental only. It is not intended for public use. ## 1.7.0-alpha2 ### New Features - Support range index for decimal128. NOTE: The Range algorithm is experimental only. It is not intended for public use. ## 1.7.0-alpha1 ### New Features - Add encryptExpression helper ## 1.7.0-alpha0 ### New Features - Support range index for int32, int64, double, and date. NOTE: The Range algorithm is experimental only. It is not intended for public use. ## 1.6.2 ## Fixed - Fix build on FreeBSD. - Set context error state during KMS provider validation. ## 1.6.1 ## Fixed - Fix libbson dependency in pkg-config for MongoDB repository package. ## 1.6.0 ## New Features - Support accessToken to authenticate with Azure. ## Fixed - Use correct schema when `collMod` command includes `validator.$jsonSchema`. ## 1.6.0-alpha0 ### New Features - Support accessToken to authenticate with GCP. ### Improvements - Use CRLF, not LF, for HTTP request newlines. - Include full body of HTTP errors in `mongocrypt_status_t`. ## 1.5.2 ### Fixed - Fix datakey decryption requiring multiple rounds of KMS requests. ## 1.5.1 ## Warnings - This release has a severe bug in the context returned by `mongocrypt_ctx_rewrap_many_datakey_init` that may result in data corruption. Please upgrade to 1.5.2 before using `mongocrypt_ctx_rewrap_many_datakey_init`. ### New Features - Update Java bindings to support remaining 1.5.0 API. ## 1.5.0 ## Warnings - This release has a severe bug in the context returned by `mongocrypt_ctx_rewrap_many_datakey_init` that may result in data corruption. Please upgrade to 1.5.2 before using `mongocrypt_ctx_rewrap_many_datakey_init`. ## Fixed - Update to use new payload for FLE 2.0 find. - Require contention factor. ## 1.5.0-rc2 ### Fixed - Fix handling of create command with $jsonSchema. - Fix leak on encrypt or decrypt error. ## Improved - Accept string values for QueryType and IndexType. ## 1.4.1 ### Fixed - Add missing MONGOCRYPT_EXPORT to mongocrypt_ctx_provide_kms_providers ## 1.5.0-rc1 ## Fixed - Revert new payload for FLE 2.0 find. - Do not send "create" and "createIndexes" to mongocryptd when bypassing query analysis. ## 1.5.0-rc0 ## Fixed - Account for shared library rename. - Update to use new payload for FLE 2.0 find. ## 1.5.0-alpha2 ## New Features - Fix explain when using csfle shared library. - Do not bypass "create" or "createIndexes" commands. Support "collMod". - Bypass "hello", "buildInfo", "getCmdLineOpts", and "getLog" commands. ## Fixed - Preserve $db in output command. - Add missing MONGOCRYPT_EXPORT to mongocrypt_ctx_provide_kms_providers ## 1.5.0-alpha1 ### Fixed - Pick a random contention factor on FLE 2.0 insert. ## 1.5.0-alpha0 ### New Features - Support FLE 2.0. - Support FLE 1.0 Shared Library. - Support Key Management API. ## 1.4.0 ### New Features - Support on-demand credentials with `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` state and `mongocrypt_ctx_provide_kms_providers`. ## 1.4.0-alpha0 ### New Features - Support on-demand AWS credentials with `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` state and `mongocrypt_ctx_provide_kms_providers`. ### Fixed - Resolve 32 bit Windows compile errors. ## 1.3.1 ### New Features - Support custom key material through `mongocrypt_ctx_setopt_key_material`. ### Fixed - Fix deprecation warnings with OpenSSL 3.0. - Resolve possible symbol conflicts with OpenSSL. ## 1.3.0 - Support "kmip" KMS provider. - Add mongocrypt_kms_ctx_get_kms_provider. - Apply default port to endpoints returned in mongocrypt_kms_ctx_endpoint ## 1.2.2 - Fix pkg-config and PPA build dependency on libbson. - Fix JSON schema caching behavior when server reports no JSON schema. ## 1.2.1 ### Fixed - Fix possible crash when oauth credentials expire. ## 1.2.0 ### Added - Support AWS temporary credentials via session token. ### Fixed - Add "=" padding to base64url encoding. ## 1.1.0 ### Added - Add ENABLE_PIC cmake option, set to ON by default, so static libraries build with -fPIC by default on relevant systems. ### Fixed - Errors produced in all crypto callbacks are propagated to user. ## 1.1.0-beta1 ### Deprecated - mongocrypt_setopt_kms_provider_aws and mongocrypt_setopt_kms_provider_local are deprecated in favor of the more flexible mongocrypt_setopt_kms_providers, which supports configuration of all KMS providers. - mongocrypt_ctx_setopt_masterkey_aws, mongocrypt_ctx_setopt_masterkey_aws_endpoint, and mongocrypt_ctx_setopt_masterkey_local are deprecated in favor of the more flexible mongocrypt_ctx_setopt_key_encryption_key, which supports configuration for all KMS providers. ### Added - Introduces a new crypto hook for signing the JSON Web Token (JWT) for Google Cloud Platform (GCP) requests: - mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5 - Introduces a CLI utility `csfle` to test the context state machine against live KMS, mongocryptd, and mongod. See ./test/util/README.md. - Introduces two new functions to the libmongocrypt API. - mongocrypt_setopt_kms_providers To set the KMS providers. - mongocrypt_ctx_setopt_key_encryption_key To set the key encryption key. - Adds support for Azure and GCP KMS providers. libmongocrypt-1.19.0/CMakeLists.txt000066400000000000000000000632071521103432300172340ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.15...4.0) project (mongocrypt C) # Used for the csfle-markup util: enable_language (CXX OPTIONAL) set (CMAKE_C_STANDARD 99) option (ENABLE_STATIC "Install static libraries" ON) option (ENABLE_PIC "Enables building of position independent code for static library components." ON ) option (ENABLE_BUILD_FOR_PPA "Maintainer-only option for preparing PPA build" OFF) option (ENABLE_ONLINE_TESTS "Enable online tests and the csfle utility. Requires libmongoc." ON) if (MINGW) # MSVCRT-based mingw-w64 GCC requires this macro to enable C99 format specifiers. This does not apply to UCRT-based # mingw-w64 GCC, but apply the workaround anyways until bson-compat.h no longer requires this. add_compile_definitions ("__USE_MINGW_ANSI_STDIO=1") endif () list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) include (GNUInstallDirs) include (LTO) include (ImportBSON) option (MONGOCRYPT_ENABLE_DECIMAL128 "Enable extended support for Decimal128" ON) mark_as_advanced (MONGOCRYPT_ENABLE_DECIMAL128) set (maybe_dfp_library) if (MONGOCRYPT_ENABLE_DECIMAL128) include (ImportDFP) set (maybe_dfp_library mongocrypt::intel_dfp) endif () if (USE_SHARED_LIBBSON AND ENABLE_BUILD_FOR_PPA) message (FATAL_ERROR "PPA build requires static linking to libbson") endif () if (DEFINED MONGOCRYPT_DFP_DIR AND ENABLE_BUILD_FOR_PPA) message (FATAL_ERROR "PPA build requires building with vendored Intel DFP") endif () # Enable a higher warning level and warnings-as-errors include (MongoC-Warnings) option (ENABLE_MORE_WARNINGS_AS_ERRORS "Enable extra warnings-as-errors in the build." OFF) if (ENABLE_MORE_WARNINGS_AS_ERRORS) mongoc_add_platform_compile_options ( msvc:/W3 msvc:/WX msvc:/wd4996 msvc:/wd4359 gnu-like:-Wall gnu-like:-Werror gnu-like:-Wswitch-enum gnu-like:-Wswitch-default gnu-like:-Wsign-compare ) add_link_options ( # Enable link-time warnings. VS 2015 sees LNK4099 spuriously, but it is a # non-fatal issue. LNK4217 and LNK4049 appear when using dllimport on # locally-defined symbols in kms-message. "$,/WX;/ignore:4217;/ignore:4049;/ignore:4099,-Werror>" ) endif () include (GenerateExportHeader) include (CTest) include (Platform) set (MONGOCRYPT_PUBLIC_HEADERS src/mongocrypt-compat.h src/mongocrypt.h ) set (MONGOCRYPT_SOURCES src/crypto/cng.c src/crypto/commoncrypto.c src/crypto/libcrypto.c src/crypto/none.c src/mc-array.c src/mc-efc.c src/mc-fle2-find-range-payload.c src/mc-fle2-find-range-payload-v2.c src/mc-fle2-insert-update-payload.c src/mc-fle2-insert-update-payload-v2.c src/mc-fle2-encryption-placeholder.c src/mc-fle2-find-equality-payload.c src/mc-fle2-find-equality-payload-v2.c src/mc-fle2-find-text-payload.c src/mc-fle2-payload-iev.c src/mc-fle2-payload-iev-v2.c src/mc-fle2-payload-uev.c src/mc-fle2-payload-uev-common.c src/mc-fle2-payload-uev-v2.c src/mc-fle2-rfds.c src/mc-fle2-tag-and-encrypted-metadata-block.c src/mc-parse-utils.c src/mc-range-edge-generation.c src/mc-range-mincover.c src/mc-range-encoding.c src/mc-rangeopts.c src/mc-textopts.c src/mc-reader.c src/mc-schema-broker.c src/mc-str-encode-string-sets.c src/mc-text-search-str-encode.c src/mc-tokens.c src/mc-writer.c src/mongocrypt-binary.c src/mongocrypt-buffer.c src/mongocrypt-cache.c src/mongocrypt-cache-collinfo.c src/mongocrypt-cache-key.c src/mongocrypt-cache-oauth.c src/mongocrypt-ciphertext.c src/mongocrypt-crypto.c src/mongocrypt-ctx-datakey.c src/mongocrypt-ctx-decrypt.c src/mongocrypt-ctx-encrypt.c src/mongocrypt-ctx-rewrap-many-datakey.c src/mongocrypt-ctx.c src/mongocrypt-endpoint.c src/mongocrypt-kek.c src/mongocrypt-key.c src/mongocrypt-key-broker.c src/mongocrypt-kms-ctx.c src/mongocrypt-log.c src/mongocrypt-marking.c src/mongocrypt-opts.c src/mongocrypt-status.c src/mongocrypt-traverse-util.c src/mongocrypt-util.c src/mongocrypt.c src/os_win/os_mutex.c src/os_posix/os_mutex.c src/os_win/os_dll.c src/os_posix/os_dll.c src/unicode/case-fold-map.c src/unicode/diacritic-fold-map.c src/unicode/fold.c ) # If MONGOCRYPT_CRYPTO is not set, choose a system default. if (NOT MONGOCRYPT_CRYPTO) set (MONGOCRYPT_CRYPTO OpenSSL) if (APPLE) set (MONGOCRYPT_CRYPTO CommonCrypto) elseif (WIN32) set (MONGOCRYPT_CRYPTO CNG) endif () endif () # Otherwise, override with crypto hooks. if (DISABLE_NATIVE_CRYPTO) set (MONGOCRYPT_CRYPTO none) endif () set (MONGOCRYPT_ENABLE_CRYPTO 0) set (MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO 0) set (MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO 0) set (MONGOCRYPT_ENABLE_CRYPTO_CNG 0) if (MONGOCRYPT_CRYPTO STREQUAL CommonCrypto) message ("Building with common crypto") set (MONGOCRYPT_ENABLE_CRYPTO 1) set (MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO 1) elseif (MONGOCRYPT_CRYPTO STREQUAL CNG) message ("Building with CNG") set (MONGOCRYPT_ENABLE_CRYPTO 1) set (MONGOCRYPT_ENABLE_CRYPTO_CNG 1) elseif (MONGOCRYPT_CRYPTO STREQUAL OpenSSL) message ("Building with OpenSSL") include (FindOpenSSL) message ("Found OpenSSL version ${OPENSSL_VERSION}") set (MONGOCRYPT_ENABLE_CRYPTO 1) set (MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO 1) elseif (MONGOCRYPT_CRYPTO STREQUAL none) message ("Building with no native crypto, hooks MUST be supplied with mongocrypt_setopt_crypto_hooks") else () message (FATAL_ERROR "Unknown crypto provider ${MONGOCRYPT_CRYPTO}") endif () set (BUILD_VERSION "0.0.0" CACHE STRING "Library version") if (BUILD_VERSION STREQUAL "0.0.0") if (EXISTS ${CMAKE_BINARY_DIR}/VERSION_CURRENT) file (STRINGS ${CMAKE_BINARY_DIR}/VERSION_CURRENT BUILD_VERSION) elseif (EXISTS ${PROJECT_SOURCE_DIR}/VERSION_CURRENT) file (STRINGS ${PROJECT_SOURCE_DIR}/VERSION_CURRENT BUILD_VERSION) message (STATUS "File VERSION_CURRENT contained BUILD_VERSION ${BUILD_VERSION}") else () find_package (Python COMPONENTS Interpreter) if (TARGET Python::Interpreter) execute_process ( COMMAND "${Python_EXECUTABLE}" etc/calc_release_version.py WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE CALC_RELEASE_VERSION RESULT_VARIABLE CALC_RELEASE_VERSION_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ) if (NOT CALC_RELEASE_VERSION_RESULT STREQUAL 0) # If python failed above, stderr would tell the user about it message (FATAL_ERROR "BUILD_VERSION not specified and could not be calculated\ (script invocation failed); specify in CMake command, -DBUILD_VERSION=" ) else () set (BUILD_VERSION ${CALC_RELEASE_VERSION}) message ("calculated BUILD_VERSION ${BUILD_VERSION}") file (WRITE ${CMAKE_BINARY_DIR}/VERSION_CURRENT ${CALC_RELEASE_VERSION}) endif () else () message (FATAL_ERROR "BUILD_VERSION not specified and could not be calculated\ (Python was not found on the system); specify in CMake command, -DBUILD_VERSION=" ) endif () message (STATUS "Storing BUILD_VERSION ${BUILD_VERSION} in file VERSION_CURRENT for later use") file (WRITE ${PROJECT_SOURCE_DIR}/VERSION_CURRENT ${BUILD_VERSION}) endif () else () message (STATUS "Storing BUILD_VERSION ${BUILD_VERSION} in file VERSION_CURRENT for later use") file (WRITE ${PROJECT_SOURCE_DIR}/VERSION_CURRENT ${BUILD_VERSION}) endif () set (MONGOCRYPT_BUILD_VERSION ${BUILD_VERSION}) configure_file ( "${PROJECT_SOURCE_DIR}/src/mongocrypt-config.h.in" "${PROJECT_BINARY_DIR}/src/mongocrypt-config.h" ) # Define the mc-mlib target, which is private and header-only. It is not exported # nor are its headers installed. add_library (_mongo-mc-mlib INTERFACE) add_library (mongo::mc-mlib ALIAS _mongo-mc-mlib) list (APPEND MCMLIB_DEFINITIONS MLIB_USER) CHECK_INCLUDE_FILE (strings.h HAVE_STRINGS_H) if (HAVE_STRINGS_H) list (APPEND MCMLIB_DEFINITIONS MLIB_HAVE_STRINGS_H) endif () set_property( TARGET _mongo-mc-mlib APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS ${MCMLIB_DEFINITIONS} ) set_property( TARGET _mongo-mc-mlib APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$" ) # kms-message add_subdirectory (kms-message) # Define mongocrypt library add_library (mongocrypt SHARED ${MONGOCRYPT_SOURCES}) target_include_directories ( mongocrypt PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/kms-message/src" "${CMAKE_CURRENT_SOURCE_DIR}/src" PUBLIC "$" ) target_link_libraries ( mongocrypt PRIVATE _mongocrypt::libbson_for_shared kms_message_static $ PUBLIC mongocrypt::platform ${maybe_dfp_library} ) if (NOT USE_SHARED_LIBBSON) if (APPLE) message ("compiling with unexported symbols list to hide bson symbols") set_target_properties (mongocrypt PROPERTIES LINK_FLAGS "-Wl,-unexported_symbols_list,\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/libmongocrypt-hidden-symbols.txt\"") elseif (UNIX) message ("compiling with version map to version and hide bson symbols") set_target_properties (mongocrypt PROPERTIES LINK_FLAGS "-Wl,--version-script=\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/libmongocrypt-hidden-symbols.map\"") endif () endif () if (NOT WIN32 AND CMAKE_VERSION VERSION_GREATER 3.25) # Do not enable conversion warnings with older cmake. Older cmake does not support # including headers as system headers when the headers are added to the project via # the add_subdirectory() CMake command. libbson produces conversion warnings and is # included in ImportBSON.cmake. target_compile_options (mongocrypt PRIVATE -Wconversion -Wsign-conversion) target_compile_options (kms_message_static PRIVATE -Wconversion -Wsign-conversion) endif () generate_export_header (mongocrypt EXPORT_FILE_NAME src/mongocrypt-export.h BASE_NAME mongocrypt ) add_library (mongocrypt_static STATIC ${MONGOCRYPT_SOURCES}) # Checking CMAKE_C_FLAGS for -fPIC is not a foolproof way of checking whether # -fPIC was set as a compiler flag. However, users were instructed before to # pass -fPIC through CMAKE_C_FLAGS. This will prevent redundant output in # the common case that users are setting -DCMAKE_C_FLAGS='-fPIC' string (FIND "${CMAKE_C_FLAGS}" "-fPIC" FPIC_LOCATION) if (NOT WIN32 AND ENABLE_PIC AND "${FPIC_LOCATION}" EQUAL "-1") set_property (TARGET mongocrypt_static PROPERTY POSITION_INDEPENDENT_CODE TRUE) message ("Adding -fPIC to compilation of mongocrypt_static components") endif () target_include_directories ( mongocrypt_static PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/kms-message/src" "${CMAKE_CURRENT_SOURCE_DIR}/src" PUBLIC "$" ) target_compile_definitions ( mongocrypt_static PUBLIC MONGOCRYPT_STATIC_DEFINE KMS_MSG_STATIC ) target_link_libraries ( mongocrypt_static PRIVATE _mongocrypt::libbson_for_static kms_message_static $ PUBLIC mongocrypt::platform ${maybe_dfp_library} ) set (PKG_CONFIG_STATIC_LIBS "\${prefix}/${CMAKE_INSTALL_LIBDIR}/libmongocrypt-static.a") set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} ${CMAKE_THREAD_LIBS_INIT}") if (CMAKE_DL_LIBS) string (APPEND PKG_CONFIG_STATIC_LIBS " -l${CMAKE_DL_LIBS}") endif () set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} \${prefix}/${CMAKE_INSTALL_LIBDIR}/libkms_message-static.a") if (ENABLE_BUILD_FOR_PPA) set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} \${prefix}/${CMAKE_INSTALL_LIBDIR}/libbson-static-for-libmongocrypt.a") #librt needed for libbson on linux for clock_gettime find_library (RT_LIBRARY rt) if (RT_LIBRARY) set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} ${RT_LIBRARY}") endif () set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -pthread") endif () if (MONGOCRYPT_DFP_DIR STREQUAL "USE-SYSTEM" AND MONGOCRYPT_ENABLE_DECIMAL128) get_property (SYSTEM_DFP_LOC TARGET intel_dfp PROPERTY IMPORTED_LOCATION) set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} ${SYSTEM_DFP_LOC}") endif () if (MONGOCRYPT_CRYPTO STREQUAL CommonCrypto) target_link_libraries (mongocrypt PRIVATE "-framework CoreFoundation -framework Security") target_link_libraries (mongocrypt_static PRIVATE "-framework CoreFoundation -framework Security") set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -framework CoreFoundation -framework Security") elseif (MONGOCRYPT_CRYPTO STREQUAL CNG) target_link_libraries (mongocrypt PRIVATE "bcrypt") target_link_libraries (mongocrypt_static PRIVATE "bcrypt") set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -lbcrypt") elseif (MONGOCRYPT_CRYPTO STREQUAL OpenSSL) target_link_libraries (mongocrypt PRIVATE OpenSSL::SSL OpenSSL::Crypto) target_link_libraries (mongocrypt_static PRIVATE OpenSSL::SSL OpenSSL::Crypto) set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -lssl -lcrypto") endif () # Link to libm for math functions (pow, log, etc.) # Do not link on Apple. On macOS Big Sur, libm resolves to the SDK's tbd file, like: # /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/libm.tbd # Not all consumers can easily link to a tbd file (notably golang will reject a tbd suffix by default) # macOS includes libm as part of libSystem (along with libc). # It does not need to be explicitly linked. if (NOT APPLE) find_library (M_LIBRARY m) if (M_LIBRARY) set (PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -lm") endif () endif () set_target_properties (mongocrypt PROPERTIES SOVERSION 0 VERSION "0.0.0" OUTPUT_NAME "mongocrypt" ) set_target_properties (mongocrypt_static PROPERTIES SOVERSION 0 VERSION "0.0.0" OUTPUT_NAME "mongocrypt-static" ) if (BUILD_TESTING) # Use C++ in the testing DLL to ensure we can load a library with the C++ runtime enable_language (CXX) add_library (test-dll MODULE test/test-dll.cpp) set_target_properties (test-dll PROPERTIES SUFFIX ".dll" PREFIX "" ) # Create two stubbed crypt_shared libraries add_library (stubbed-crypt_shared SHARED test/crypt_shared-stub.cpp) add_library (stubbed-crypt_shared-2 SHARED test/crypt_shared-stub.cpp) set_target_properties(stubbed-crypt_shared stubbed-crypt_shared-2 PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/src" LINK_LIBRARIES _mongocrypt::libbson_for_static COMPILE_FEATURES cxx_std_11 # Define `__STDC_LIMIT_MACROS` in libbson to define integer macros (e.g. SIZE_MAX) when including in C++ code. # C99 standard notes: "C++ implementations should define these macros only when __STDC_LIMIT_MACROS is defined before is included." COMPILE_DEFINITIONS __STDC_LIMIT_MACROS PREFIX "" ) set_target_properties(stubbed-crypt_shared-2 PROPERTIES SUFFIX ".dll") if (MONGOCRYPT_TESTING_CRYPT_SHARED_FILE) # Generate a target that copies the CSFLE library into the binary directory of test-mongocrypt set (stamp "${CMAKE_CURRENT_BINARY_DIR}/mongo_crypt_v1.copied.$.stamp") add_custom_command ( OUTPUT "${stamp}" COMMAND "${CMAKE_COMMAND}" -E copy "${MONGOCRYPT_TESTING_CRYPT_SHARED_FILE}" "$/mongo_crypt_v1${CMAKE_SHARED_LIBRARY_SUFFIX}" COMMAND "${CMAKE_COMMAND}" -E touch "${stamp}" DEPENDS "${MONGOCRYPT_TESTING_CRYPT_SHARED_FILE}" COMMENT "Getting mongo_crypt library" ) add_custom_target (copy-crypt_shared ALL DEPENDS "${stamp}") else () # The first stubbed crypt_shared library will take the place of the actual crypt_shared for testing message (STATUS "Generating a stubbed crypt_shared dynamic library for use in testing.") message (STATUS "Provide a MONGOCRYPT_TESTING_CRYPT_SHARED_FILE= to provide a crypt_shared for use in testing") set_target_properties (stubbed-crypt_shared PROPERTIES # Normalize the output name expected by libmongocrypt OUTPUT_NAME "mongo_crypt_v1" ) endif () endif () set (TEST_MONGOCRYPT_SOURCES test/test-gcp-auth.c test/test-mc-cmp.c test/test-mc-efc.c test/test-mc-fle2-encryption-placeholder.c test/test-mc-fle2-find-equality-payload-v2.c test/test-mc-fle2-find-range-payload-v2.c test/test-mc-fle2-find-text-payload.c test/test-mc-fle2-payload-iev.c test/test-mc-fle2-payload-iev-v2.c test/test-mc-fle2-payload-iup.c test/test-mc-fle2-payload-iup-v2.c test/test-mc-fle2-payload-uev.c test/test-mc-fle2-payload-uev-v2.c test/test-mc-fle2-rfds.c test/test-mc-fle2-tag-and-encrypted-metadata-block.c test/test-mc-range-edge-generation.c test/test-mc-range-mincover.c test/test-mc-rangeopts.c test/test-mc-textopts.c test/test-mc-reader.c test/test-mc-text-search-str-encode.c test/test-mc-schema-broker.c test/test-mc-tokens.c test/test-mc-range-encoding.c test/test-mc-writer.c test/test-mongocrypt-assert-match-bson.c test/test-mongocrypt-buffer.c test/test-mongocrypt-cache.c test/test-mongocrypt-cache-oauth.c test/test-mongocrypt-ciphertext.c test/test-mongocrypt-cleanup.c test/test-mongocrypt-compact.c test/test-mongocrypt-crypto.c test/test-mongocrypt-crypto-hooks.c test/test-mongocrypt-crypto-std-hooks.c test/test-mongocrypt-csfle-lib.c test/test-mongocrypt-ctx-decrypt.c test/test-mongocrypt-ctx-encrypt.c test/test-mongocrypt-ctx-rewrap-many-datakey.c test/test-mongocrypt-ctx-setopt.c test/test-mongocrypt-datakey.c test/test-mongocrypt-dll.c test/test-mongocrypt-endpoint.c test/test-mongocrypt-kek.c test/test-mongocrypt-key.c test/test-mongocrypt-key-broker.c test/test-mongocrypt-key-cache.c test/test-mongocrypt-kms-ctx.c test/test-mongocrypt-kms-responses.c test/test-mongocrypt-local-kms.c test/test-mongocrypt-log.c test/test-mongocrypt-marking.c test/test-mongocrypt-opts.c test/test-mongocrypt-status.c test/test-mongocrypt-traverse-util.c test/test-mongocrypt-util.c test/test-mongocrypt.c test/test-named-kms-providers.c test/test-unicode-fold.c ) if (BUILD_TESTING) # Define test-mongocrypt add_executable (test-mongocrypt ${TEST_MONGOCRYPT_SOURCES}) # Use the static version since it allows the test binary to use private symbols target_include_directories (test-mongocrypt PRIVATE ./src "${CMAKE_CURRENT_SOURCE_DIR}/kms-message/src") target_link_libraries (test-mongocrypt PRIVATE _mongocrypt::libbson_for_static mongocrypt_static mongo::mc-mlib) target_include_directories (test-mongocrypt PRIVATE "${CMAKE_CURRENT_LIST_DIR}/test") target_compile_definitions (test-mongocrypt PRIVATE # Set a definition so that testcases can know where test-mongocrypt.exe was written to "TEST_MONGOCRYPT_OUTPUT_PATH=\"$\"" # Tell test-mongocrypt whether we have a real csfle library for testing TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB=$ # Tell test-mongocrypt the path of the libmongocrypt shared library for testing. "TEST_MONGOCRYPT_MONGOCRYPT_SHARED_PATH=\"$\"" ) add_test ( NAME mongocrypt COMMAND test-mongocrypt WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) foreach (test IN ITEMS path str) add_executable (mlib.${test}.test src/mc-mlib/${test}.test.c) add_test (mlib.${test} mlib.${test}.test) target_link_libraries (mlib.${test}.test PRIVATE mongo::mc-mlib) endforeach () if ("cxx_relaxed_constexpr" IN_LIST CMAKE_CXX_COMPILE_FEATURES) file (GLOB_RECURSE test_files CONFIGURE_DEPENDS src/*.test.cpp) foreach (file IN LISTS test_files) # Compute a nice test name file (RELATIVE_PATH relpath "${CMAKE_CURRENT_LIST_DIR}/src" "${file}") file (TO_CMAKE_PATH "${relpath}" relpath) string (REPLACE "src/" "" relpath "${relpath}") string (REPLACE "/" "." test_name "${relpath}") string (REGEX REPLACE "\\.test\\.cpp$" "" test_name "${test_name}") # Generate a test executable: set (exe_name "${test_name}.test") add_executable ("${exe_name}" "${file}") target_compile_features ("${exe_name}" PRIVATE cxx_relaxed_constexpr) target_link_libraries ("${exe_name}" PRIVATE mongocrypt mongo::mc-mlib ${maybe_dfp_library} _mongocrypt::libbson_for_static ) add_test ("${test_name}" "${exe_name}") endforeach () endif () endif () # BUILD_TESTING if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES) add_executable (csfle-markup src/csfle-markup.cpp) target_link_libraries (csfle-markup PRIVATE mongocrypt_static _mongocrypt::libbson_for_static mongo::mc-mlib) target_compile_features (csfle-markup PRIVATE cxx_std_20) endif () # Exclude example-state-machine since it requires native crypto. if (NOT MONGOCRYPT_CRYPTO STREQUAL none) # Define example-state-machine add_executable (example-state-machine test/example-state-machine.c) target_link_libraries (example-state-machine PRIVATE mongocrypt _mongocrypt::libbson_for_shared) target_include_directories (example-state-machine PRIVATE ./src "${CMAKE_CURRENT_SOURCE_DIR}/kms-message/src") add_test ( NAME example-state-machine COMMAND example-state-machine WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) # Define example-state-machine-static add_executable (example-state-machine-static test/example-state-machine.c) target_link_libraries (example-state-machine-static PRIVATE mongocrypt_static _mongocrypt::libbson_for_static) target_include_directories (example-state-machine-static PRIVATE ./src) add_test ( NAME example-state-machine-static COMMAND example-state-machine-static WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) if (ENABLE_ONLINE_TESTS) message ("compiling utilities") add_executable (csfle test/util/csfle.c test/util/util.c) target_link_libraries (csfle PRIVATE mongocrypt_static) target_include_directories (csfle PRIVATE ${CMAKE_BINARY_DIR}/src) target_include_directories (csfle PRIVATE ./src) target_include_directories (csfle PRIVATE ./kms-message/src) target_link_libraries (csfle PRIVATE _mongocrypt::mongoc) endif () endif () if (ENABLE_STATIC) set (TARGETS_TO_INSTALL mongocrypt mongocrypt_static) else () set (TARGETS_TO_INSTALL mongocrypt) endif () install ( TARGETS ${TARGETS_TO_INSTALL} EXPORT mongocrypt_targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # This export set is not installed, and is only to allow export() of the mc-mlib-using targets install (TARGETS _mongo-mc-mlib EXPORT _exports_for_export) export (EXPORT _exports_for_export) install ( FILES ${MONGOCRYPT_PUBLIC_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/src/mongocrypt-config.h ${CMAKE_CURRENT_BINARY_DIR}/src/mongocrypt-export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mongocrypt COMPONENT Devel ) set (PROJECT_VERSION "${BUILD_VERSION}") set (PROJECT_DESCRIPTION "The libmongocrypt client-side field level encryption library.") if (NOT ENABLE_BUILD_FOR_PPA) set (PKG_CONFIG_STATIC_REQUIRES "bson2-static") endif () if (USE_SHARED_LIBBSON) set (PKG_CONFIG_REQUIRES "bson2") set (PKG_CONFIG_STATIC_REQUIRES "bson2") endif () set (PKG_CONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") set (PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}/mongocrypt") set (PKG_CONFIG_LIBS "-L\${libdir} -lmongocrypt") if (ENABLE_BUILD_FOR_PPA) set (PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} \${prefix}/${CMAKE_INSTALL_LIBDIR}/libbson-static-for-libmongocrypt.a") #librt needed for libbson on linux for clock_gettime find_library (RT_LIBRARY rt) if (RT_LIBRARY) set (PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} ${RT_LIBRARY}") endif () set (PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} -pthread") endif () if (MONGOCRYPT_DFP_DIR STREQUAL "USE-SYSTEM" AND MONGOCRYPT_ENABLE_DECIMAL128) get_property (SYSTEM_DFP_LOC TARGET intel_dfp PROPERTY IMPORTED_LOCATION) set (PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} ${SYSTEM_DFP_LOC}") endif () set (PKG_CONFIG_CFLAGS "-I\${includedir}") set (PKG_CONFIG_STATIC_CFLAGS "${PKG_CONFIG_CFLAGS} -DMONGOCRYPT_STATIC_DEFINE -DKMS_MSG_STATIC") configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libmongocrypt.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libmongocrypt.pc" ) configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libmongocrypt-static.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libmongocrypt-static.pc" ) install ( FILES "${CMAKE_BINARY_DIR}/libmongocrypt.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) if (ENABLE_STATIC) install ( FILES "${CMAKE_BINARY_DIR}/libmongocrypt-static.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) endif () include (CMakePackageConfigHelpers) set (INCLUDE_INSTALL_DIRS "${CMAKE_INSTALL_INCLUDEDIR}/mongocrypt") set (LIBRARY_INSTALL_DIRS ${CMAKE_INSTALL_LIBDIR}) write_basic_package_version_file ( "${CMAKE_CURRENT_BINARY_DIR}/mongocrypt/mongocrypt-config-version.cmake" COMPATIBILITY AnyNewerVersion ) configure_file (cmake/mongocrypt-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/mongocrypt/mongocrypt-config.cmake" @ONLY ) install (EXPORT mongocrypt_targets NAMESPACE mongo:: FILE mongocrypt_targets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mongocrypt ) install ( FILES "${CMAKE_CURRENT_BINARY_DIR}/mongocrypt/mongocrypt-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/mongocrypt/mongocrypt-config-version.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mongocrypt COMPONENT Devel ) libmongocrypt-1.19.0/CODEOWNERS000066400000000000000000000004241521103432300160570ustar00rootroot00000000000000# Code Owners will automatically be added as reviewers on PRs # Listing code owners is required by DRIVERS-3098 * @mongodb/dbx-c-cxx # Python Bindings bindings/python @mongodb/dbx-python # GitHub actions (currently only used by Python Bindings) .github @mongodb/dbx-python libmongocrypt-1.19.0/CONTRIBUTING.md000066400000000000000000000004201521103432300167110ustar00rootroot00000000000000# Formatting To format, install `pipx` and run: ```bash ./etc/format-all.sh ``` To use a specified python install, set `MONGOCRYPT_PYTHON`: ```bash # Set MONGOCRYPT_PYTHON to a python install with pipx installed. export MONGOCRYPT_PYTHON=python ./etc/format-all.sh ``` libmongocrypt-1.19.0/Earthfile000066400000000000000000000471441521103432300163240ustar00rootroot00000000000000# Earthly Intro: https://docs.earthly.dev/ # Earthfile Reference: https://docs.earthly.dev/docs/earthfile # Quick notes: # • The shell script at ".evergreen/earthly.sh" can be used to automatically # download and use a fixed version of Earthly that is compatible with # this file version. Execute the shell script as if it were the Earthly # executable itself. # • In this file, the convention is to copy the source tree into /s/libmongocrypt # • Earthly copies the "build context" (working directory) into the local buildkit # daemon before each build execution. (Similar to the Docker build context). # If you have a large amount of data in the working directory, this could be # slow. The ".earthlyignore" file specifies patterns of files and directories # to exclude from the context upload. Modify it to suite your needs, if necessary. # • Only a subset of the source tree is COPY'd into the build environment. Files # outside of this set will not be available in the build. See the COPY_SOURCE # command for the list. # • Modification at any layer will invalidate caching on all subsequent build # layers. This is important and by-design in Earthly. Push infrequently-modified # operations to earlier in the process the pipeline to make better use of # the cache. # # This file has a few major sections: # - Setup FUNCTIONs # - Utility FUNCTIONs # - Environment targets # - Build/test/CI targets # # All environment targets begin with "env.". All build targets (should) accept an "env" # parameter that specifies the name of the environment to use for the task. The name # of an environment is specified following the "env." prefix. For example, the # Ubuntu 22.04 environment is named "u22", so its environment target is "env.u22", # and can be used i.e. "earthly +build --env=u22" # # The following environment are defined in this file: # • u24 - Ubuntu 24.04 # • u22 - Ubuntu 22.04 # • c10 - CentOS Stream 10 - Stand-in for RHEL 10 # • c9 - CentOS Stream 9 - Stand-in for RHEL 9 # • alma8 - AlmaLinux 8 - Stand-in for RHEL 8 # • amzn2023 - AmazonLinux 2023 # • deb11 - Debian 11.0 # • deb12 - Debian 12.0 # • deb13 - Debian 13 # • alpine - Alpine Linux 3.23 # # When adding new environments, prefer an unqualified image ID with a version: # • DO NOT: "ubuntu" # • DO NOT: "ubuntu:latest" # • DO NOT: "docker.io/library/ubuntu:22.10" # • DO: "ubuntu:22.10" # Use of an unqualified image ID may enable separate registry in CI and local development. # ### VERSION 0.8 FROM alpine:3.23 WORKDIR /s init: # Special initializing target that sets up the base image and adds the "__install" # script. This scripts abstracts around the underlying package manager interface # to "do the right thing" when you want to install packages. Package names will # still need to be spelled correctly for the respective system. # # Invoke +init with a "--base" parameter that specifies the base image to pull ARG --required base FROM $base COPY etc/install-package.sh /usr/local/bin/__install RUN chmod +x /usr/local/bin/__install ENV USER_CACHES_DIR=/Cache # Environment setup commands below. Each provides the basic environment for a # libmongocrypt build. Additional packages and setup may be required for # individual tasks. DEBIAN_SETUP: # Setup for a debian-like build environment. Used for both Debian and Ubuntu FUNCTION RUN __install build-essential g++ libssl-dev curl unzip python3 pkg-config \ git ccache findutils ca-certificates REDHAT_SETUP: # Setup for a redhat-like build environment. Used for CentOS Stream and AlmaLinux. FUNCTION RUN __install epel-release && \ __install gcc-c++ make openssl-devel curl unzip git ccache findutils \ patch AMZ_SETUP: # Setup for Amazon Linux. FUNCTION RUN __install gcc-c++ make openssl-devel curl unzip tar gzip \ openssh-clients patch git ALPINE_SETUP: # Setup for an Alpine Linux build environment FUNCTION RUN __install make bash gcc g++ unzip curl tar gzip git musl-dev \ linux-headers openssl-dev python3 # Environment targets are defined below. These do not have build outputs, but # are rather themselves the "outputs" to be used as the environment for subsequent # tasks env.c9: # Use CentOS Stream 9 as a stand-in for RHEL 9 FROM +init --base=quay.io/centos/centos:stream9 DO +REDHAT_SETUP # uv is not available via dnf: use pip instead. RUN __install python3.12-pip RUN python3.12 -m pip install --user uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.c10: # Use CentOS Stream 10 as a stand-in for RHEL 10 FROM +init --base=quay.io/centos/centos:stream10 DO +REDHAT_SETUP # uv is available via dnf: install directly. RUN __install uv RUN uv --version && uvx python --version env.alma8: # Use AlmaLinux 8 as a stand-in for RHEL 8 FROM +init --base=almalinux:8 DO +REDHAT_SETUP # uv is not available via dnf: use pip instead. RUN dnf config-manager --set-enabled powertools RUN __install python3.12-pip RUN python3.12 -m pip install --user uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version # Utility command for Ubuntu environments ENV_UBUNTU: FUNCTION ARG --required version FROM +init --base=ubuntu:$version DO +DEBIAN_SETUP env.u22: # An Ubuntu 22.04 environment DO +ENV_UBUNTU --version 22.04 # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.10 RUN python3 -m pip install --user uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.u24: # An Ubuntu 24.04 environment DO +ENV_UBUNTU --version 24.04 # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.12 RUN python3 -m pip install --user --break-system-packages uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.amzn2023: # An Amazon "2023" environment. (AmazonLinux 2023) FROM +init --base=amazonlinux:2023 DO +AMZ_SETUP # uv is not available via dnf: use pip instead. RUN __install python3.13-pip RUN python3.13 -m pip install --user uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version # Utility command for Debian setup ENV_DEBIAN: FUNCTION ARG --required version FROM +init --base=debian:$version DO +DEBIAN_SETUP env.deb-unstable: DO +ENV_DEBIAN --version=unstable # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.13 RUN python3 -m pip install --user --break-system-packages uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.deb11: # A Debian 11.0 environment DO +ENV_DEBIAN --version 11.0 # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.9 RUN python3 -m pip install --user uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.deb12: # A Debian 12.0 environment DO +ENV_DEBIAN --version 12.0 # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.11 RUN python3 -m pip install --user --break-system-packages uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.deb13: # A Debian 13 environment DO +ENV_DEBIAN --version 13.0 # uv is not available via apt: use pip instead. RUN __install python3-pip # Python 3.13 RUN python3 -m pip install --user --break-system-packages uv ENV PATH=/root/.local/bin:$PATH RUN uv --version && uvx python --version env.alpine: FROM +init --base=alpine:3.23 DO +ALPINE_SETUP # uv is available via apk: install directly. RUN __install uv RUN uv --version && uvx python --version # Utility: Warm-up obtaining CMake and Ninja for the build. This is usually # very quick, but on some platforms we need to compile them from source. CACHE_WARMUP: FUNCTION # Copy only the scripts that are strictly necessary for the operation, to # avoid cache invalidation later on. COPY .evergreen/setup-env.sh \ .evergreen/init.sh \ .evergreen/install-build-tools.sh \ /T/ RUN bash -c '. /T/install-build-tools.sh && UV_TOOL_DIR=/T/uv-tools UV_TOOL_BIN_DIR=/T/uv-bin install_build_tools' ENV PATH="/T/uv-bin:$PATH" ENV CMAKE_GENERATOR="Ninja" COPY_SOURCE: FUNCTION COPY --dir \ .git/ \ cmake/ \ kms-message/ \ test/ \ src/ \ doc/ \ etc/ \ LICENSE \ .evergreen/ \ third-party/ \ CMakeLists.txt \ "/s/libmongocrypt" COPY --dir bindings/cs/ "/s/libmongocrypt/bindings/" BUILD_EXAMPLE_STATE_MACHINE: FUNCTION COPY test/example-state-machine.c /s/ RUN pkg-config --exists libmongocrypt --print-errors && \ gcc /s/example-state-machine.c \ -o /s/example-state-machine \ $(pkg-config --cflags --libs libmongocrypt) COPY --dir test/example /s/test/example RUN cd /s && /s/example-state-machine rpm-build: FROM +init --base fedora:45 GIT CLONE https://src.fedoraproject.org/rpms/libmongocrypt.git /R # Install the packages listed by "BuildRequires" and rpm-build: RUN __install /R/libmongocrypt.spec RUN __install rpm-build DO +COPY_SOURCE RUN cp -r /s/libmongocrypt/. /R RUN awk -f /R/etc/rpm/tweak.awk < /R/libmongocrypt.spec > /R/libmongocrypt.2.spec RUN rpmbuild -ba /R/libmongocrypt.2.spec \ -D "_topdir /X" \ -D "_sourcedir /R" SAVE ARTIFACT /X/RPMS / SAVE ARTIFACT /X/SRPMS / rpm-install-runtime: # Install the runtime RPM FROM +init --base fedora:45 COPY +rpm-build/RPMS /tmp/libmongocrypt-rpm/ RUN dnf makecache RUN __install $(find /tmp/libmongocrypt-rpm/ -name 'libmongocrypt-1.*.rpm') rpm-install-dev: # Install the development RPM FROM +rpm-install-runtime COPY +rpm-build/RPMS /tmp/libmongocrypt-rpm/ RUN dnf makecache RUN __install $(find /tmp/libmongocrypt-rpm/ -name 'libmongocrypt-devel-*.rpm') rpm-devel-test: # Attempt to build a small app using pkg-config and the dev RPM FROM +rpm-install-dev RUN __install gcc DO +BUILD_EXAMPLE_STATE_MACHINE SAVE ARTIFACT /s/example-state-machine / rpm-runtime-test: # Attempt to run a libmongocrypt-using app with the runtime RPM installed FROM +rpm-install-runtime COPY +rpm-devel-test/example-state-machine /s/ COPY --dir test/example /s/test/example RUN cd /s/ && /s/example-state-machine # A target to build the debian package. Options: # • --env=[...] (default: deb-unstable) # · Set the environment for the build. Affects which packages are available # for build dependencies. # NOTE: Uncommited local changes will be ignored and not affect the result! deb-build: ARG env=deb-unstable FROM +env.$env RUN __install git-buildpackage fakeroot debhelper cmake libbson-dev \ libintelrdfpmath-dev DO +COPY_SOURCE # Bring in the debian/ directory from the debian/unstable branch GIT CLONE https://github.com/mongodb/libmongocrypt --branch debian/unstable libmongocrypt-debian WORKDIR /s/libmongocrypt RUN git clean -fdx && git reset --hard RUN python3 etc/calc_release_version.py > VERSION_CURRENT RUN git add -f VERSION_CURRENT && \ git -c user.name=anon -c user.email=anon@localhost \ commit VERSION_CURRENT -m 'Set version' && \ env LANG=C bash libmongocrypt-debian/debian/build_snapshot.sh && \ debc ../*.changes && \ dpkg -i ../*.deb SAVE ARTIFACT /s/*.deb /debs/ deb-install-runtime: # Install the runtime deb package FROM +init --base=debian:unstable COPY +deb-build/debs/libmongocrypt0*.deb /tmp/lmc.deb RUN __install /tmp/lmc.deb deb-install-dev: # Install the development deb package FROM +deb-install-runtime COPY +deb-build/debs/libmongocrypt-dev*.deb /tmp/lmc-dev.deb RUN __install /tmp/lmc-dev.deb deb-dev-test: # Attempt to build a small app using pkg-config and the dev deb package FROM +deb-install-dev RUN __install pkg-config gcc DO +BUILD_EXAMPLE_STATE_MACHINE SAVE ARTIFACT /s/example-state-machine / deb-runtime-test: # Attempt to run a libmongocrypt-using app with the runtime DEB installed FROM +deb-install-runtime COPY +deb-dev-test/example-state-machine /s/ COPY --dir test/example /s/test/example RUN cd /s/ && /s/example-state-machine packaging-full-test: BUILD +deb-runtime-test BUILD +rpm-runtime-test check-format: FROM +init --base=python:3.13.5-slim-bookworm # uv is available via pip: install directly. RUN pip install uv COPY etc/format* /X/etc/ COPY .evergreen/init.sh /X/.evergreen/ RUN /X/etc/format.sh # Does nothing, but warms the cache COPY --dir .clang-format src test /X/ RUN /X/etc/format-all.sh --dry-run -Werror --verbose # The main "build" target. Options: # • --env=[...] (default "u22") # · Set the environment for the build. Any name of and "env." targets # can be used. # • --persist_build={true,false} (default "true") # · Persist the build directory between executions. Enables incremental # compilation and reusing of configuration between builds. The build # directory is NOT shared between different "--env" environments, only # within a single environment. build: ARG env=u22 FROM +env.$env DO +CACHE_WARMUP DO +COPY_SOURCE WORKDIR /s ARG persist_build=true IF $persist_build CACHE /s/libmongocrypt/cmake-build END RUN bash libmongocrypt/.evergreen/build_all.sh SAVE ARTIFACT /s/install /libmongocrypt-install # `create-deb-packages-and-repos` creates the .deb packages and repo directories intended for the PPA on debian-like distros. Options: # • --env=[...] # · Set the environment for the build. Only debian-like environments are supported. # • --packager_distro=[...] is passed to `create-packages-and-repos.sh` # • --packager_arch=[...] is passed to `create-packages-and-repos.sh` create-deb-packages-and-repos: ARG env FROM +env.$env DO +CACHE_WARMUP DO +COPY_SOURCE WORKDIR /s RUN __install dh-make dpkg-dev apt-utils ARG packager_distro ARG packager_arch RUN env \ WORKDIR=/s \ PYTHON=python3 \ PACKAGER_DISTRO=$packager_distro \ PACKAGER_ARCH=$packager_arch \ bash libmongocrypt/.evergreen/create-packages-and-repos.sh SAVE ARTIFACT libmongocrypt/repo AS LOCAL repo # `test-deb-packages-from-ppa` tests Debian and Ubuntu packages installed on the PPA. # • --env=[...] (default "deb11") # · Set the environment for the build. Only debian-like environments are supported. # • --distro=[...] (default "debian bullseye") # · Has the form: "(debian|ubuntu) (name)". Must match distro set for `--env`. # • --version=[...] (default "development"). The libmongocrypt package version. # · May refer to a release branch (e.g. "1.8"). Release branch packages are updated on a tagged release. # · "development" packages are updated by the `publish-packages` tasks every commit. test-deb-packages-from-ppa: ARG env="deb11" ARG distro="debian bullseye" ARG version="development" FROM +env.$env WORKDIR /s RUN __install apt-transport-https # Install libmongocrypt following install steps described in README.md: RUN curl -s --location https://pgp.mongodb.com/libmongocrypt.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/libmongocrypt.gpg RUN echo "deb https://libmongocrypt.s3.amazonaws.com/apt/$distro/libmongocrypt/$version main" | tee /etc/apt/sources.list.d/libmongocrypt.list # Test using libmongocrypt: RUN __install libmongocrypt-dev gcc RUN echo " #include #include int main(void) { const char *ver = mongocrypt_version(NULL); printf (\"Using libmongocrypt %s\", ver); }" > test.c RUN gcc -o test.out test.c $(pkg-config --libs --cflags libmongocrypt) RUN ./test.out # `sign` uses Garasign to sign a file with the libmongocrypt key. # Requires prior authentication with Artifactory. # See: https://docs.devprod.prod.corp.mongodb.com/release-tools-container-images/garasign/garasign_signing/. sign: ARG --required file_to_sign ARG --required output_file FROM artifactory.corp.mongodb.com/release-tools-container-registry-local/garasign-gpg WORKDIR /s COPY ${file_to_sign} /s/file RUN --secret garasign_username --secret garasign_password \ GRS_CONFIG_USER1_USERNAME=${garasign_username} \ GRS_CONFIG_USER1_PASSWORD=${garasign_password} \ /bin/bash -c "gpgloader && gpg --yes -v --armor -o /s/file.asc --detach-sign /s/file" # Verify the signature RUN touch /keyring RUN curl -sS https://pgp.mongodb.com/libmongocrypt.pub | gpg -q --no-default-keyring --keyring "/keyring" --import - RUN gpgv --keyring "/keyring" "/s/file.asc" "/s/file" SAVE ARTIFACT /s/file.asc AS LOCAL ${output_file} # silkbomb: # An environment with the `silkbomb` command. # # See https://docs.devprod.prod.corp.mongodb.com/mms/python/src/sbom/silkbomb/ for documentation of silkbomb. silkbomb: FROM artifactory.corp.mongodb.com/release-tools-container-registry-public-local/silkbomb:2.0 # Alias the silkbomb executable to a simpler name: RUN ln -s /python/src/sbom/silkbomb/bin /usr/local/bin/silkbomb # sbom-generate: # Generate/update the etc/cyclonedx.sbom.json file from the etc/purls.txt file. # # This target will update the existing etc/cyclonedx.sbom.json file in-place based # on the content of etc/purls.txt and etc/cyclonedx.sbom.json. sbom-generate: FROM +silkbomb # Copy in the relevant files: WORKDIR /s COPY etc/purls.txt etc/cyclonedx.sbom.json /s/ # Update the SBOM file: RUN silkbomb update \ --refresh \ --no-update-sbom-version \ --purls purls.txt \ --sbom-in cyclonedx.sbom.json \ --sbom-out cyclonedx.sbom.json # Save the result back to the host: SAVE ARTIFACT /s/cyclonedx.sbom.json AS LOCAL etc/cyclonedx.sbom.json # sbom-generate-new-serial-number: # Equivalent to +sbom-generate but includes the --generate-new-serial-number # flag to generate a new unique serial number and reset the SBOM version to 1. # # This target will update the existing etc/cyclonedx.sbom.json file in-place based # on the content of etc/purls.txt and etc/cyclonedx.sbom.json. sbom-generate-new-serial-number: FROM +silkbomb # Copy in the relevant files: WORKDIR /s COPY etc/purls.txt etc/cyclonedx.sbom.json /s/ # Update the SBOM file: RUN silkbomb update \ --refresh \ --generate-new-serial-number \ --purls purls.txt \ --sbom-in cyclonedx.sbom.json \ --sbom-out cyclonedx.sbom.json # Save the result back to the host: SAVE ARTIFACT /s/cyclonedx.sbom.json AS LOCAL etc/cyclonedx.sbom.json # sbom-validate: # Validate the SBOM Lite for the given branch. sbom-validate: FROM +silkbomb # Copy in the relevant files: WORKDIR /s COPY etc/purls.txt etc/cyclonedx.sbom.json /s/ # Run the SilkBomb tool to download the artifact that matches the requested branch RUN silkbomb validate \ --purls purls.txt \ --sbom-in cyclonedx.sbom.json \ --exclude jira libmongocrypt-1.19.0/LICENSE000066400000000000000000000261341521103432300154770ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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.libmongocrypt-1.19.0/README.md000066400000000000000000000212361521103432300157470ustar00rootroot00000000000000# libmongocrypt # The companion C library for client side encryption in drivers. This project uses [Semantic Versioning](https://semver.org/). # Bugs / Feature Requests # If you have encountered a bug, or would like to see a new feature in libmongocrypt, please open a case in our issue management tool, JIRA: - [Create an account and login](https://jira.mongodb.org). - Navigate to [the MONGOCRYPT project](https://jira.mongodb.org/projects/MONGOCRYPT). - Click **Create Issue** - Please provide as much information as possible about the issue type and how to reproduce it. ## Documentation ## See [The Integration Guide](integrating.md) to integrate with your driver. See [mongocrypt.h](src/mongocrypt.h) for the public API reference. ## Building libmongocrypt ## On Windows and macOS, libmongocrypt can use the platform's default encryption APIs as its encryption backend. On other systems, one will want to install the OpenSSL development libraries, which libmongocrypt will use as the default encryption backend. Then build libmongocrypt: ``` git clone https://github.com/mongodb/libmongocrypt cd libmongocrypt mkdir cmake-build && cd cmake-build cmake ../ make ``` This builds libmongocrypt.dylib and test-libmongocrypt, in the cmake-build directory. ## Installing libmongocrypt on macOS ## Install the latest release of libmongocrypt with the following. ``` brew install mongodb/brew/libmongocrypt ``` To install the latest unstable development version of libmongocrypt, use `brew install mongodb/brew/libmongocrypt --HEAD`. Do not use the unstable version of libmongocrypt in a production environment. ## Building libmongocrypt from source on macOS ## First install [Homebrew according to its own instructions](https://brew.sh/). Install the XCode Command Line Tools: ``` xcode-select --install ``` Then clone and build libmongocrypt: ``` git clone https://github.com/mongodb/libmongocrypt.git cd libmongocrypt cmake . cmake --build . --target install ``` Then, libmongocrypt can be used with pkg-config: ``` pkg-config libmongocrypt --libs --cflags ``` Or use cmake's `find_package`: ``` find_package (mongocrypt) # Then link against mongo::mongocrypt ``` ## Installing libmongocrypt on Windows ## A Windows DLL for x86_64 is available on the Github Releases page. See the [latest release](https://github.com/mongodb/libmongocrypt/releases/latest). Use `gpg` to verify the signature. The public key for `libmongocrypt` is available on https://pgp.mongodb.com/. ### Testing ### `test-mongocrypt` mocks all I/O with files stored in the `test/data` and `test/example` directories. Run `test-mongocrypt` from the source directory: ``` cd libmongocrypt ./cmake-build/test-mongocrypt ``` libmongocrypt is continuously built and published on evergreen. Submit patch builds to this evergreen project when making changes to test on supported platforms. The latest tarball containing libmongocrypt built on all supported variants is [published here](https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz). ### Troubleshooting ### If OpenSSL is installed in a non-default directory, pass `-DOPENSSL_ROOT_DIR=/path/to/openssl` to the cmake command for libmongocrypt. If there are errors with cmake configuration, send the set of steps you performed to the maintainers of this project. If there are compilation or linker errors, run `make` again, setting `VERBOSE=1` in the environment or on the command line (which shows exact compile and link commands), and send the output to the maintainers of this project. ### Releasing ### See [releasing](./doc/releasing.md). ## Installing libmongocrypt From Distribution Packages ## Distribution packages (i.e., .deb/.rpm) are built and published for several Linux distributions. The installation of these packages for supported platforms is documented here. ### Package Publication Channels ### The libmongocrypt project publishes packages in three different channels: `release`, `testing`, and `development`. The channel descriptions are: - `release`: packages representing final releases, having version numbers like `1.17.2`, `1.18.0`, etc. - `testing`: packages representing pre-releases (e.g., alpha and beta versions); this channel is currently dormant - `development`: packages created from each build which passes CI, having version numbers like `1.17.3~+git`; these packages are not considered suitable for production use In the below sections, replace the placeholder `` with the value that best matches your particular needs. ### .deb Packages (Debian and Ubuntu) ### The repository containing the Debian and Ubuntu .deb packages can be configured automatically, using extrepo, or manually. Once the repository is configured then the packages can be installed. #### Repository configuration with extrepo #### Extrepo is available on Debian 11 and newer, as well as Ubuntu 22.04 and newer. First, install the extrepo package: ``` sudo apt install extrepo ``` If you would like to see the information about the repository, it can be viewed with the search command: ``` extrepo search libmongocrypt ``` In order to enable the repository, execute this command: ``` sudo extrepo enable libmongocrypt-release ``` Once the repository is configured, continue with package installation. #### Manual repository configuration #### First, import the public key used to sign the package repositories: ``` sudo sh -c 'curl -s --location https://pgp.mongodb.com/libmongocrypt.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/libmongocrypt.gpg' ``` Second, create a list entry for the repository. For Ubuntu systems (be sure to change `` to `jammy` or `noble` as appropriate to your system): ``` echo "deb https://libmongocrypt.s3.amazonaws.com/apt/ubuntu /libmongocrypt/ universe" | sudo tee /etc/apt/sources.list.d/libmongocrypt.list ``` For Debian systems (be sure to change `` to `bullseye`, `bookworm`, or `trixie` as appropriate to your system): ``` echo "deb https://libmongocrypt.s3.amazonaws.com/apt/debian /libmongocrypt/ main" | sudo tee /etc/apt/sources.list.d/libmongocrypt.list ``` #### Package installation #### Finally, update the package cache and install the libmongocrypt packages: ``` sudo apt-get update sudo apt-get install -y libmongocrypt-dev ``` ### .rpm Packages (RedHat, Suse, and Amazon) ### RPMs are available for supported systems running on both x86_64 and AArch64 (also called ARM64) processors. The sections below use `x86_64` in the example repository URLs. Substituting `aarch64` in the place of `x86_64` will permit installation of libmongocrypt packages on systems running on AArch64 processors. #### RedHat Enterprise Linux #### Create the file `/etc/yum.repos.d/libmongocrypt.repo` with contents: ``` [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/redhat/$releasever/libmongocrypt//x86_64 gpgcheck=1 enabled=1 gpgkey=https://pgp.mongodb.com/libmongocrypt.asc ``` Then install the libmongocrypt packages: ``` sudo yum install -y libmongocrypt ``` #### Amazon Linux 2023 #### Create the file `/etc/yum.repos.d/libmongocrypt.repo` with contents: ``` [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/amazon/2023/libmongocrypt//x86_64 gpgcheck=1 enabled=1 gpgkey=https://pgp.mongodb.com/libmongocrypt.asc ``` Then install the libmongocrypt packages: ``` sudo yum install -y libmongocrypt ``` #### Amazon Linux 2 #### Create the file `/etc/yum.repos.d/libmongocrypt.repo` with contents: ``` [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/amazon/2/libmongocrypt//x86_64 gpgcheck=1 enabled=1 gpgkey=https://pgp.mongodb.com/libmongocrypt.asc ``` Then install the libmongocrypt packages: ``` sudo yum install -y libmongocrypt ``` #### Amazon Linux #### Create the file `/etc/yum.repos.d/libmongocrypt.repo` with contents: ``` [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/amazon/2013.03/libmongocrypt//x86_64 gpgcheck=1 enabled=1 gpgkey=https://pgp.mongodb.com/libmongocrypt.asc ``` Then install the libmongocrypt packages: ``` sudo yum install -y libmongocrypt ``` #### Suse #### First, import the public key used to sign the package repositories: ``` sudo rpm --import https://pgp.mongodb.com/libmongocrypt.asc ``` Second, add the repository (be sure to change `` to `12` or `15`, as appropriate to your system): ``` sudo zypper addrepo --gpgcheck "https://libmongocrypt.s3.amazonaws.com/zypper/suse//libmongocrypt//x86_64" libmongocrypt ``` Finally, install the libmongocrypt packages: ``` sudo zypper -n install libmongocrypt ``` libmongocrypt-1.19.0/bindings/000077500000000000000000000000001521103432300162615ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/cs/000077500000000000000000000000001521103432300166665ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/cs/README.md000066400000000000000000000004711521103432300201470ustar00rootroot00000000000000### The C# bindings have been moved! #### https://github.com/mongodb/mongo-csharp-driver/tree/main/src/MongoDB.Driver.Encryption They have been renamed from ```MongoDB.Libmongocrypt``` to ```MongoDB.Driver.Encryption``` and can be found on nuget here: https://www.nuget.org/packages/MongoDB.Driver.Encryption/ libmongocrypt-1.19.0/bindings/node/000077500000000000000000000000001521103432300172065ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/node/README.md000066400000000000000000000003201521103432300204600ustar00rootroot00000000000000### The Node.js Bindings Have Moved! #### https://github.com/mongodb-js/mongodb-client-encryption They can still be found at the same npm package: - https://www.npmjs.com/package/mongodb-client-encryption libmongocrypt-1.19.0/bindings/python/000077500000000000000000000000001521103432300176025ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/.evergreen/000077500000000000000000000000001521103432300216425ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/.evergreen/integ-setup.sh000077500000000000000000000026071521103432300244520ustar00rootroot00000000000000#!/usr/bin/bash set -eux DRIVERS_TOOLS="$(pwd)/drivers-tools" PROJECT_DIRECTORY="${project_directory}" PYMONGO_DIR="$(pwd)/mongo-python-driver" # Python has cygwin path problems on Windows. if [ "Windows_NT" = "${OS:-}" ]; then DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS) PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY) fi export PROJECT_DIRECTORY export DRIVERS_TOOLS export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration" export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" export MONGOCRYPT_DIR=${PROJECT_DIRECTORY}/all/${variant_name} cat < expansion.yml DRIVERS_TOOLS: "$DRIVERS_TOOLS" MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME" MONGODB_BINARIES: "$MONGODB_BINARIES" PROJECT_DIRECTORY: "$PROJECT_DIRECTORY" PYMONGO_DIR: "$PYMONGO_DIR" MONGOCRYPT_DIR: "$MONGOCRYPT_DIR" EOT # Set up drivers-tools with a .env file. git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git ${DRIVERS_TOOLS} cat < ${DRIVERS_TOOLS}/.env DRIVERS_TOOLS="$DRIVERS_TOOLS" MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME" MONGODB_BINARIES="$MONGODB_BINARIES" PROJECT_DIRECTORY="$PROJECT_DIRECTORY" EOT # Get the secrets bash $DRIVERS_TOOLS/.evergreen/csfle/setup-secrets.sh # Start the csfle servers bash $DRIVERS_TOOLS/.evergreen/csfle/start-servers.sh # Clone mongo-python-driver git clone https://github.com/mongodb/mongo-python-driver.git ${PYMONGO_DIR} libmongocrypt-1.19.0/bindings/python/.evergreen/integ-teardown.sh000077500000000000000000000003541521103432300251320ustar00rootroot00000000000000#! /bin/bash set -o xtrace # Write all commands first to stderr set -o errexit # Exit the script with error if any of the commands fail bash ${DRIVERS_TOOLS}/.evergreen/csfle/teardown.sh bash ${DRIVERS_TOOLS}/.evergreen/teardown.sh libmongocrypt-1.19.0/bindings/python/.evergreen/integ-test.sh000077500000000000000000000027221521103432300242670ustar00rootroot00000000000000#! /bin/bash set -eux pushd $(pwd)/libmongocrypt/bindings/python # For createvirtualenv and find_python3 . .evergreen/utils.sh BASE_PYTHON=$(find_python3) # MONGOCRYPT_DIR is set by libmongocrypt/.evergreen/config.yml MONGOCRYPT_DIR="$MONGOCRYPT_DIR" CRYPT_SHARED_DIR="$DRIVERS_TOOLS" MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" MACHINE=$(uname -m) if [ $MACHINE == "aarch64" ]; then PYTHON="/opt/mongodbtoolchain/v4/bin/python3" else PYTHON="/opt/python/3.13/bin/python3" fi if [ -d "${MONGOCRYPT_DIR}/nocrypto/lib64" ]; then PYMONGOCRYPT_LIB="${MONGOCRYPT_DIR}/nocrypto/lib64/libmongocrypt.so" else PYMONGOCRYPT_LIB="${MONGOCRYPT_DIR}/nocrypto/lib/libmongocrypt.so" fi export PYMONGOCRYPT_LIB createvirtualenv $PYTHON .venv pip install -e . pip install uv pushd $PYMONGO_DIR pip install -e ".[test,encryption]" source ${DRIVERS_TOOLS}/.evergreen/csfle/secrets-export.sh set -x export DB_USER="bob" export DB_PASSWORD="pwd123" export CLIENT_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/client.pem" export CA_PEM="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem" export DYLD_FALLBACK_LIBRARY_PATH=$CRYPT_SHARED_DIR:${DYLD_FALLBACK_LIBRARY_PATH:-} export LD_LIBRARY_PATH=$CRYPT_SHARED_DIR:${LD_LIBRARY_PATH-} export PATH=$CRYPT_SHARED_DIR:$MONGODB_BINARIES:$PATH export TEST_CRYPT_SHARED="1" pytest --maxfail=10 -v -m encryption # Now test with stable pymongo. pip uninstall -y pymongo pip install pymongo pytest --maxfail=10 -v -m encryption popd deactivate rm -rf .venv popd libmongocrypt-1.19.0/bindings/python/.evergreen/test.sh000077500000000000000000000106211521103432300231600ustar00rootroot00000000000000#!/bin/bash # Test the Python bindings for libmongocrypt set -o xtrace # Write all commands first to stderr set -o errexit # Exit the script with error if any of the commands fail # For createvirtualenv and find_python3 . .evergreen/utils.sh BASE_PYTHON=$(find_python3) # MONGOCRYPT_DIR is set by libmongocrypt/.evergreen/config.yml MONGOCRYPT_DIR="$MONGOCRYPT_DIR" git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/bin/mongocrypt.dll PYMONGOCRYPT_LIB_CRYPTO=$(cygpath -m ${MONGOCRYPT_DIR}/bin/mongocrypt.dll) export PYMONGOCRYPT_LIB=$(cygpath -m $PYMONGOCRYPT_LIB) PYTHONS=("C:/python/Python39/python.exe" "C:/python/Python310/python.exe" "C:/python/Python311/python.exe" "C:/python/Python312/python.exe" "C:/python/Python313/python.exe" "C:/python/Python314/python.exe") export CRYPT_SHARED_PATH=../crypt_shared/bin/mongo_crypt_v1.dll C:/python/Python310/python.exe drivers-evergreen-tools/.evergreen/mongodl.py --component crypt_shared \ --version latest --out ../crypt_shared/ elif [ "Darwin" = "$(uname -s)" ]; then export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib/libmongocrypt.dylib PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib/libmongocrypt.dylib PYTHONS=( "/Library/Frameworks/Python.framework/Versions/3.9/bin/python3" "/Library/Frameworks/Python.framework/Versions/3.10/bin/python3" "/Library/Frameworks/Python.framework/Versions/3.11/bin/python3" "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3" "/Library/Frameworks/Python.framework/Versions/3.13/bin/python3" "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3" ) export CRYPT_SHARED_PATH="../crypt_shared/lib/mongo_crypt_v1.dylib" python3 drivers-evergreen-tools/.evergreen/mongodl.py --component crypt_shared \ --version latest --out ../crypt_shared/ else if [ -e "${MONGOCRYPT_DIR}/lib64/" ]; then export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib64/libmongocrypt.so PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib64/libmongocrypt.so else export PYMONGOCRYPT_LIB=${MONGOCRYPT_DIR}/nocrypto/lib/libmongocrypt.so PYMONGOCRYPT_LIB_CRYPTO=${MONGOCRYPT_DIR}/lib/libmongocrypt.so fi export CRYPT_SHARED_PATH="../crypt_shared/lib/mongo_crypt_v1.so" MACHINE=$(uname -m) if [ $MACHINE == "aarch64" ]; then PYTHONS=("/opt/mongodbtoolchain/v3/bin/python3" "/opt/mongodbtoolchain/v4/bin/python3" ) else PYTHONS=( "/opt/python/3.9/bin/python3" "/opt/python/3.10/bin/python3" "/opt/python/3.11/bin/python3" "/opt/python/3.12/bin/python3" "/opt/python/3.13/bin/python3" "/opt/python/3.14/bin/python3" ) fi /opt/mongodbtoolchain/v3/bin/python3 drivers-evergreen-tools/.evergreen/mongodl.py --component \ crypt_shared --version latest --out ../crypt_shared/ fi for PYTHON_BINARY in "${PYTHONS[@]}"; do echo "Running test with python: $PYTHON_BINARY" $PYTHON_BINARY -c 'import sys; print(sys.version)' git clean -dffx createvirtualenv $PYTHON_BINARY .venv python -m pip install --prefer-binary -v -e ".[test]" || python -m pip install --pre --prefer-binary -v -e ".[test]" echo "Running tests with crypto enabled libmongocrypt..." PYMONGOCRYPT_LIB=$PYMONGOCRYPT_LIB_CRYPTO python -c 'from pymongocrypt.binding import lib;assert lib.mongocrypt_is_crypto_available(), "mongocrypt_is_crypto_available() returned False"' PYMONGOCRYPT_LIB=$PYMONGOCRYPT_LIB_CRYPTO python -m pytest -v --ignore=test/performance . echo "Running tests with crypt_shared on dynamic library path..." TEST_CRYPT_SHARED=1 DYLD_FALLBACK_LIBRARY_PATH=../crypt_shared/lib/:$DYLD_FALLBACK_LIBRARY_PATH \ LD_LIBRARY_PATH=../crypt_shared/lib:$LD_LIBRARY_PATH \ PATH=../crypt_shared/bin:$PATH \ python -m pytest -v --ignore=test/performance . deactivate rm -rf .venv done # Verify the sbom file LIBMONGOCRYPT_VERSION=$(cat ./scripts/libmongocrypt-version.txt) EXPECTED="pkg:github/mongodb/libmongocrypt@$LIBMONGOCRYPT_VERSION" if grep -q $EXPECTED sbom.json; then echo "SBOM is up to date!" else echo "SBOM is out of date! Run the \"update-sbom.sh\" script." exit 1 fi libmongocrypt-1.19.0/bindings/python/.evergreen/utils.sh000077500000000000000000000050761521103432300233510ustar00rootroot00000000000000#!/bin/bash -ex # Usage: # createvirtualenv /path/to/python /output/path/for/venv # * param1: Python binary to use for the virtualenv # * param2: Path to the virtualenv to create createvirtualenv () { PYTHON=$1 VENVPATH=$2 # Prefer venv VENV="$PYTHON -m venv" if [ "$(uname -s)" = "Darwin" ]; then VIRTUALENV="$PYTHON -m virtualenv" else VIRTUALENV=$(command -v virtualenv 2>/dev/null || echo "$PYTHON -m virtualenv") VIRTUALENV="$VIRTUALENV -p $PYTHON" fi if ! $VENV $VENVPATH 2>/dev/null; then # Workaround for bug in older versions of virtualenv. $VIRTUALENV $VENVPATH 2>/dev/null || $VIRTUALENV $VENVPATH fi if [ "Windows_NT" = "${OS:-}" ]; then # Workaround https://bugs.python.org/issue32451: # mongovenv/Scripts/activate: line 3: $'\r': command not found dos2unix $VENVPATH/Scripts/activate || true . $VENVPATH/Scripts/activate else . $VENVPATH/bin/activate fi export PIP_QUIET=1 python -m pip install --upgrade pip } # Usage: # PYTHON = find_python3 find_python3() { PYTHON="" # Add a fallback system python3 if it is available and Python 3.9+. if is_python_39 "$(command -v python3)"; then PYTHON="$(command -v python3)" fi # Find a suitable toolchain version, if available. if [ "$(uname -s)" = "Darwin" ]; then PYTHON="/Library/Frameworks/Python.Framework/Versions/3.9/bin/python3" elif [ "Windows_NT" = "${OS:-}" ]; then # Magic variable in cygwin PYTHON="C:/python/Python39/python.exe" else # Prefer our own toolchain, fall back to mongodb toolchain if it has Python 3.9+. if [ -f "/opt/python/3.9/bin/python3" ]; then PYTHON="/opt/python/3.9/bin/python3" elif is_python_39 "$(command -v /opt/mongodbtoolchain/v4/bin/python3)"; then PYTHON="/opt/mongodbtoolchain/v4/bin/python3" elif is_python_39 "$(command -v /opt/mongodbtoolchain/v3/bin/python3)"; then PYTHON="/opt/mongodbtoolchain/v3/bin/python3" fi fi if [ -z "$PYTHON" ]; then echo "Cannot run pre-commit without python3.9+ installed!" exit 1 fi echo "$PYTHON" } # Function that returns success if the provided Python binary is version 3.9 or later # Usage: # is_python_39 /path/to/python # * param1: Python binary is_python_39() { if [ -z "$1" ]; then return 1 elif $1 -c "import sys; exit(sys.version_info[:2] < (3, 9))"; then # runs when sys.version_info[:2] >= (3, 9) return 0 else return 1 fi } libmongocrypt-1.19.0/bindings/python/.gitignore000066400000000000000000000001501521103432300215660ustar00rootroot00000000000000*~ *#* .DS* *.cm *.class *.pyc *.pyd build/ doc/_build/ dist/ *.so *.egg .tox .eggs/ .idea/ *.egg-info/ libmongocrypt-1.19.0/bindings/python/CHANGELOG.rst000066400000000000000000000210111521103432300216160ustar00rootroot00000000000000Changelog ========= Changes in Version 1.18.1 ------------------------- - Bundle libmongocrypt 1.18.2 in release wheels. Changes in Version 1.18.0 ------------------------- - Bundle libmongocrypt 1.18.1 in release wheels. Changes in Version 1.17.0 ------------------------- - Bundle libmongocrypt 1.17.0 in release wheels. Changes in Version 1.16.0 ------------------------- - Add support for text-based Queryable Encryption with the new "textPreview" algorithm. NOTE: The "textPreview" algorithm is experimental only. It is not intended for public use. Changes in Version 1.15.1 ------------------------- - Bundle libmongocrypt 1.15.1 in release wheels. Changes in Version 1.15.0 ------------------------- - Add support for Python 3.14. - Drop support for Python 3.8. - Bundle libmongocrypt 1.15.0 in release wheels. Changes in Version 1.14.1 ------------------------- - Bundle libmongocrypt 1.14.1 in release wheels. Changes in Version 1.14.0 ------------------------- - Bundle libmongocrypt 1.14.0 in release wheels. Changes in Version 1.13.0 ------------------------- - Bundle libmongocrypt 1.13.1 in release wheels. - Add support for the ``key_expiration_ms`` option to ``MongoCryptOptions``. - Add support for ``$lookup`` in CSFLE and QE. Changes in Version 1.12.0 ------------------------- - Bundle libmongocrypt 1.12.0 in release wheels. - Add support for kms retries. Changes in Version 1.11.0 ------------------------- - Bundle libmongocrypt 1.11.0 in release wheels. - Add support for Python 3.13. - Fix bug in Python async support for ``AsyncMongoCryptCallback.fetch_keys``. Changes in Version 1.10.1 ------------------------- - Bundle libmongocrypt 1.10.1 in release wheels. Changes in Version 1.10.0 ------------------------ - Add Python async support. - Drop support for Python 3.7 and PyPy 3.8. Python >=3.8 or PyPy >=3.9 is now required. - Add support for range-based Queryable Encryption with the new "range" algorithm on MongoDB 8.0+. This replaces the experimental "rangePreview" algorithm. - Add Secure Software Development Life Cycle automation to release process. GitHub Releases for pymongocrypt now include a Software Bill of Materials, and signature files corresponding to the distribution files released on PyPI. Changes in Version 1.9.2 ------------------------ - Fix support for building source distributions with setuptools >= 70. Changes in Version 1.9.1 ------------------------ - Fix bug in our release process which blocked uploading 1.9.0. Changes in Version 1.9.0 ------------------------ - Add support for named KMS providers like "local:name1". This feature requires libmongocrypt >= 1.9.0. - Use libmongocrypt native crypto when available which results in 10-50x better performance. On Linux, it is recommended to download the platform specific build and set PYMONGOCRYPT_LIB to the crypto-enabled libmongocrypt.so. - Bundle the crypto-enabled libmongocrypt builds in macOS and Windows wheels for better performance. - Bundle libmongocrypt 1.9.0 in release wheels. Changes in Version 1.8.0 ------------------------ - Update from manylinux2010 to manylinux2014 wheels. - Bundle libmongocrypt 1.8.4 in release wheels. - Add support for manylinux_2_28_aarch64 wheels. Changes in Version 1.7.0 ------------------------ - Add support for Python 3.12 on MacOS and Linux. - Update required cryptography version to >=2.5. Changes in Version 1.6.1 ------------------------ - Bundle libmongocrypt 1.8.1 in release wheels. Changes in Version 1.6.0 ------------------------ - Drop support for Python 2 and Python <3.7. Python >=3.7 is now required. - Bundle libmongocrypt 1.8.0 in release wheels. - **Remove support for libmongocrypt <=1.8.0, libmongocrypt >=1.8.0 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.5.2 ------------------------ - Bundle libmongocrypt 1.7.3 in release wheels. Changes in Version 1.5.1 ------------------------ - Bundle libmongocrypt 1.7.1 in release wheels. Changes in Version 1.5.0 ------------------------ - Add support for range-based Queryable Encryption with the new "rangePreview" algorithm. NOTE: The "rangePreview" algorithm is experimental only. It is not intended for public use. - Bundle libmongocrypt 1.7.0 in release wheels. - **Remove support for libmongocrypt <=1.7.0, libmongocrypt >=1.7.0 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.4.1 ------------------------ - Fixed spurious exception traceback at interpreter shutdown: ``AttributeError: 'NoneType' object has no attribute 'mongocrypt_destroy'`` Changes in Version 1.4.0 ------------------------ - Bundle libmongocrypt 1.6.1 in release wheels. - Support GCP attached service accounts when using GCP KMS. - Support Azure VM-assigned Managed Identity for Automatic KMS Credentials. - Support obtaining AWS credentials for CSFLE in the same way as for MONGODB-AWS. Changes in Version 1.3.1 ------------------------ 1.3.1 is a recommended upgrade for all users of 1.3.0. - Fix a potential data corruption bug in RewrapManyDataKey (ClientEncryption.rewrap_many_data_key) when rotating encrypted data encryption keys backed by GCP or Azure key services. The following conditions will trigger this bug: - A GCP-backed or Azure-backed data encryption key being rewrapped requires fetching an access token for decryption of the data encryption key. The result of this bug is that the key material for all data encryption keys being rewrapped is replaced by new randomly generated material, destroying the original key material. To mitigate potential data corruption, upgrade to this version or higher before using RewrapManyDataKey to rotate Azure-backed or GCP-backed data encryption keys. A backup of the key vault collection should always be taken before key rotation. - Bundle libmongocrypt 1.5.2 in release wheels. - **Remove support for libmongocrypt <=1.5.1, libmongocrypt >=1.5.2 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.3.0 ------------------------ - Bundle libmongocrypt 1.5.0 in release wheels. - Add support for Queryable Encryption with MongoDB 6.0. - Add support for the crypt_shared library which can be used instead of mongocryptd. - **Remove support for libmongocrypt 1.3, libmongocrypt >=1.5 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.2.0 ------------------------ - Add support for the "kmip" KMS provider. - Add MongoCryptKmsContext.kms_provider property. - Bundle libmongocrypt 1.3.0 in release wheels. - **Remove support for libmongocrypt 1.2, libmongocrypt >=1.3 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.1.2 ------------------------ - Fix a bug where decrypting from a memoryview was not supported. - Bundle libmongocrypt 1.2.2 in release wheels. Changes in Version 1.1.1 ------------------------ - Bundle libmongocrypt 1.2.1 in release wheels. Changes in Version 1.1.0 ------------------------ - Add support for Azure and GCP KMS providers. - Add support for temporary AWS credentials via the "sessionToken" option. - Bundle libmongocrypt 1.2.0 in release wheels. - **Remove support for libmongocrypt 1.0 and 1.1, libmongocrypt >=1.2 is now required.** Note this is only relevant for users that install from source or use the ``PYMONGOCRYPT_LIB`` environment variable. Changes in Version 1.0.1 ------------------------ - Bundle libmongocrypt 1.0.4 in release wheels. Changes in Version 1.0.0 ------------------------ - The first stable version. - Bundle libmongocrypt 1.0.0 in release wheels. Changes in Version 0.1b3 ------------------------ - Add support for custom KMS endpoints with the AWS masterkey provider. - Bundle libmongocrypt 1.0.0 in release wheels. Changes in Version 0.1b2 ------------------------ - Document that pip 19 is required for manylinux2010 wheel installation. - Bundle libmongocrypt 1.0.0-beta5 in release wheels. Changes in Version 0.1b1 ------------------------ - Make pymongocrypt compatible with manylinux2010 releases. - Bundle libmongocrypt 1.0.0-beta4 in release wheels. Changes in Version 0.1b0 ------------------------ - Initial Python binding for libmongocrypt. - Bundle libmongocrypt 1.0.0-beta4 in release wheels. libmongocrypt-1.19.0/bindings/python/CONTRIBUTING.md000066400000000000000000000034121521103432300220330ustar00rootroot00000000000000# Contributing to PyMongoCrypt ## Asyncio considerations PyMongoCrypt adds asyncio capability by modifying the source files in */asynchronous to */synchronous using [unasync](https://github.com/python-trio/unasync/) and some custom transforms. Where possible, edit the code in `*/asynchronous/*.py` and not the synchronous files. You can run `pre-commit run --all-files synchro` before running tests if you are testing synchronous code. To prevent the synchro hook from accidentally overwriting code, it first checks to see whether a sync version of a file is changing and not its async counterpart, and will fail. In the unlikely scenario that you want to override this behavior, first export `OVERRIDE_SYNCHRO_CHECK=1`. Sometimes, the synchro hook will fail and introduce changes many previously unmodified files. This is due to static Python errors, such as missing imports, incorrect syntax, or other fatal typos. To resolve these issues, run `pre-commit run --all-files --hook-stage manual ruff` and fix all reported errors before running the synchro hook again. ## Updating the libmongocrypt bindings To update the libmongocrypt bindings in `pymongocrypt/binding.py`, run the following script: ```bash python scripts/update_binding.py ``` ## Update the bundled version of libmongocrypt To update the bundled version of libmongocrypt, run the following script: ```bash bash scripts/update-version.sh ``` This will set the version in `scripts/libmongocrypt-version.sh` and update `sbom.json` to reflect the new vendored version of `libmongocrypt`. ## Building wheels To build wheels, run `scripts/release.sh`. It will build the appropriate wheel for the current system on Windows and MacOS. If docker is available on Linux or MacOS, it will build the manylinux wheels. libmongocrypt-1.19.0/bindings/python/LICENSE000066400000000000000000000261351521103432300206160ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. libmongocrypt-1.19.0/bindings/python/README.rst000066400000000000000000000170711521103432300212770ustar00rootroot00000000000000============ PyMongoCrypt ============ :Info: Python bindings for libmongocrypt. See `GitHub `_ for the latest source. :Author: Shane Harvey About ===== Python wrapper library for libmongocrypt that supports client side encryption in drivers. PyMongoCrypt uses `cffi `_ and `cryptography `_. PyMongoCrypt supports Python 3.8+ and PyPy3.9+. Support / Feedback ================== For issues with, questions about, or feedback for PyMongoCrypt, please look into our `support channels `_. Please do not email any of the PyMongoCrypt developers directly with issues or questions - you're more likely to get an answer on the `mongodb-user `_ list on Google Groups. Bugs / Feature Requests ======================= Think you’ve found a bug? Want to see a new feature in PyMongoCrypt? Please open a case in our issue management tool, JIRA: - `Create an account and login `_. - Navigate to `the PYTHON project `_. - Click **Create Issue** - Please provide as much information as possible about the issue type and how to reproduce it. Bug reports in JIRA for all driver projects (i.e. PYTHON, CSHARP) and the Core Server (i.e. SERVER) project are **public**. How To Ask For Help ------------------- Please include all of the following information when opening an issue: - Detailed steps to reproduce the problem, including full traceback, if possible. - The exact python version used, with patch level:: $ python -c "import sys; print(sys.version)" - The exact version of PyMongoCrypt used:: $ python -c "import pymongocrypt; print(pymongocrypt.__version__)" - The exact version of libbmongocrypt used by PyMongoCrypt:: $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" - The exact version of PyMongo used (if applicable), with patch level:: $ python -c "import pymongo; print(pymongo.version); print(pymongo.has_c())" - The operating system and version (e.g. Windows 7, OSX 10.8, ...) - Web framework or asynchronous network library used, if any, with version (e.g. Django 1.7, mod_wsgi 4.3.0, gevent 1.0.1, Tornado 4.0.2, ...) Security Vulnerabilities ------------------------ If you've identified a security vulnerability in a driver or any other MongoDB project, please report it according to the `instructions here `_. Installation ============ PyMongoCrypt can be installed with `pip `_:: $ python -m pip install pymongocrypt $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.9.0 PyMongoCrypt ships wheels for macOS, Windows, and manylinux2010 that include an embedded libmongocrypt build. Installing from wheels on Linux requires pip 19 or later because it adds `support for manylinux2010 wheels `_. Older versions of pip will attempt installation using the pymongocrypt-X.Y.tar.gz source distribution which requires the extra step of downloading and installing libmongocrypt as described below. Users can upgrade to pip 19 by running:: $ python -m pip install --upgrade 'pip>=19' Installing from source ---------------------- Installing from source (or the pymongocrypt-X.Y.tar.gz source distribution, or pip < 19 on Linux) requires an extra step of installing libmongocrypt. First, install PyMongoCrypt from source:: $ git clone git@github.com:mongodb/libmongocrypt.git $ python -m pip install ./libmongocrypt/bindings/python Next, install libmongocrypt: Installing libmongocrypt ^^^^^^^^^^^^^^^^^^^^^^^^ libmongocrypt is continuously built and published on evergreen. Follow the instructions `here `_ to install the correct libmongocrypt build for your operating system. Alternatively, the latest tarballs containing libmongocrypt binaries are `published here `_. Download and extract the correct ``libmongocrypt.tar.gz`` for your operating system and set ``PYMONGOCRYPT_LIB`` to the path to your operating system's ``libmongocrypt.so`` file. For example:: macOS:: $ # Set PYMONGOCRYPT_LIB for macOS: $ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt/lib/libmongocrypt.dylib $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.18.0 Windows:: $ # Set PYMONGOCRYPT_LIB for Windows: $ chmod +x $(pwd)/libmongocrypt-all/windows-test/bin/mongocrypt.dll $ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt/bin/mongocrypt.dll $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.18.0 Linux: set the libmongocrypt build for your platform, for example for Ubuntu 22.04 x86_64:: $ # Set PYMONGOCRYPT_LIB for Ubuntu 22.04 x86_64: $ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt/lib64/libmongocrypt.so $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.18.0 $ # Check that native crypto is enabled for better performance: $ python -c 'from pymongocrypt.binding import lib;print(lib.mongocrypt_is_crypto_available())' True Note if your Linux platform is not available, the generic ``linux-x86_64-glibc_2_7-nocrypto`` build should still be compatible however the "nocrypto" build will result in lower performance for encryption and decryption:: $ # Set PYMONGOCRYPT_LIB for linux-x86_64-glibc_2_7-nocrypto: $ export PYMONGOCRYPT_LIB=$(pwd)/libmongocrypt/lib64/libmongocrypt.so $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.18.0 $ python -c 'from pymongocrypt.binding import lib;print(lib.mongocrypt_is_crypto_available())' False Dependencies ============ PyMongoCrypt supports Python 3.8+ and PyPy3.9+. PyMongoCrypt requires `cffi `_ and `cryptography `_. If not installed using one of the official wheels, PyMongoCrypt also requires libmongocrypt to be installed on your system. If libmongocrypt is not installed you will see an error like this: .. code-block:: python >>> import pymongocrypt Traceback (most recent call last): File "", line 1, in File "pymongocrypt/__init__.py", line 15, in from pymongocrypt.binding import libmongocrypt_version, lib File "pymongocrypt/binding.py", line 803, in lib = ffi.dlopen(os.environ.get('PYMONGOCRYPT_LIB', 'mongocrypt')) File "/.../lib/python3.8/site-packages/cffi/api.py", line 146, in dlopen lib, function_cache = _make_ffi_library(self, name, flags) File "/.../lib/python3.8/site-packages/cffi/api.py", line 828, in _make_ffi_library backendlib = _load_backend_lib(backend, libname, flags) File "/.../lib/python3.8/site-packages/cffi/api.py", line 823, in _load_backend_lib raise OSError(msg) OSError: ctypes.util.find_library() did not manage to locate a library called 'mongocrypt' Use the ``PYMONGOCRYPT_LIB`` environment variable to load a locally installed libmongocrypt build without relying on platform specific library path environment variables, like ``LD_LIBRARY_PATH``. For example:: $ export PYMONGOCRYPT_LIB='/path/to/libmongocrypt.so' $ python -c "import pymongocrypt; print(pymongocrypt.libmongocrypt_version())" 1.9.0 Testing ======= The easiest way to run the tests is to run **python setup.py test** in the root of the distribution. libmongocrypt-1.19.0/bindings/python/RELEASE.rst000066400000000000000000000066361521103432300214270ustar00rootroot00000000000000===================== PyMongoCrypt Releases ===================== Versioning ---------- PyMongoCrypt's version numbers follow `semantic versioning`_: each version number is structured "major.minor.patch". Patch releases fix bugs, minor releases add features (and may fix bugs), and major releases include API changes that break backwards compatibility (and may add features and fix bugs). In between releases we add .devN to the version number to denote the version under development. So if we just released 1.0.0, then the current dev version might be 1.0.1.dev0 or 1.1.0.dev0. PyMongoCrypt's version numbers do not necessarily correspond to the embedded libmongocrypt library's version number. For example, assume the current PyMongoCrypt version is 1.0 and libmongocrypt is 1.0. Let's say that libmongocrypt 2.0.0 is released which has breaking changes to its API. If those 2.0.0 changes do not require any breaking changes to PyMongoCrypt, then the next version can be 1.1. .. _semantic versioning: http://semver.org/ Release Process --------------- PyMongoCrypt ships wheels for macOS, Windows, and manylinux2010 that include an embedded libmongocrypt build. Releasing a new version requires macOS with Docker and a Windows machine. #. Create a ticket for the release and create a PR. The PR needs to include the next steps including the version change because the branch is protected from directly pushing commits. #. Edit the release.sh script to embed the most recent libmongocrypt tag into our wheels, for example:: # The libmongocrypt git revision release to embed in our wheels. -REVISION=$(git rev-list -n 1 1.0.0) +REVISION=$(git rev-list -n 1 1.0.1) #. Add a changelog entry for this release in CHANGELOG.rst. #. Bump "__version__" in ``pymongocrypt/version.py``. #. After merging the PR, clone the repository and check out the commit with the version change. #. Create and push tag:: $ git tag -a "pymongocrypt-" -m "pymongocrypt-" $ MACOS_TARGET=macos_x86_64 PYTHON= ./release.sh $ PYTHON= ./release.sh Make sure to run using the official binaries for Python 3.8 and 3.10. You should end up with the same files created by Evergreen (except for the Windows wheel). #. To build the release package for Windows, launch a windows-64-vsMulti-small Evergreen spawn host, clone the repro, checkout the release tag, and run the release script:: $ git clone git@github.com:mongodb/libmongocrypt.git $ cd libmongocrypt/bindings/python $ git checkout "pymongocrypt " $ ./release.sh This will create the following distributions:: $ ls dist pymongocrypt--py2.py3-none-win_amd64.whl libmongocrypt-1.19.0/bindings/python/hatch_build.py000066400000000000000000000022021521103432300224160ustar00rootroot00000000000000"""A custom hatch build hook for pymongo.""" from __future__ import annotations import os import sys from pathlib import Path from hatchling.builders.hooks.plugin.interface import BuildHookInterface class CustomHook(BuildHookInterface): """The pymongocrypt build hook.""" def initialize(self, version, build_data): """Initialize the hook.""" if self.target_name == "sdist": return # Ensure wheel is marked as binary. # On linux, we use auditwheel to set the name. if sys.platform == "darwin": os.environ["MACOSX_DEPLOYMENT_TARGET"] = "11.0" build_data["tag"] = "py3-none-macosx_11_0_universal2" patt = ".dylib" elif os.name == "nt": build_data["tag"] = "py3-none-win_amd64" patt = ".dll" else: patt = ".so" here = Path(__file__).parent.resolve() dpath = here / "pymongocrypt" for fpath in dpath.glob(f"*{patt}"): relpath = os.path.relpath(fpath, here) build_data["artifacts"].append(relpath) build_data["force_include"][relpath] = relpath libmongocrypt-1.19.0/bindings/python/pymongocrypt/000077500000000000000000000000001521103432300223545ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/pymongocrypt/__init__.py000066400000000000000000000013131521103432300244630ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from pymongocrypt.binding import lib, libmongocrypt_version # noqa: F401 from pymongocrypt.version import __version__ # noqa: F401 libmongocrypt-1.19.0/bindings/python/pymongocrypt/asynchronous/000077500000000000000000000000001521103432300251075ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/pymongocrypt/asynchronous/auto_encrypter.py000066400000000000000000000040631521103432300305270ustar00rootroot00000000000000# Copyright 2024-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. from pymongocrypt.asynchronous.state_machine import run_state_machine from pymongocrypt.mongocrypt import MongoCrypt class AsyncAutoEncrypter: def __init__(self, callback, mongo_crypt_opts): """Encrypts and decrypts MongoDB commands. This class is used by a driver to support automatic encryption and decryption of MongoDB commands. :Parameters: - `callback`: A :class:`MongoCryptCallback`. - `mongo_crypt_opts`: A :class:`MongoCryptOptions`. """ self.callback = callback self.mongocrypt = MongoCrypt(mongo_crypt_opts, callback) async def encrypt(self, database, cmd): """Encrypt a MongoDB command. :Parameters: - `database`: The database for this command. - `cmd`: A MongoDB command as BSON. :Returns: The encrypted command. """ with self.mongocrypt.encryption_context(database, cmd) as ctx: return await run_state_machine(ctx, self.callback) async def decrypt(self, response): """Decrypt a MongoDB command response. :Parameters: - `response`: A MongoDB command response as BSON. :Returns: The decrypted command response. """ with self.mongocrypt.decryption_context(response) as ctx: return await run_state_machine(ctx, self.callback) async def close(self): """Cleanup resources.""" self.mongocrypt.close() await self.callback.close() libmongocrypt-1.19.0/bindings/python/pymongocrypt/asynchronous/credentials.py000066400000000000000000000126211521103432300277600ustar00rootroot00000000000000# Copyright 2024-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. import os from collections import namedtuple from datetime import datetime, timedelta, timezone try: from pymongo_auth_aws.auth import aws_temp_credentials _HAVE_AUTH_AWS = True except ImportError: _HAVE_AUTH_AWS = False import httpx from pymongocrypt.errors import MongoCryptError _azure_creds = namedtuple("_azure_creds", ["access_token", "expires_utc"]) _azure_creds_cache = None async def _get_gcp_credentials(): """Get on-demand GCP credentials""" metadata_host = os.getenv("GCE_METADATA_HOST") or "metadata.google.internal" url = ( "http://%s/computeMetadata/v1/instance/service-accounts/default/token" % metadata_host ) headers = {"Metadata-Flavor": "Google"} client = httpx.AsyncClient() try: response = await client.get(url, headers=headers) except Exception as e: msg = "unable to retrieve GCP credentials: %s" % e raise MongoCryptError(msg) from e finally: await client.aclose() if response.status_code != 200: msg = f"Unable to retrieve GCP credentials: expected StatusCode 200, got StatusCode: {response.status_code}. Response body:\n{response.content}" raise MongoCryptError(msg) try: data = response.json() except Exception as e: raise MongoCryptError( f"unable to retrieve GCP credentials: error reading response body\n{response.content}" ) from e if not data.get("access_token"): msg = ( "unable to retrieve GCP credentials: got unexpected empty accessToken from GCP Metadata Server. Response body: %s" % response.content ) raise MongoCryptError(msg) return {"accessToken": data["access_token"]} async def _get_azure_credentials(): """Get on-demand Azure credentials""" global _azure_creds_cache # Credentials are considered expired when: Expiration - now < 1 mins. creds = _azure_creds_cache if creds: if creds.expires_utc - datetime.now(tz=timezone.utc) < timedelta(seconds=60): _azure_creds_cache = None else: return {"accessToken": creds.access_token} url = "http://169.254.169.254/metadata/identity/oauth2/token" url += "?api-version=2018-02-01" url += "&resource=https://vault.azure.net" headers = {"Metadata": "true", "Accept": "application/json"} client = httpx.AsyncClient() try: response = await client.get(url, headers=headers) except Exception as e: msg = "Failed to acquire IMDS access token: %s" % e raise MongoCryptError(msg) from e finally: await client.aclose() if response.status_code != 200: msg = "Failed to acquire IMDS access token." raise MongoCryptError(msg) try: data = response.json() except Exception as e: raise MongoCryptError("Azure IMDS response must be in JSON format.") from e for key in ["access_token", "expires_in"]: if not data.get(key): msg = "Azure IMDS response must contain %s, but was %s." msg = msg % (key, response.content) raise MongoCryptError(msg) try: expires_in = int(data["expires_in"]) except ValueError as e: raise MongoCryptError( 'Azure IMDS response must contain "expires_in" integer, but was %s.' % response.content ) from e expires_utc = datetime.now(tz=timezone.utc) + timedelta(seconds=expires_in) _azure_creds_cache = _azure_creds(data["access_token"], expires_utc) return {"accessToken": data["access_token"]} async def _ask_for_kms_credentials(kms_providers): """Get on-demand kms credentials. This is a separate function so it can be overridden in unit tests.""" global _azure_creds_cache on_demand_aws = "aws" in kms_providers and not len(kms_providers["aws"]) on_demand_gcp = "gcp" in kms_providers and not len(kms_providers["gcp"]) on_demand_azure = "azure" in kms_providers and not len(kms_providers["azure"]) if not any([on_demand_aws, on_demand_gcp, on_demand_azure]): return {} creds = {} if on_demand_aws: if not _HAVE_AUTH_AWS: raise RuntimeError( "On-demand AWS credentials require pymongo-auth-aws: " "install with: python -m pip install 'pymongo[aws]'" ) aws_creds = aws_temp_credentials() creds_dict = { "accessKeyId": aws_creds.username, "secretAccessKey": aws_creds.password, } if aws_creds.token: creds_dict["sessionToken"] = aws_creds.token creds["aws"] = creds_dict if on_demand_gcp: creds["gcp"] = await _get_gcp_credentials() if on_demand_azure: try: creds["azure"] = await _get_azure_credentials() except Exception: _azure_creds_cache = None raise return creds libmongocrypt-1.19.0/bindings/python/pymongocrypt/asynchronous/explicit_encrypter.py000066400000000000000000000144251521103432300314030ustar00rootroot00000000000000# Copyright 2024-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. from pymongocrypt.asynchronous.state_machine import run_state_machine from pymongocrypt.mongocrypt import MongoCrypt from pymongocrypt.options import DataKeyOpts, ExplicitEncryptOpts class AsyncExplicitEncrypter: def __init__(self, callback, mongo_crypt_opts): """Encrypts and decrypts BSON values. This class is used by a driver to support explicit encryption and decryption of individual fields in a BSON document. :Parameters: - `callback`: A :class:`MongoCryptCallback`. - `mongo_crypt_opts`: A :class:`MongoCryptOptions`. """ self.callback = callback if mongo_crypt_opts.schema_map is not None: raise ValueError("mongo_crypt_opts.schema_map must be None") self.mongocrypt = MongoCrypt(mongo_crypt_opts, callback) async def create_data_key( self, kms_provider, master_key=None, key_alt_names=None, key_material=None ): """Creates a data key used for explicit encryption. :Parameters: - `kms_provider`: The KMS provider to use. Supported values are "aws", "azure", "gcp", "kmip", "local", or a named provider like "kmip:name". - `master_key`: See class:`DataKeyOpts`. - `key_alt_names` (optional): An optional list of string alternate names used to reference a key. If a key is created with alternate names, then encryption may refer to the key by the unique alternate name instead of by ``_id``. - `key_material`: (optional) See class:`DataKeyOpts`. :Returns: The _id of the created data key document. """ # CDRIVER-3275 each key_alt_name needs to be wrapped in a bson # document. encoded_names = [] if key_alt_names is not None: for name in key_alt_names: encoded_names.append(self.callback.bson_encode({"keyAltName": name})) if key_material is not None: key_material = self.callback.bson_encode({"keyMaterial": key_material}) opts = DataKeyOpts(master_key, encoded_names, key_material) with self.mongocrypt.data_key_context(kms_provider, opts) as ctx: key = await run_state_machine(ctx, self.callback) return await self.callback.insert_data_key(key) async def rewrap_many_data_key(self, filter, provider=None, master_key=None): """Decrypts and encrypts all matching data keys with a possibly new `master_key` value. :Parameters: - `filter`: A document used to filter the data keys. - `provider`: (optional) The name of a different kms provider. - `master_key`: Optional document for the given provider. :Returns: A binary document with the rewrap data. """ with self.mongocrypt.rewrap_many_data_key_context( filter, provider, master_key ) as ctx: return await run_state_machine(ctx, self.callback) async def encrypt( self, value, algorithm, key_id=None, key_alt_name=None, query_type=None, contention_factor=None, range_opts=None, is_expression=False, text_opts=None, ): """Encrypts a BSON value. Note that exactly one of ``key_id`` or ``key_alt_name`` must be provided. :Parameters: - `value` (bytes): The BSON value to encrypt. - `algorithm` (string): The encryption algorithm to use. See :class:`Algorithm` for some valid options. - `key_id` (bytes): The bytes of the binary subtype 4 ``_id`` data key. For example, ``uuid.bytes`` or ``bytes(bson_binary)``. - `key_alt_name` (string): Identifies a key vault document by 'keyAltName'. - `query_type` (str): The query type to execute. - `contention_factor` (int): The contention factor to use when the algorithm is "Indexed". - `range_opts` (bytes): Options for explicit encryption with the "range" algorithm encoded as a BSON document. - `is_expression` (boolean): True if this is an encryptExpression() context. Defaults to False. - `text_opts` (bytes): Options for explicit encryption with the "textPreview" algorithm encoded as a BSON document. :Returns: The encrypted BSON value. .. versionchanged:: 1.3 Added the `query_type` and `contention_factor` parameters. .. versionchanged:: 1.5 Added the `range_opts` and `is_expression` parameters. .. versionchanged:: 1.16 Added the `text_opts` parameter. """ # CDRIVER-3275 key_alt_name needs to be wrapped in a bson document. if key_alt_name is not None: key_alt_name = self.callback.bson_encode({"keyAltName": key_alt_name}) opts = ExplicitEncryptOpts( algorithm, key_id, key_alt_name, query_type, contention_factor, range_opts, is_expression, text_opts, ) with self.mongocrypt.explicit_encryption_context(value, opts) as ctx: return await run_state_machine(ctx, self.callback) async def decrypt(self, value): """Decrypts a BSON value. :Parameters: - `value`: The encoded document to decrypt, which must be in the form { "v" : encrypted BSON value }}. :Returns: The decrypted BSON value. """ with self.mongocrypt.explicit_decryption_context(value) as ctx: return await run_state_machine(ctx, self.callback) def close(self): """Cleanup resources.""" self.mongocrypt.close() libmongocrypt-1.19.0/bindings/python/pymongocrypt/asynchronous/state_machine.py000066400000000000000000000117371521103432300302760ustar00rootroot00000000000000# Copyright 2024-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. from abc import abstractmethod from pymongocrypt.asynchronous.credentials import _ask_for_kms_credentials from pymongocrypt.binding import lib from pymongocrypt.compat import ABC from pymongocrypt.errors import MongoCryptError class AsyncMongoCryptCallback(ABC): """Callback ABC to perform I/O on behalf of libbmongocrypt.""" @abstractmethod async def kms_request(self, kms_context): """Complete a KMS request. :Parameters: - `kms_context`: A :class:`MongoCryptKmsContext`. :Returns: None """ @abstractmethod async def collection_info(self, database, filter): """Get the collection info for a namespace. The returned collection info is passed to libmongocrypt which reads the JSON schema. :Parameters: - `database`: The database on which to run listCollections. - `filter`: The filter to pass to listCollections. :Returns: The all or first document from the listCollections command response as BSON. """ @abstractmethod async def mark_command(self, database, cmd): """Mark a command for encryption. :Parameters: - `database`: The database on which to run this command. - `cmd`: The BSON command to run. :Returns: The marked command response from mongocryptd. """ @abstractmethod async def fetch_keys(self, filter): """Yields one or more keys from the key vault. :Parameters: - `filter`: The filter to pass to find. :Returns: A generator which yields the requested keys from the key vault. """ @abstractmethod async def insert_data_key(self, data_key): """Insert a data key into the key vault. :Parameters: - `data_key`: The data key document to insert. :Returns: The _id of the inserted data key document. """ @abstractmethod def bson_encode(self, doc): """Encode a document to BSON. A document can be any mapping type (like :class:`dict`). :Parameters: - `doc`: mapping type representing a document :Returns: The encoded BSON bytes. """ @abstractmethod async def close(self): """Release resources.""" async def run_state_machine(ctx, callback): """Run the libmongocrypt state machine until completion. :Parameters: - `ctx`: A :class:`MongoCryptContext`. - `callback`: A :class:`AsyncMongoCryptCallback`. :Returns: The completed libmongocrypt operation. """ while True: state = ctx.state # Check for terminal states first. if state == lib.MONGOCRYPT_CTX_ERROR: ctx._raise_from_status() elif state == lib.MONGOCRYPT_CTX_READY: return ctx.finish() elif state == lib.MONGOCRYPT_CTX_DONE: return None if state == lib.MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: list_colls_filter = ctx.mongo_operation() coll_info = await callback.collection_info(ctx.database, list_colls_filter) if coll_info: if isinstance(coll_info, list): for i in coll_info: ctx.add_mongo_operation_result(i) else: ctx.add_mongo_operation_result(coll_info) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: mongocryptd_cmd = ctx.mongo_operation() result = await callback.mark_command(ctx.database, mongocryptd_cmd) ctx.add_mongo_operation_result(result) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS: key_filter = ctx.mongo_operation() async for key in callback.fetch_keys(key_filter): ctx.add_mongo_operation_result(key) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_KMS: for kms_ctx in ctx.kms_contexts(): with kms_ctx: await callback.kms_request(kms_ctx) ctx.complete_kms() elif state == lib.MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: creds = await _ask_for_kms_credentials(ctx.kms_providers) ctx.provide_kms_providers(callback.bson_encode(creds)) else: raise MongoCryptError(f"unknown state: {state}") libmongocrypt-1.19.0/bindings/python/pymongocrypt/auto_encrypter.py000066400000000000000000000012431521103432300257710ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Alias file for import compatibility from pymongocrypt.synchronous.auto_encrypter import * libmongocrypt-1.19.0/bindings/python/pymongocrypt/binary.py000066400000000000000000000055501521103432300242170ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Internal helpers for dealing with mongocrypt_binary_t.""" from pymongocrypt.binding import ffi, lib from pymongocrypt.errors import MongoCryptError def _to_bytes(mongocrypt_binary): """Returns this mongocrypt_binary_t as bytes.""" data = mongocrypt_binary.data if data == ffi.NULL: raise MongoCryptError("mongocrypt_binary_t.data returned NULL") return ffi.unpack(ffi.cast("char*", data), mongocrypt_binary.len) def _write_bytes(mongocrypt_binary, data): """Writes the given data to a mongocrypt_binary_t.""" buf = mongocrypt_binary.data if buf == ffi.NULL: raise MongoCryptError("mongocrypt_binary_t.data returned NULL") ffi.memmove(buf, data, len(data)) mongocrypt_binary.len = len(data) class _MongoCryptBinary: __slots__ = ("bin",) def __init__(self, binary): """Wraps a mongocrypt_binary_t.""" if binary == ffi.NULL: raise MongoCryptError("unable to create new mongocrypt_binary object") self.bin = binary def _close(self): """Cleanup resources.""" if self.bin: lib.mongocrypt_binary_destroy(self.bin) self.bin = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self._close() def to_bytes(self): """Returns this mongocrypt_binary_t as bytes.""" data = self.bin.data if data == ffi.NULL: return b"" return ffi.unpack(ffi.cast("char*", data), self.bin.len) class MongoCryptBinaryOut(_MongoCryptBinary): __slots__ = () def __init__(self): """Wraps a mongocrypt_binary_t.""" super().__init__(lib.mongocrypt_binary_new()) class MongoCryptBinaryIn(_MongoCryptBinary): __slots__ = ("cref",) def __init__(self, data): """Creates a mongocrypt_binary_t from binary data.""" # mongocrypt_binary_t does not own the data it is passed so we need to # create a separate reference to keep the data alive. self.cref = ffi.from_buffer("uint8_t[]", data) super().__init__(lib.mongocrypt_binary_new_from_data(self.cref, len(data))) def _close(self): """Cleanup resources.""" super()._close() if self.cref is not None: ffi.release(self.cref) self.cref = None libmongocrypt-1.19.0/bindings/python/pymongocrypt/binding.py000066400000000000000000001660241521103432300243510ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import os.path import sys from pathlib import Path import cffi from packaging.version import Version from pymongocrypt.version import _MIN_LIBMONGOCRYPT_VERSION def _parse_version(version): return Version(version) ffi = cffi.FFI() # Start embedding from update_binding.py ffi.cdef( """ /* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** @file mongocrypt.h The top-level handle to libmongocrypt. */ /** * @mainpage libmongocrypt * See all public API documentation in: @ref mongocrypt.h */ /* clang-format off */ /* clang-format on */ /** * Returns the version string for libmongocrypt. * * @param[out] len An optional length of the returned string. May be NULL. * @returns a NULL terminated version string for libmongocrypt. */ const char *mongocrypt_version(uint32_t *len); /** * Returns true if libmongocrypt was built with native crypto support. * * If libmongocrypt was not built with native crypto support, setting crypto * hooks is required. * * @returns True if libmongocrypt was built with native crypto support. */ bool mongocrypt_is_crypto_available(void); /** * A non-owning view of a byte buffer. * * When constructing a 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 mongocrypt_binary_t as an argument will make a copy of * the viewed data. For example, the following is valid: * * @code{.c} * mongocrypt_binary_t bin = mongocrypt_binary_new_from_data(mydata, mylen); * assert (mongocrypt_setopt_kms_provider_local (crypt), bin); * // The viewed data of bin has been copied. Ok to free the view and the data. * mongocrypt_binary_destroy (bin); * my_free_fn (mydata); * @endcode * * Functions with a mongocrypt_binary_t* out guarantee the lifetime of the * viewed data to live as long as the parent object. For example, @ref * mongocrypt_ctx_mongo_op guarantees that the viewed data of * mongocrypt_binary_t is valid until the parent ctx is destroyed with @ref * mongocrypt_ctx_destroy. * * The `mongocrypt_binary_t` struct definition is public. * Consumers may rely on the struct layout. */ typedef struct _mongocrypt_binary_t { void *data; uint32_t len; } mongocrypt_binary_t; /** * Create a new non-owning view of a buffer (data + length). * * Use this to create a mongocrypt_binary_t used for output parameters. * * @returns A new mongocrypt_binary_t. */ mongocrypt_binary_t *mongocrypt_binary_new(void); /** * Create a new non-owning view of a buffer (data + length). * * @param[in] data A pointer to an array of bytes. This data is not copied. @p * data must outlive the binary object. * @param[in] len The length of the @p data byte array. * * @returns A new @ref mongocrypt_binary_t. */ mongocrypt_binary_t *mongocrypt_binary_new_from_data(uint8_t *data, uint32_t len); /** * Get a pointer to the viewed data. * * @param[in] binary The @ref mongocrypt_binary_t. * * @returns A pointer to the viewed data. */ uint8_t *mongocrypt_binary_data(const mongocrypt_binary_t *binary); /** * Get the length of the viewed data. * * @param[in] binary The @ref mongocrypt_binary_t. * * @returns The length of the viewed data. */ uint32_t mongocrypt_binary_len(const mongocrypt_binary_t *binary); /** * Free the @ref mongocrypt_binary_t. * * This does not free the viewed data. * * @param[in] binary The mongocrypt_binary_t destroy. */ void mongocrypt_binary_destroy(mongocrypt_binary_t *binary); /** * Indicates success or contains error information. * * Functions like @ref mongocrypt_ctx_encrypt_init follow a pattern to expose a * status. A boolean is returned. True indicates success, and false indicates * failure. On failure a status on the handle is set, and is accessible with a * corresponding (handle)_status function. E.g. @ref mongocrypt_ctx_status. */ typedef struct _mongocrypt_status_t mongocrypt_status_t; /** * Indicates the type of error. */ typedef enum { MONGOCRYPT_STATUS_OK = 0, MONGOCRYPT_STATUS_ERROR_CLIENT = 1, MONGOCRYPT_STATUS_ERROR_KMS = 2, MONGOCRYPT_STATUS_ERROR_CRYPT_SHARED = 3, } mongocrypt_status_type_t; /** * 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. * * @returns A new status object. */ mongocrypt_status_t *mongocrypt_status_new(void); /** * Set a status object with message, type, and code. * * Use this to set the @ref mongocrypt_status_t given in the crypto hooks. * * @param[in] type The status type. * @param[in] code The status code. * @param[in] message The message. * @param[in] message_len Due to historical behavior, pass 1 + the string length * of @p 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. * */ void mongocrypt_status_set(mongocrypt_status_t *status, mongocrypt_status_type_t type, uint32_t code, const char *message, int32_t message_len); /** * Indicates success or the type of error. * * @param[in] status The status object. * * @returns A @ref mongocrypt_status_type_t. */ mongocrypt_status_type_t mongocrypt_status_type(mongocrypt_status_t *status); /** * Get an error code or 0. * * @param[in] status The status object. * * @returns An error code. */ uint32_t mongocrypt_status_code(mongocrypt_status_t *status); /** * Get the error message associated with a status or NULL. * * @param[in] status The status object. * @param[out] len An optional length of the returned string (excluding the * trailing NULL byte). May be NULL. * * @returns A NULL terminated error message or NULL. */ const char *mongocrypt_status_message(mongocrypt_status_t *status, uint32_t *len); /** * Returns true if the status indicates success. * * @param[in] status The status to check. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_status_ok(mongocrypt_status_t *status); /** * Free the memory for a status object. * * @param[in] status The status to destroy. */ void mongocrypt_status_destroy(mongocrypt_status_t *status); /** * Indicates the type of log message. */ typedef enum { MONGOCRYPT_LOG_LEVEL_FATAL = 0, MONGOCRYPT_LOG_LEVEL_ERROR = 1, MONGOCRYPT_LOG_LEVEL_WARNING = 2, MONGOCRYPT_LOG_LEVEL_INFO = 3, MONGOCRYPT_LOG_LEVEL_TRACE = 4 } mongocrypt_log_level_t; /** * A log callback function. Set a custom log callback with @ref * mongocrypt_setopt_log_handler. * * @param[in] message A NULL terminated message. * @param[in] message_len The length of message. * @param[in] ctx A context provided by the caller of @ref * mongocrypt_setopt_log_handler. */ typedef void (*mongocrypt_log_fn_t)(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx); /** * The top-level handle to libmongocrypt. * * Create a mongocrypt_t handle to perform operations within libmongocrypt: * encryption, decryption, registering log callbacks, etc. * * Functions on a mongocrypt_t are thread safe, though functions on derived * 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. */ typedef struct _mongocrypt_t mongocrypt_t; /** * Allocate a new @ref mongocrypt_t object. * * Set options using mongocrypt_setopt_* functions, then initialize with @ref * mongocrypt_init. When done with the @ref mongocrypt_t, free with @ref * mongocrypt_destroy. * * @returns A new @ref mongocrypt_t object. */ mongocrypt_t *mongocrypt_new(void); /** * Set a handler on the @ref mongocrypt_t object to get called on every log * message. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] log_fn The log callback. * @param[in] log_ctx A context passed as an argument to the log callback every * invocation. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_fn, void *log_ctx); /** * Enable or disable KMS retry behavior. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] enable A boolean indicating whether to retry operations. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_retry_kms(mongocrypt_t *crypt, bool enable); /** * Enable support for multiple collection schemas. Required to support $lookup. * * @param[in] crypt The @ref mongocrypt_t object. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_enable_multiple_collinfo(mongocrypt_t *crypt); /** * Configure an AWS KMS provider on the @ref mongocrypt_t object. * * This has been superseded by the more flexible: * @ref mongocrypt_setopt_kms_providers * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] aws_access_key_id The AWS access key ID used to generate KMS * messages. * @param[in] 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[in] aws_secret_access_key The AWS secret access key used to generate * KMS messages. * @param[in] 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). * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt, const char *aws_access_key_id, int32_t aws_access_key_id_len, const char *aws_secret_access_key, int32_t aws_secret_access_key_len); /** * Configure a local KMS provider on the @ref mongocrypt_t object. * * This has been superseded by the more flexible: * @ref mongocrypt_setopt_kms_providers * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] key A 96 byte master key used to encrypt and decrypt key vault * keys. The viewed data is copied. It is valid to destroy @p key with @ref * mongocrypt_binary_destroy immediately after. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_kms_provider_local(mongocrypt_t *crypt, mongocrypt_binary_t *key); /** * Configure KMS providers with a BSON document. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] kms_providers A BSON document mapping the KMS provider names * to credentials. Set a KMS provider value to an empty document to supply * credentials on-demand with @ref mongocrypt_ctx_provide_kms_providers. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_setopt_kms_providers(mongocrypt_t *crypt, mongocrypt_binary_t *kms_providers); /** * Set a local schema map for encryption. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] schema_map A BSON document representing the schema map supplied by * the user. The keys are collection namespaces and values are JSON schemas. The * viewed data copied. It is valid to destroy @p schema_map with @ref * mongocrypt_binary_destroy immediately after. * @pre @p crypt has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status */ bool mongocrypt_setopt_schema_map(mongocrypt_t *crypt, mongocrypt_binary_t *schema_map); /** * Set a local EncryptedFieldConfigMap for encryption. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] efc_map 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. * @pre @p crypt has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status */ bool mongocrypt_setopt_encrypted_field_config_map(mongocrypt_t *crypt, mongocrypt_binary_t *efc_map); /** * @brief Append an additional search directory to the search path for loading * the crypt_shared dynamic library. * * @param[in] crypt The @ref mongocrypt_t object to update * @param[in] 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. * * @note 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. * * @note 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. * * @note 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. */ void mongocrypt_setopt_append_crypt_shared_lib_search_path(mongocrypt_t *crypt, const char *path); /** * @brief Set a single override path for loading the crypt_shared dynamic * library. * * @param[in] crypt The @ref mongocrypt_t object to update * @param[in] 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. * * @note This function will do no IO nor path validation. All validation will * occur during the call to @ref mongocrypt_init. * * @note 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. * * @note 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. */ void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t *crypt, const char *path); /** * @brief Opt-into handling the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state. * * If set, before entering the MONGOCRYPT_CTX_NEED_KMS state, * contexts may enter the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state * and then wait for credentials to be supplied through * @ref mongocrypt_ctx_provide_kms_providers. * * A context will only enter MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS * if an empty document was set for a KMS provider in @ref * mongocrypt_setopt_kms_providers. * * @param[in] crypt The @ref mongocrypt_t object to update */ void mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t *crypt); /** * @brief Opt-into handling the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state. * * A context enters the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state when * processing a `bulkWrite` command. The target database of the `bulkWrite` may differ from the command database * ("admin"). * * @param[in] crypt The @ref mongocrypt_t object to update */ void mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(mongocrypt_t *crypt); /** * Initialize new @ref mongocrypt_t object. * * Set options before using @ref mongocrypt_setopt_kms_provider_local, @ref * mongocrypt_setopt_kms_provider_aws, or @ref mongocrypt_setopt_log_handler. * * @param[in] crypt The @ref mongocrypt_t object. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status Failure may occur if previously * set * options are invalid. */ bool mongocrypt_init(mongocrypt_t *crypt); /** * Get the status associated with a @ref mongocrypt_t object. * * @param[in] crypt The @ref mongocrypt_t object. * @param[out] status Receives the status. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_status(mongocrypt_t *crypt, mongocrypt_status_t *status); /** * Destroy the @ref mongocrypt_t object. * * @param[in] crypt The @ref mongocrypt_t object to destroy. */ void mongocrypt_destroy(mongocrypt_t *crypt); /** * Obtain a nul-terminated version string of the loaded crypt_shared dynamic * library, if available. * * If no crypt_shared was successfully loaded, this function returns NULL. * * @param[in] crypt The mongocrypt_t object after a successful call to * mongocrypt_init. * @param[out] len An optional output parameter to which the length of the * returned string is written. If provided and no crypt_shared library was * loaded, zero is written to *len. * * @return A nul-terminated string of the dynamically loaded crypt_shared * library. * * @note For a numeric value that can be compared against, use * @ref mongocrypt_crypt_shared_lib_version. */ const char *mongocrypt_crypt_shared_lib_version_string(const mongocrypt_t *crypt, uint32_t *len); /** * @brief Obtain a 64-bit constant encoding the version of the loaded * crypt_shared library, if available. * * @param[in] crypt The mongocrypt_t object after a successful call to * mongocrypt_init. * * @return A 64-bit encoded version number, with the version encoded as four * sixteen-bit integers, or zero if no crypt_shared library was loaded. * * The version is encoded as four 16-bit numbers, from high to low: * * - Major version * - Minor version * - Revision * - Reserved * * For example, version 6.2.1 would be encoded as: 0x0006'0002'0001'0000 */ uint64_t mongocrypt_crypt_shared_lib_version(const mongocrypt_t *crypt); /** * Manages the state machine for encryption or decryption. */ typedef struct _mongocrypt_ctx_t mongocrypt_ctx_t; /** * Create a new uninitialized @ref mongocrypt_ctx_t. * * Initialize the context with functions like @ref mongocrypt_ctx_encrypt_init. * When done, destroy it with @ref mongocrypt_ctx_destroy. * * @param[in] crypt The @ref mongocrypt_t object. * @returns A new context. */ mongocrypt_ctx_t *mongocrypt_ctx_new(mongocrypt_t *crypt); /** * Get the status associated with a @ref mongocrypt_ctx_t object. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[out] status Receives the status. * * @returns True if the output is an ok status, false if it is an error * status. * * @see mongocrypt_status_ok */ bool mongocrypt_ctx_status(mongocrypt_ctx_t *ctx, mongocrypt_status_t *status); /** * Set the key id to use for explicit encryption. * * It is an error to set both this and the key alt name. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] 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 * @p key_id with @ref mongocrypt_binary_destroy immediately after. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id); /** * Set the keyAltName to use for explicit encryption or * data key creation. * * Pass the binary encoding a BSON document like the following: * * { "keyAltName" : (BSON UTF8 value) } * * For explicit encryption, it is an error to set both the keyAltName * and the key id. * * For creating data keys, call this function repeatedly to set * multiple keyAltNames. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] key_alt_name The name to use. The viewed data is copied. It is * valid to destroy @p key_alt_name with @ref mongocrypt_binary_destroy * immediately after. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_key_alt_name(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_alt_name); /** * Set the keyMaterial to use for encrypting data. * * Pass the binary encoding of a BSON document like the following: * * { "keyMaterial" : (BSON BINARY value) } * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] 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. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool 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-Random" * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] algorithm A string specifying the algorithm to * use for encryption. * @param[in] len The length of the algorithm string. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorithm, int len); /// String constant for setopt_algorithm "Deterministic" encryption /// String constant for setopt_algorithm "Random" encryption /// String constant for setopt_algorithm "Indexed" explicit encryption /// String constant for setopt_algorithm "Unindexed" explicit encryption // DEPRECATED: support "RangePreview" has been removed in favor of "range". /// NOTE: "textPreview" is experimental only and may be removed in a future non-major release. /** * Identify the AWS KMS master key to use for creating a data key. * * This has been superseded by the more flexible: * @ref mongocrypt_ctx_setopt_key_encryption_key * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] region The AWS region. * @param[in] region_len The string length of @p region. Pass -1 to determine * the string length with strlen (must be NULL terminated). * @param[in] cmk The Amazon Resource Name (ARN) of the customer master key * (CMK). * @param[in] cmk_len The string length of @p cmk_len. Pass -1 to determine the * string length with strlen (must be NULL terminated). * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_masterkey_aws(mongocrypt_ctx_t *ctx, const char *region, int32_t region_len, const char *cmk, int32_t cmk_len); /** * Identify a custom AWS endpoint when creating a data key. * This is used internally to construct the correct HTTP request * (with the Host header set to this endpoint). This endpoint * is persisted in the new data key, and will be returned via * @ref mongocrypt_kms_ctx_endpoint. * * This has been superseded by the more flexible: * @ref mongocrypt_ctx_setopt_key_encryption_key * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] endpoint The endpoint. * @param[in] endpoint_len The string length of @p endpoint. Pass -1 to * determine the string length with strlen (must be NULL terminated). * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_masterkey_aws_endpoint(mongocrypt_ctx_t *ctx, const char *endpoint, int32_t endpoint_len); /** * Set the master key to "local" for creating a data key. * This has been superseded by the more flexible: * @ref mongocrypt_ctx_setopt_key_encryption_key * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_masterkey_local(mongocrypt_ctx_t *ctx); /** * Set key encryption key document for creating a data key or for rewrapping * datakeys. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] bin BSON representing the key encryption key document with * an additional "provider" field. The following forms are accepted: * * AWS * { * provider: "aws", * region: , * key: , * endpoint: * } * * Azure * { * provider: "azure", * keyVaultEndpoint: , * keyName: , * keyVersion: * } * * GCP * { * provider: "gcp", * projectId: , * location: , * keyRing: , * keyName: , * keyVersion: , * endpoint: * } * * Local * { * provider: "local" * } * * KMIP * { * provider: "kmip", * keyId: * endpoint: * } * * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status. */ bool mongocrypt_ctx_setopt_key_encryption_key(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *bin); /** * Initialize a context to create a data key. * * Associated options: * - @ref mongocrypt_ctx_setopt_masterkey_aws * - @ref mongocrypt_ctx_setopt_masterkey_aws_endpoint * - @ref mongocrypt_ctx_setopt_masterkey_local * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status * @pre A master key option has been set, and an associated KMS provider * has been set on the parent @ref mongocrypt_t. */ bool mongocrypt_ctx_datakey_init(mongocrypt_ctx_t *ctx); /** * Initialize a context for encryption. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] db The database name. * @param[in] db_len The byte length of @p db. Pass -1 to determine the string * length with strlen (must * be NULL terminated). * @param[in] cmd The BSON command to be encrypted. The viewed data is copied. * It is valid to destroy @p cmd with @ref mongocrypt_binary_destroy immediately * after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t db_len, mongocrypt_binary_t *cmd); /** * Explicit helper method to encrypt a single BSON object. Contexts * created for explicit encryption will not go through mongocryptd. * * To specify a key_id, algorithm, or iv to use, please use the * corresponding mongocrypt_setopt methods before calling this. * * This method expects the passed-in BSON to be of the form: * { "v" : BSON value to encrypt } * * The value of "v" is expected to be the BSON value passed to a driver * ClientEncryption.encrypt helper. * * Associated options for FLE 1: * - @ref mongocrypt_ctx_setopt_key_id * - @ref mongocrypt_ctx_setopt_key_alt_name * - @ref mongocrypt_ctx_setopt_algorithm * * Associated options for Queryable Encryption: * - @ref mongocrypt_ctx_setopt_key_id * - @ref mongocrypt_ctx_setopt_index_key_id * - @ref mongocrypt_ctx_setopt_contention_factor * - @ref mongocrypt_ctx_setopt_query_type * - @ref mongocrypt_ctx_setopt_algorithm_range * * An error is returned if FLE 1 and Queryable Encryption incompatible options * are set. * * @param[in] ctx A @ref mongocrypt_ctx_t. * @param[in] msg A @ref mongocrypt_binary_t the plaintext BSON value. The * viewed data is copied. It is valid to destroy @p msg with @ref * mongocrypt_binary_destroy immediately after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool 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". * * This method expects the passed-in BSON to be of the form: * { "v" : FLE2RangeFindDriverSpec } * * FLE2RangeFindDriverSpec is a BSON document with one of these forms: * * 1. A Match Expression of this form: * {$and: [{: {: , {: {: }}]} * 2. An Aggregate Expression of this form: * {$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 * * Associated options for Queryable Encryption: * - @ref mongocrypt_ctx_setopt_key_id * - @ref mongocrypt_ctx_setopt_index_key_id * - @ref mongocrypt_ctx_setopt_contention_factor * - @ref mongocrypt_ctx_setopt_query_type * - @ref mongocrypt_ctx_setopt_algorithm_range * * An error is returned if FLE 1 and Queryable Encryption incompatible options * are set. * * @param[in] ctx A @ref mongocrypt_ctx_t. * @param[in] msg A @ref mongocrypt_binary_t the plaintext BSON value. The * viewed data is copied. It is valid to destroy @p msg with @ref * mongocrypt_binary_destroy immediately after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_explicit_encrypt_expression_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg); /** * Initialize a context for decryption. * * This method expects the passed-in BSON to be of the form: * { "v" : BSON value to encrypt } * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] doc The document to be decrypted. The viewed data is copied. It is * valid to destroy @p doc with @ref mongocrypt_binary_destroy immediately * after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *doc); /** * Explicit helper method to decrypt a single BSON object. * * Pass the binary encoding of a BSON document containing the BSON value to * encrypt like the following: * * { "v" : (BSON BINARY value of subtype 6) } * * @param[in] ctx A @ref mongocrypt_ctx_t. * @param[in] msg A @ref mongocrypt_binary_t the encrypted BSON. The viewed data * is copied. It is valid to destroy @p msg with @ref mongocrypt_binary_destroy * immediately after. */ bool mongocrypt_ctx_explicit_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg); /** * @brief Initialize a context to rewrap datakeys. * * Associated options: * - @ref mongocrypt_ctx_setopt_key_encryption_key * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] 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, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status. */ bool mongocrypt_ctx_rewrap_many_datakey_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *filter); /** * Indicates the state of the @ref mongocrypt_ctx_t. Each state requires * different handling. See [the integration * guide](https://github.com/mongodb/libmongocrypt/blob/master/integrating.md#state-machine) * for information on what to do for each state. */ typedef enum { MONGOCRYPT_CTX_ERROR = 0, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1, /* run on main MongoClient */ MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB = 8, /* run on main MongoClient */ MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2, /* run on mongocryptd. */ MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3, /* run on key vault */ MONGOCRYPT_CTX_NEED_KMS = 4, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7, /* fetch/renew KMS credentials */ MONGOCRYPT_CTX_READY = 5, /* ready for encryption/decryption */ MONGOCRYPT_CTX_DONE = 6, } mongocrypt_ctx_state_t; /** * Get the current state of a context. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @returns A @ref mongocrypt_ctx_state_t. */ mongocrypt_ctx_state_t mongocrypt_ctx_state(mongocrypt_ctx_t *ctx); /** * Get BSON necessary to run the mongo operation when mongocrypt_ctx_t * is in MONGOCRYPT_CTX_NEED_MONGO_* states. * * @p op_bson is a BSON document to be used for the operation. * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) 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 command to send to * mongocryptd. * * The lifetime of @p op_bson is tied to the lifetime of @p ctx. It is valid * until @ref mongocrypt_ctx_destroy is called. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[out] op_bson A BSON document for the MongoDB operation. The data * viewed by @p op_bson is guaranteed to be valid until @p ctx is destroyed with * @ref mongocrypt_ctx_destroy. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *op_bson); /** * Get the database to run the mongo operation. * * Only applies when mongocrypt_ctx_t is in the state: * MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB. * * The lifetime of the returned string is tied to the lifetime of @p ctx. It is * valid until @ref mongocrypt_ctx_destroy is called. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @returns A string or NULL. If NULL, an error status is set. Retrieve it with * @ref mongocrypt_ctx_status */ const char *mongocrypt_ctx_mongo_db(mongocrypt_ctx_t *ctx); /** * Feed a BSON reply or result when mongocrypt_ctx_t is in * MONGOCRYPT_CTX_NEED_MONGO_* states. This may be called multiple times * depending on the operation. * * reply is a BSON document result being fed back for this operation. * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) it is a doc from a listCollections * cursor. (Note, if listCollections returned no result, do not call this * function.) * - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a doc from a find cursor. * (Note, if find returned no results, do not call this function. reply must * not * be NULL.) * - For MONGOCRYPT_CTX_NEED_MONGO_MARKINGS it is a reply from mongocryptd. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] reply A BSON document for the MongoDB operation. The viewed data * is copied. It is valid to destroy @p reply with @ref * mongocrypt_binary_destroy immediately after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *reply); /** * Call when done feeding the reply (or replies) back to the context. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_mongo_done(mongocrypt_ctx_t *ctx); /** * Manages a single KMS HTTP request/response. */ typedef struct _mongocrypt_kms_ctx_t mongocrypt_kms_ctx_t; /** * Get the next KMS handle. * * Multiple KMS handles may be retrieved at once. Drivers may do this to fan * out multiple concurrent KMS HTTP requests. Feeding multiple KMS requests * is thread-safe. * * 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 `ctx`. * * @param[in] ctx A @ref mongocrypt_ctx_t. * @returns a new @ref mongocrypt_kms_ctx_t or NULL. */ mongocrypt_kms_ctx_t *mongocrypt_ctx_next_kms_ctx(mongocrypt_ctx_t *ctx); /** * Get the HTTP request message for a KMS handle. * * The lifetime of @p msg is tied to the lifetime of @p kms. It is valid * until @ref mongocrypt_ctx_kms_done is called. * * @param[in] kms A @ref mongocrypt_kms_ctx_t. * @param[out] msg The HTTP request to send to KMS. The data viewed by @p msg is * guaranteed to be valid until the call of @ref mongocrypt_ctx_kms_done of the * parent @ref mongocrypt_ctx_t. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_kms_ctx_status */ bool mongocrypt_kms_ctx_message(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *msg); /** * Get the hostname from which to connect over TLS. * * The storage for @p endpoint is not owned by the caller, but * is valid until calling @ref mongocrypt_ctx_kms_done. * * @param[in] kms A @ref mongocrypt_kms_ctx_t. * @param[out] endpoint The output endpoint as a NULL terminated string. * The endpoint consists of a hostname and port separated by a colon. * E.g. "example.com:123". A port is always present. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_kms_ctx_status */ bool mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t *kms, const char **endpoint); /** * Indicates how many bytes to feed into @ref mongocrypt_kms_ctx_feed. * * @param[in] kms The @ref mongocrypt_kms_ctx_t. * @returns The number of requested bytes. */ uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms); /** * Indicates how long to sleep before sending this request. * * @param[in] kms The @ref mongocrypt_kms_ctx_t. * @returns How long to sleep in microseconds. */ int64_t mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t *kms); /** * Feed bytes from the HTTP response. * * Feeding more bytes than what has been returned in @ref * mongocrypt_kms_ctx_bytes_needed is an error. * * @param[in] kms The @ref mongocrypt_kms_ctx_t. * @param[in] bytes The bytes to feed. The viewed data is copied. It is valid to * destroy @p bytes with @ref mongocrypt_binary_destroy immediately after. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_kms_ctx_status */ bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes); /** * Feed bytes from the KMS response. * * Feeding more bytes than what has been returned in @ref * mongocrypt_kms_ctx_bytes_needed is an error. * * @param[in] kms The @ref mongocrypt_kms_ctx_t. * @param[in] bytes The bytes to feed. The viewed data is copied. It is valid to * destroy @p bytes with @ref mongocrypt_binary_destroy immediately after. * @param[out] should_retry Whether the KMS request should be retried. Retry in-place * without calling @ref mongocrypt_kms_ctx_fail. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_kms_ctx_status */ bool mongocrypt_kms_ctx_feed_with_retry(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes, bool *should_retry); /** * Indicate a network error. Discards all data fed to this KMS context with @ref mongocrypt_kms_ctx_feed. * The @ref mongocrypt_kms_ctx_t may be reused. * * @param[in] kms The @ref mongocrypt_kms_ctx_t. * @return A boolean indicating whether the failed request may be retried. */ bool mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t *kms); /** * Get the status associated with a @ref mongocrypt_kms_ctx_t object. * * @param[in] kms The @ref mongocrypt_kms_ctx_t object. * @param[out] status Receives the status. * * @returns A boolean indicating success. If false, an error status is set. */ bool mongocrypt_kms_ctx_status(mongocrypt_kms_ctx_t *kms, mongocrypt_status_t *status); /** * 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[in] kms The @ref mongocrypt_kms_ctx_t object. * @param[out] len Receives the length of the returned string. It may be NULL. * If it is not NULL, it is set to the length of the returned string without * the NULL terminator. * * @returns One of the NULL terminated static strings: "aws", "azure", "gcp", or * "kmip". */ const char *mongocrypt_kms_ctx_get_kms_provider(mongocrypt_kms_ctx_t *kms, uint32_t *len); /** * Call when done handling all KMS contexts. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_kms_done(mongocrypt_ctx_t *ctx); /** * Call in response to the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state * to set per-context KMS provider settings. These follow the same format * as @ref mongocrypt_setopt_kms_providers. If no keys are present in the * BSON input, the KMS provider settings configured for the @ref mongocrypt_t * at initialization are used. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] kms_providers_definition A BSON document mapping the KMS provider * names to credentials. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status. */ bool mongocrypt_ctx_provide_kms_providers(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *kms_providers_definition); /** * Perform the final encryption or decryption. * * @param[in] ctx A @ref mongocrypt_ctx_t. * @param[out] out The final BSON. The data viewed by @p out is guaranteed * to be valid until @p ctx is destroyed with @ref mongocrypt_ctx_destroy. * The meaning of this BSON depends on the type of @p ctx. * * If @p ctx was initialized with @ref mongocrypt_ctx_encrypt_init, then * this BSON is the (possibly) encrypted command to send to the server. * * If @p ctx was initialized with @ref mongocrypt_ctx_decrypt_init, then * this BSON is the decrypted result to return to the user. * * If @p ctx was initialized with @ref mongocrypt_ctx_explicit_encrypt_init, * then this BSON has the form { "v": (BSON binary) } where the BSON binary * is the resulting encrypted value. * * If @p ctx was initialized with @ref mongocrypt_ctx_explicit_decrypt_init, * then this BSON has the form { "v": (BSON value) } where the BSON value * is the resulting decrypted value. * * If @p ctx was initialized with @ref mongocrypt_ctx_datakey_init, then * this BSON is the document containing the new data key to be inserted into * the key vault collection. * * If @p ctx was initialized with @ref mongocrypt_ctx_rewrap_many_datakey_init, * then this BSON has the form: * { "v": [{ "_id": ..., "keyMaterial": ..., "masterKey": ... }, ...] } * where each BSON document in the array contains the updated fields of a * rewrapped datakey to be bulk-updated into the key vault collection. * Note: the updateDate field should be updated using the $currentDate operator. * * @returns a bool indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out); /** * Destroy and free all memory associated with a @ref mongocrypt_ctx_t. * * @param[in] ctx A @ref mongocrypt_ctx_t. */ void mongocrypt_ctx_destroy(mongocrypt_ctx_t *ctx); /** * An crypto AES-256-CBC encrypt or decrypt function. * * Note, @p in is already padded. Encrypt with padding disabled. * @param[in] ctx An optional context object that may have been set when hooks * were enabled. * @param[in] key An encryption key (32 bytes for AES_256). * @param[in] iv An initialization vector (16 bytes for AES_256); * @param[in] in The input. * @param[out] out A preallocated byte array for the output. See @ref * mongocrypt_binary_data. * @param[out] bytes_written Set this to the number of bytes written to @p out. * @param[out] status An optional status to pass error messages. See @ref * mongocrypt_status_set. * @returns A boolean indicating success. If returning false, set @p status * with a message indicating the error using @ref mongocrypt_status_set. */ typedef bool (*mongocrypt_crypto_fn)(void *ctx, mongocrypt_binary_t *key, mongocrypt_binary_t *iv, mongocrypt_binary_t *in, mongocrypt_binary_t *out, uint32_t *bytes_written, mongocrypt_status_t *status); /** * A crypto signature or HMAC function. * * Currently used in callbacks for HMAC SHA-512, HMAC SHA-256, and RSA SHA-256 * signature. * * @param[in] ctx An optional context object that may have been set when hooks * were enabled. * @param[in] key An encryption key (32 bytes for HMAC_SHA512). * @param[in] in The input. * @param[out] out A preallocated byte array for the output. See @ref * mongocrypt_binary_data. * @param[out] status An optional status to pass error messages. See @ref * mongocrypt_status_set. * @returns A boolean indicating success. If returning false, set @p status * with a message indicating the error using @ref mongocrypt_status_set. */ typedef bool (*mongocrypt_hmac_fn)(void *ctx, mongocrypt_binary_t *key, mongocrypt_binary_t *in, mongocrypt_binary_t *out, mongocrypt_status_t *status); /** * A crypto hash (SHA-256) function. * * @param[in] ctx An optional context object that may have been set when hooks * were enabled. * @param[in] in The input. * @param[out] out A preallocated byte array for the output. See @ref * mongocrypt_binary_data. * @param[out] status An optional status to pass error messages. See @ref * mongocrypt_status_set. * @returns A boolean indicating success. If returning false, set @p status * with a message indicating the error using @ref mongocrypt_status_set. */ typedef bool (*mongocrypt_hash_fn)(void *ctx, mongocrypt_binary_t *in, mongocrypt_binary_t *out, mongocrypt_status_t *status); /** * A crypto secure random function. * * @param[in] ctx An optional context object that may have been set when hooks * were enabled. * @param[out] out A preallocated byte array for the output. See @ref * mongocrypt_binary_data. * @param[in] count The number of random bytes requested. * @param[out] status An optional status to pass error messages. See @ref * mongocrypt_status_set. * @returns A boolean indicating success. If returning false, set @p status * with a message indicating the error using @ref mongocrypt_status_set. */ typedef bool (*mongocrypt_random_fn)(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status); bool mongocrypt_setopt_crypto_hooks(mongocrypt_t *crypt, mongocrypt_crypto_fn aes_256_cbc_encrypt, mongocrypt_crypto_fn aes_256_cbc_decrypt, mongocrypt_random_fn random, mongocrypt_hmac_fn hmac_sha_512, mongocrypt_hmac_fn hmac_sha_256, mongocrypt_hash_fn sha_256, void *ctx); /** * Set a crypto hook for the AES256-CTR operations. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] aes_256_ctr_encrypt The crypto callback function for encrypt * operation. * @param[in] aes_256_ctr_decrypt The crypto callback function for decrypt * operation. * @param[in] ctx Unused. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status * */ bool mongocrypt_setopt_aes_256_ctr(mongocrypt_t *crypt, mongocrypt_crypto_fn aes_256_ctr_encrypt, mongocrypt_crypto_fn aes_256_ctr_decrypt, void *ctx); /** * Set an AES256-ECB crypto hook for the AES256-CTR operations. If CTR hook was * configured using @ref mongocrypt_setopt_aes_256_ctr, ECB hook will be * ignored. * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] aes_256_ecb_encrypt The crypto callback function for encrypt * operation. * @param[in] ctx Unused. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status * */ bool mongocrypt_setopt_aes_256_ecb(mongocrypt_t *crypt, mongocrypt_crypto_fn aes_256_ecb_encrypt, void *ctx); /** * Set a crypto hook for the RSASSA-PKCS1-v1_5 algorithm with a SHA-256 hash. * * See: https://tools.ietf.org/html/rfc3447#section-8.2 * * Note: this function has the wrong name. It should be: * mongocrypt_setopt_crypto_hook_sign_rsassa_pkcs1_v1_5 * * @param[in] crypt The @ref mongocrypt_t object. * @param[in] sign_rsaes_pkcs1_v1_5 The crypto callback function. * @param[in] sign_ctx A context passed as an argument to the crypto callback * every invocation. * @pre @ref mongocrypt_init has not been called on @p crypt. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status * */ bool mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(mongocrypt_t *crypt, mongocrypt_hmac_fn sign_rsaes_pkcs1_v1_5, void *sign_ctx); /** * @brief Opt-into skipping query analysis. * * If opted in: * - The crypt_shared library will not attempt to be loaded. * - A mongocrypt_ctx_t will never enter the MONGOCRYPT_CTX_NEED_MARKINGS state. * * @param[in] crypt The @ref mongocrypt_t object to update */ void mongocrypt_setopt_bypass_query_analysis(mongocrypt_t *crypt); /** * DEPRECATED: Use of `mongocrypt_setopt_use_range_v2` is deprecated. Range V2 is always enabled. * * @param[in] crypt The @ref mongocrypt_t object. * * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_status */ bool mongocrypt_setopt_use_range_v2(mongocrypt_t *crypt); /** * Set the contention factor used for explicit encryption. * The contention factor is only used for indexed Queryable Encryption. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] contention_factor * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status. */ bool mongocrypt_ctx_setopt_contention_factor(mongocrypt_ctx_t *ctx, int64_t contention_factor); /** * Set the index key id to use for explicit Queryable Encryption. * * If the index key id not set, the key id from @ref * mongocrypt_ctx_setopt_key_id is used. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] 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 * @p key_id with @ref mongocrypt_binary_destroy immediately after. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_index_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id); /** * Set the query type to use for explicit Queryable Encryption. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] query_type The query type string * @param[in] len The length of query_type, or -1 for automatic * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_type, int len); /** * Set options for explicit encryption with the "range" algorithm. * * @p opts is a BSON document of the form: * { * "min": Optional, * "max": Optional, * "sparsity": Optional, * "precision": Optional, * "trimFactor": Optional * } * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] opts BSON. * @pre @p ctx has not been initialized. * @returns A boolean indicating success. If false, an error status is set. * Retrieve it with @ref mongocrypt_ctx_status */ bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts); /** * Set options for explicit encryption with the "textPreview" algorithm. * * NOTE: "textPreview" is experimental only and may be removed in a future non-major release. * @p 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, * . }, * } * * "prefix" and "suffix" can both be set. */ bool mongocrypt_ctx_setopt_algorithm_text(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts); /** * Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set. * * @param[in] ctx The @ref mongocrypt_ctx_t object. * @param[in] cache_expiration_ms The cache expiration time in milliseconds. If zero, the cache * never expires. */ bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms); /// String constants for setopt_query_type // DEPRECATED: Support "rangePreview" has been removed in favor of "range". /// NOTE: "substringPreview" is experimental and may be removed in a future non-major release. /// NOTE: "suffixPreview" is experimental and may be removed in a future non-major release. /// NOTE: "prefixPreview" is experimental and may be removed in a future non-major release. """ ) # End embedding from update_binding.py def _to_string(cdata): """Decode a cdata c-string to a Python str.""" return ffi.string(cdata).decode() def libmongocrypt_version(): """Returns the version of libmongocrypt.""" return _to_string(lib.mongocrypt_version(ffi.NULL)) # Use the PYMONGOCRYPT_LIB environment variable to load a custom libmongocrypt # build without relying on platform specific library path environment # variables, like LD_LIBRARY_PATH. For example: # export PYMONGOCRYPT_LIB='/path/to/libmongocrypt.so' # If the PYMONGOCRYPT_LIB is not set then load the embedded library and # fallback to the relying on a system installed library. _base = Path(os.path.realpath(__file__)).parent if sys.platform == "win32": _path = Path(_base) / "mongocrypt.dll" elif sys.platform == "darwin": _path = Path(_base) / "libmongocrypt.dylib" else: _path = Path(_base) / "libmongocrypt.so" class _Library: """Helper class for delaying errors that would usually be raised at import time until the library is actually used.""" def __init__(self, error): self._error = error def __getattr__(self, name): raise self._error _PYMONGOCRYPT_LIB = os.environ.get("PYMONGOCRYPT_LIB") try: if _PYMONGOCRYPT_LIB: lib = ffi.dlopen(_PYMONGOCRYPT_LIB) else: try: lib = ffi.dlopen(str(_path)) except OSError: # Fallback to libmongocrypt installed on the system. lib = ffi.dlopen("mongocrypt") except OSError as exc: # dlopen raises OSError when the library cannot be found. # Delay the error until the library is actually used. lib = _Library(exc) else: _limongocrypt_version = Version(libmongocrypt_version()) if _limongocrypt_version < Version(_MIN_LIBMONGOCRYPT_VERSION): exc = RuntimeError( f"Expected libmongocrypt version %s or greater, found {_MIN_LIBMONGOCRYPT_VERSION, libmongocrypt_version()}" ) lib = _Library(exc) libmongocrypt-1.19.0/bindings/python/pymongocrypt/compat.py000066400000000000000000000020071521103432300242100ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Utility functions and definitions for Python compatibility.""" import sys PY3 = sys.version_info[0] >= 3 if PY3: from abc import ABC unicode_type = str else: from abc import ABCMeta as _ABCMeta ABC = _ABCMeta("ABC", (object,), {}) unicode_type = "unicode" def str_to_bytes(string): """Convert a str (or unicode) to bytes.""" if isinstance(string, bytes): return string return string.encode("utf-8") libmongocrypt-1.19.0/bindings/python/pymongocrypt/credentials.py000066400000000000000000000012401521103432300252200ustar00rootroot00000000000000# Copyright 2022-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. # Alias file for import compatibility from pymongocrypt.synchronous.credentials import * libmongocrypt-1.19.0/bindings/python/pymongocrypt/crypto.py000066400000000000000000000140231521103432300242460ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Internal crypto callbacks for libmongocrypt.""" import os import traceback from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.hashes import SHA256, SHA512, Hash from cryptography.hazmat.primitives.hmac import HMAC from cryptography.hazmat.primitives.serialization import load_der_private_key from pymongocrypt.binary import _to_bytes, _write_bytes from pymongocrypt.binding import ffi, lib from pymongocrypt.compat import str_to_bytes def _callback_error_handler(exception, exc_value, tb): """Set the mongocrypt_status_t on error.""" # From cffi docs: "First check if traceback is not None (it is None e.g. # if the whole function ran successfully but there was an error converting # the value returned: this occurs after the call)." if tb is not None: status = tb.tb_frame.f_locals["status"] msg = str_to_bytes( "".join(traceback.format_exception(exception, exc_value, tb)) ) lib.mongocrypt_status_set( status, lib.MONGOCRYPT_STATUS_ERROR_CLIENT, 1, msg, -1 ) return False def _aes_256_encrypt(key, mode, input, output, bytes_written): cipher = Cipher(algorithms.AES(_to_bytes(key)), mode, backend=default_backend()) encryptor = cipher.encryptor() data = encryptor.update(_to_bytes(input)) + encryptor.finalize() _write_bytes(output, data) bytes_written[0] = len(data) def _aes_256_decrypt(key, mode, input, output, bytes_written): cipher = Cipher(algorithms.AES(_to_bytes(key)), mode, backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(_to_bytes(input)) + decryptor.finalize() _write_bytes(output, data) bytes_written[0] = len(data) @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *," " mongocrypt_binary_t *, mongocrypt_binary_t *, uint32_t *," " mongocrypt_status_t *)", onerror=_callback_error_handler, ) def aes_256_cbc_encrypt(ctx, key, iv, input, output, bytes_written, status): # Note that libmongocrypt pads the input before calling this method. _aes_256_encrypt(key, modes.CBC(_to_bytes(iv)), input, output, bytes_written) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *," " mongocrypt_binary_t *, mongocrypt_binary_t *, uint32_t *," " mongocrypt_status_t *)", onerror=_callback_error_handler, ) def aes_256_cbc_decrypt(ctx, key, iv, input, output, bytes_written, status): # Note that libmongocrypt pads the input before calling this method. _aes_256_decrypt(key, modes.CBC(_to_bytes(iv)), input, output, bytes_written) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *," " mongocrypt_binary_t *, mongocrypt_binary_t *, uint32_t *," " mongocrypt_status_t *)", onerror=_callback_error_handler, ) def aes_256_ctr_encrypt(ctx, key, iv, input, output, bytes_written, status): _aes_256_encrypt(key, modes.CTR(_to_bytes(iv)), input, output, bytes_written) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *," " mongocrypt_binary_t *, mongocrypt_binary_t *, uint32_t *," " mongocrypt_status_t *)", onerror=_callback_error_handler, ) def aes_256_ctr_decrypt(ctx, key, iv, input, output, bytes_written, status): _aes_256_decrypt(key, modes.CTR(_to_bytes(iv)), input, output, bytes_written) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *, " " mongocrypt_binary_t *, mongocrypt_status_t *)", onerror=_callback_error_handler, ) def hmac_sha_256(ctx, key, input, output, status): h = HMAC(_to_bytes(key), SHA256(), backend=default_backend()) h.update(_to_bytes(input)) data = h.finalize() _write_bytes(output, data) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *, " " mongocrypt_binary_t *, mongocrypt_status_t *)", onerror=_callback_error_handler, ) def hmac_sha_512(ctx, key, input, output, status): h = HMAC(_to_bytes(key), SHA512(), backend=default_backend()) h.update(_to_bytes(input)) data = h.finalize() _write_bytes(output, data) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *, " " mongocrypt_status_t *)", onerror=_callback_error_handler, ) def sha_256(ctx, input, output, status): digest = Hash(SHA256(), backend=default_backend()) digest.update(_to_bytes(input)) data = digest.finalize() _write_bytes(output, data) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, uint32_t, mongocrypt_status_t *)", onerror=_callback_error_handler, ) def secure_random(ctx, output, count, status): data = os.urandom(int(count)) _write_bytes(output, data) return True @ffi.callback( "bool(void *, mongocrypt_binary_t *, mongocrypt_binary_t *, " " mongocrypt_binary_t *, mongocrypt_status_t *)", onerror=_callback_error_handler, ) def sign_rsaes_pkcs1_v1_5(ctx, key, input, output, status): rsa_private_key = load_der_private_key( _to_bytes(key), password=None, backend=default_backend() ) signature = rsa_private_key.sign( _to_bytes(input), padding=padding.PKCS1v15(), algorithm=SHA256() ) _write_bytes(output, signature) return True libmongocrypt-1.19.0/bindings/python/pymongocrypt/errors.py000066400000000000000000000025341521103432300242460ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from pymongocrypt.binding import _to_string, ffi, lib class MongoCryptError(Exception): def __init__(self, msg, code=-1): """Top level Exception for all MongoCrypt errors. :Parameters: - `msg`: An error message. - `code`: The mongocrypt_status_t code. """ super().__init__(msg) self.code = code @classmethod def from_status(cls, status): """Constructs an error from a mongocrypt_status_t. :Parameters: - `status`: A CFFI mongocrypt_status_t. """ if lib.mongocrypt_status_ok(status): raise ValueError("status must not be ok") msg = _to_string(lib.mongocrypt_status_message(status, ffi.NULL)) return cls(msg, lib.mongocrypt_status_code(status)) libmongocrypt-1.19.0/bindings/python/pymongocrypt/explicit_encrypter.py000066400000000000000000000012471521103432300266460ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Alias file for import compatibility from pymongocrypt.synchronous.explicit_encrypter import * libmongocrypt-1.19.0/bindings/python/pymongocrypt/mongocrypt.py000066400000000000000000000662731521103432300251450ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import platform import sys from packaging.version import Version from pymongocrypt.asynchronous.state_machine import AsyncMongoCryptCallback from pymongocrypt.binary import MongoCryptBinaryIn, MongoCryptBinaryOut from pymongocrypt.binding import _to_string, ffi, lib from pymongocrypt.compat import str_to_bytes from pymongocrypt.crypto import ( aes_256_cbc_decrypt, aes_256_cbc_encrypt, aes_256_ctr_decrypt, aes_256_ctr_encrypt, hmac_sha_256, hmac_sha_512, secure_random, sha_256, sign_rsaes_pkcs1_v1_5, ) from pymongocrypt.errors import MongoCryptError from pymongocrypt.options import MongoCryptOptions from pymongocrypt.synchronous.state_machine import MongoCryptCallback class MongoCrypt: def __init__(self, options, callback): """Abstracts libmongocrypt's mongocrypt_t type. :Parameters: - `options`: A :class:`MongoCryptOptions`. - `callback`: A :class:`MongoCryptCallback`. """ self.__opts = options # type: MongoCryptOptions self.__callback = callback self.__crypt = None if not isinstance(options, MongoCryptOptions): raise TypeError("options must be a MongoCryptOptions") if not isinstance(callback, (AsyncMongoCryptCallback, MongoCryptCallback)): raise TypeError( "callback must be a MongoCryptCallback or AsyncMongoCryptCallback" ) self.__crypt = lib.mongocrypt_new() if self.__crypt == ffi.NULL: raise MongoCryptError("unable to create new mongocrypt object") try: self.__init() except Exception: # Destroy the mongocrypt object on error. self.close() raise def __init(self): """Internal init helper.""" kms_providers = self.__opts.kms_providers with MongoCryptBinaryIn(self.__callback.bson_encode(kms_providers)) as kmsopt: if not lib.mongocrypt_setopt_kms_providers(self.__crypt, kmsopt.bin): self.__raise_from_status() schema_map = self.__opts.schema_map if schema_map is not None: with MongoCryptBinaryIn(schema_map) as binary_schema_map: if not lib.mongocrypt_setopt_schema_map( self.__crypt, binary_schema_map.bin ): self.__raise_from_status() encrypted_fields_map = self.__opts.encrypted_fields_map if encrypted_fields_map is not None: with MongoCryptBinaryIn( encrypted_fields_map ) as binary_encrypted_fields_map: if not lib.mongocrypt_setopt_encrypted_field_config_map( self.__crypt, binary_encrypted_fields_map.bin ): self.__raise_from_status() if self.__opts.bypass_query_analysis: lib.mongocrypt_setopt_bypass_query_analysis(self.__crypt) if self.__opts.enable_multiple_collinfo: lib.mongocrypt_setopt_enable_multiple_collinfo(self.__crypt) # Prefer using the native crypto binding when we know it's available. try: crypto_available = lib.mongocrypt_is_crypto_available() except AttributeError: # libmongocrypt < 1.9 crypto_available = False if not crypto_available: if not lib.mongocrypt_setopt_crypto_hooks( self.__crypt, aes_256_cbc_encrypt, aes_256_cbc_decrypt, secure_random, hmac_sha_512, hmac_sha_256, sha_256, ffi.NULL, ): self.__raise_from_status() if not lib.mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5( self.__crypt, sign_rsaes_pkcs1_v1_5, ffi.NULL ): self.__raise_from_status() if not lib.mongocrypt_setopt_aes_256_ctr( self.__crypt, aes_256_ctr_encrypt, aes_256_ctr_decrypt, ffi.NULL ): self.__raise_from_status() elif sys.platform == "darwin" and Version(platform.mac_ver()[0]) < Version( "10.15" ): # MONGOCRYPT-440 libmongocrypt does not support AES-CTR on macOS < 10.15. if not lib.mongocrypt_setopt_aes_256_ctr( self.__crypt, aes_256_ctr_encrypt, aes_256_ctr_decrypt, ffi.NULL ): self.__raise_from_status() if self.__opts.crypt_shared_lib_path is not None: lib.mongocrypt_setopt_set_crypt_shared_lib_path_override( self.__crypt, self.__opts.crypt_shared_lib_path.encode("utf-8") ) if not self.__opts.bypass_encryption: lib.mongocrypt_setopt_append_crypt_shared_lib_search_path( self.__crypt, b"$SYSTEM" ) on_demand_aws = "aws" in kms_providers and not len(kms_providers["aws"]) on_demand_gcp = "gcp" in kms_providers and not len(kms_providers["gcp"]) on_demand_azure = "azure" in kms_providers and not len(kms_providers["azure"]) if any([on_demand_aws, on_demand_gcp, on_demand_azure]): lib.mongocrypt_setopt_use_need_kms_credentials_state(self.__crypt) # Enable KMS retry and key_expiration_ms when available, libmongocrypt >= 1.12.0, try: if not lib.mongocrypt_setopt_retry_kms(self.__crypt, True): self.__raise_from_status() if self.__opts.key_expiration_ms is not None: if not lib.mongocrypt_setopt_key_expiration( self.__crypt, self.__opts.key_expiration_ms ): self.__raise_from_status() except AttributeError: # libmongocrypt < 1.12 pass if not lib.mongocrypt_init(self.__crypt): self.__raise_from_status() if ( self.__opts.crypt_shared_lib_required and self.crypt_shared_lib_version is None ): raise MongoCryptError( "crypt_shared_lib_required=True but the crypt_shared library could not be loaded " f"from crypt_shared_lib_path={self.__opts.crypt_shared_lib_path}" + " or the operating system's dynamic library search path" ) def __raise_from_status(self): status = lib.mongocrypt_status_new() try: lib.mongocrypt_status(self.__crypt, status) exc = MongoCryptError.from_status(status) finally: lib.mongocrypt_status_destroy(status) raise exc @property def crypt_shared_lib_version(self): ver = lib.mongocrypt_crypt_shared_lib_version_string(self.__crypt, ffi.NULL) if ver == ffi.NULL: return None return ver def close(self): """Cleanup resources.""" if self.__crypt is None: return # Since close is called by __del__, we need to be sure to guard # against the case where global variables are set to None at # interpreter shutdown, see PYTHON-3530. if lib is not None: lib.mongocrypt_destroy(self.__crypt) self.__crypt = None def __del__(self): self.close() def _create_context(self): """Returns a new mongocrypt_ctx_t""" ctx = lib.mongocrypt_ctx_new(self.__crypt) if ctx == ffi.NULL: self.__raise_from_status() return ctx def encryption_context(self, database, command): """Creates a context to use for encryption. :Parameters: - `database`: The database name. - `command`: The encoded BSON command to encrypt. :Returns: A :class:`EncryptionContext`. """ return EncryptionContext( self._create_context(), self.__opts.kms_providers, database, command ) def decryption_context(self, command): """Creates a context to use for decryption. :Parameters: - `command`: The encoded BSON command to decrypt. :Returns: A :class:`DecryptionContext`. """ return DecryptionContext( self._create_context(), self.__opts.kms_providers, command ) def explicit_encryption_context(self, value, opts): """Creates a context to use for explicit encryption. :Parameters: - `value`: The encoded document to encrypt, which must be in the form { "v" : BSON value to encrypt }}. - `opts`: A :class:`ExplicitEncryptOpts`. :Returns: A :class:`ExplicitEncryptionContext`. """ return ExplicitEncryptionContext( self._create_context(), self.__opts.kms_providers, value, opts ) def explicit_decryption_context(self, value): """Creates a context to use for explicit decryption. :Parameters: - `value`: The encoded document to decrypt, which must be in the form { "v" : encrypted BSON value }}. :Returns: A :class:`ExplicitDecryptionContext`. """ return ExplicitDecryptionContext( self._create_context(), self.__opts.kms_providers, value ) def data_key_context(self, kms_provider, opts=None): """Creates a context to use for key generation. :Parameters: - `kms_provider`: The KMS provider. - `opts`: An optional class:`DataKeyOpts`. :Returns: A :class:`DataKeyContext`. """ return DataKeyContext( self._create_context(), self.__opts.kms_providers, kms_provider, opts, self.__callback, ) def rewrap_many_data_key_context(self, filter, provider, master_key): """Creates a context to use for rewrapping many data keys. :Parameters: - `filter`: A document used to filter the data keys. - `provider`: (optional) The name of a different kms provider. - `master_key`: Optional document for the given provider. MUST have the fields corresponding to the given provider as specified in master_key. master_key MUST NOT be given if it is not applicable for the given provider. :Returns: A :class:`RewrapManyDataKeyContext`. """ return RewrapManyDataKeyContext( self._create_context(), self.__opts.kms_providers, filter, provider, master_key, self.__callback, ) class MongoCryptContext: __slots__ = ("__ctx", "kms_providers") def __init__(self, ctx, kms_providers): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. """ self.__ctx = ctx self.kms_providers = kms_providers def _close(self): """Cleanup resources.""" if self.__ctx is None: return lib.mongocrypt_ctx_destroy(self.__ctx) self.__ctx = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self._close() @property def state(self): """The current state of the mongocrypt_ctx_t.""" return lib.mongocrypt_ctx_state(self.__ctx) def _raise_from_status(self): status = lib.mongocrypt_status_new() try: lib.mongocrypt_ctx_status(self.__ctx, status) exc = MongoCryptError.from_status(status) finally: lib.mongocrypt_status_destroy(status) raise exc def mongo_operation(self): """Returns the mongo operation to execute as bson bytes.""" with MongoCryptBinaryOut() as binary: if not lib.mongocrypt_ctx_mongo_op(self.__ctx, binary.bin): self._raise_from_status() return binary.to_bytes() def add_mongo_operation_result(self, document): """Adds the mongo operation's command response. :Parameters: - `document`: A raw BSON command response document. """ with MongoCryptBinaryIn(document) as binary: if not lib.mongocrypt_ctx_mongo_feed(self.__ctx, binary.bin): self._raise_from_status() def complete_mongo_operation(self): """Completes the mongo operation.""" if not lib.mongocrypt_ctx_mongo_done(self.__ctx): self._raise_from_status() def provide_kms_providers(self, providers): """Provide a map of KMS providers.""" with MongoCryptBinaryIn(providers) as binary: if not lib.mongocrypt_ctx_provide_kms_providers(self.__ctx, binary.bin): self._raise_from_status() def kms_contexts(self): """Yields the MongoCryptKmsContexts.""" ctx = lib.mongocrypt_ctx_next_kms_ctx(self.__ctx) while ctx != ffi.NULL: yield MongoCryptKmsContext(ctx) ctx = lib.mongocrypt_ctx_next_kms_ctx(self.__ctx) def complete_kms(self): """Indicates that all MongoCryptKmsContexts have been completed""" if not lib.mongocrypt_ctx_kms_done(self.__ctx): self._raise_from_status() def finish(self): """Returns the finished mongo operation as bson bytes.""" with MongoCryptBinaryOut() as binary: if not lib.mongocrypt_ctx_finalize(self.__ctx, binary.bin): self._raise_from_status() return binary.to_bytes() class EncryptionContext(MongoCryptContext): __slots__ = ("database",) def __init__(self, ctx, kms_providers, database, command): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `database`: Optional, the name of the database. - `command`: The BSON command to encrypt. """ super().__init__(ctx, kms_providers) self.database = database try: with MongoCryptBinaryIn(command) as binary: database = str_to_bytes(database) if not lib.mongocrypt_ctx_encrypt_init( ctx, database, len(database), binary.bin ): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise class DecryptionContext(MongoCryptContext): __slots__ = () def __init__(self, ctx, kms_providers, command): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `command`: The encoded BSON command to decrypt. """ super().__init__(ctx, kms_providers) try: with MongoCryptBinaryIn(command) as binary: if not lib.mongocrypt_ctx_decrypt_init(ctx, binary.bin): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise class ExplicitEncryptionContext(MongoCryptContext): __slots__ = () def __init__(self, ctx, kms_providers, value, opts): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `value`: The encoded document to encrypt, which must be in the form { "v" : BSON value to encrypt }}. - `opts`: A :class:`ExplicitEncryptOpts`. """ super().__init__(ctx, kms_providers) try: algorithm = str_to_bytes(opts.algorithm) if not lib.mongocrypt_ctx_setopt_algorithm(ctx, algorithm, -1): self._raise_from_status() if opts.key_id is not None: with MongoCryptBinaryIn(opts.key_id) as binary: if not lib.mongocrypt_ctx_setopt_key_id(ctx, binary.bin): self._raise_from_status() if opts.key_alt_name is not None: with MongoCryptBinaryIn(opts.key_alt_name) as binary: if not lib.mongocrypt_ctx_setopt_key_alt_name(ctx, binary.bin): self._raise_from_status() if opts.query_type is not None: qt = str_to_bytes(opts.query_type) if not lib.mongocrypt_ctx_setopt_query_type(ctx, qt, -1): self._raise_from_status() if opts.contention_factor is not None: if not lib.mongocrypt_ctx_setopt_contention_factor( ctx, opts.contention_factor ): self._raise_from_status() if opts.range_opts is not None: with MongoCryptBinaryIn(opts.range_opts) as range_opts: if not lib.mongocrypt_ctx_setopt_algorithm_range( ctx, range_opts.bin ): self._raise_from_status() if opts.text_opts is not None: with MongoCryptBinaryIn(opts.text_opts) as text_opts: if not lib.mongocrypt_ctx_setopt_algorithm_text(ctx, text_opts.bin): self._raise_from_status() with MongoCryptBinaryIn(value) as binary: if opts.is_expression: if not lib.mongocrypt_ctx_explicit_encrypt_expression_init( ctx, binary.bin ): self._raise_from_status() else: if not lib.mongocrypt_ctx_explicit_encrypt_init(ctx, binary.bin): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise class ExplicitDecryptionContext(MongoCryptContext): __slots__ = () def __init__(self, ctx, kms_providers, value): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `value`: The encoded BSON value to decrypt. """ super().__init__(ctx, kms_providers) try: with MongoCryptBinaryIn(value) as binary: if not lib.mongocrypt_ctx_explicit_decrypt_init(ctx, binary.bin): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise class DataKeyContext(MongoCryptContext): __slots__ = () def __init__(self, ctx, kms_providers, kms_provider, opts, callback): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `kms_provider`: The KMS provider. - `opts`: An optional class:`DataKeyOpts`. - `callback`: A :class:`MongoCryptCallback`. """ super().__init__(ctx, kms_providers) try: if kms_provider not in kms_providers: raise ValueError(f"unknown kms_provider: {kms_provider}") # Account for provider names like "local:myname". provider_type = kms_provider.split(":")[0] if opts is None or opts.master_key is None: if provider_type in ["kmip", "local"]: master_key = {} else: raise ValueError( f"master_key is required for kms_provider: {kms_provider!r}" ) else: master_key = opts.master_key.copy() if provider_type == "aws": if "region" not in master_key or "key" not in master_key: raise ValueError( 'master_key must include "region" and "key" for ' f"kms_provider: {kms_provider!r}" ) elif provider_type == "azure": if "keyName" not in master_key or "keyVaultEndpoint" not in master_key: raise ValueError( 'master key must include "keyName" and ' f'"keyVaultEndpoint" for kms_provider: {kms_provider!r}' ) elif provider_type == "gcp": if ( "projectId" not in master_key or "location" not in master_key or "keyRing" not in master_key or "keyName" not in master_key ): raise ValueError( 'master key must include "projectId", "location",' f'"keyRing", and "keyName" for kms_provider: {kms_provider!r}' ) master_key["provider"] = kms_provider with MongoCryptBinaryIn(callback.bson_encode(master_key)) as mkey: if not lib.mongocrypt_ctx_setopt_key_encryption_key(ctx, mkey.bin): self._raise_from_status() if opts.key_alt_names: for key_alt_name in opts.key_alt_names: with MongoCryptBinaryIn(key_alt_name) as binary: if not lib.mongocrypt_ctx_setopt_key_alt_name(ctx, binary.bin): self._raise_from_status() if opts.key_material: with MongoCryptBinaryIn(opts.key_material) as binary: if not lib.mongocrypt_ctx_setopt_key_material(ctx, binary.bin): self._raise_from_status() if not lib.mongocrypt_ctx_datakey_init(ctx): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise class MongoCryptKmsContext: __slots__ = ("__ctx",) def __init__(self, ctx): """Abstracts libmongocrypt's mongocrypt_kms_ctx_t type. :Parameters: - `ctx`: A mongocrypt_kms_ctx_t. """ self.__ctx = ctx def _close(self): """Clear the mongocrypt_kms_ctx_t.""" self.__ctx = None def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self._close() @property def endpoint(self): """The kms hostname to connect over TLS.""" p = ffi.new("char *[]", 1) try: if not lib.mongocrypt_kms_ctx_endpoint(self.__ctx, p): self.__raise_from_status() return _to_string(p[0]) finally: ffi.release(p) @property def message(self): """The HTTP request message to send to the given endpoint.""" with MongoCryptBinaryOut() as binary: if not lib.mongocrypt_kms_ctx_message(self.__ctx, binary.bin): self.__raise_from_status() return binary.to_bytes() @property def bytes_needed(self): """Indicates how many bytes to send to :meth:`feed`.""" return lib.mongocrypt_kms_ctx_bytes_needed(self.__ctx) @property def kms_provider(self): """The KMS provider identifier associated with this KMS request. :Returns: The KMS provider as a string, eg "aws", "azure", "gcp", or "kmip". .. versionadded:: 1.2 """ return _to_string(lib.mongocrypt_kms_ctx_get_kms_provider(self.__ctx, ffi.NULL)) def feed(self, data): """Feed bytes from the HTTP response. :Parameters: - `data`: The bytes of the HTTP response. Must not exceed :attr:`bytes_needed`. """ with MongoCryptBinaryIn(data) as binary: if not lib.mongocrypt_kms_ctx_feed(self.__ctx, binary.bin): self.__raise_from_status() @property def usleep(self): """Indicates how long to sleep in microseconds before sending this request. .. versionadded:: 1.12 """ try: return lib.mongocrypt_kms_ctx_usleep(self.__ctx) except AttributeError: # libmongocrypt < 1.12 return 0 def fail(self): """Indicate a network-level failure. .. versionadded:: 1.12 """ try: if not lib.mongocrypt_kms_ctx_fail(self.__ctx): self.__raise_from_status() except AttributeError: # libmongocrypt < 1.12 pass def __raise_from_status(self): status = lib.mongocrypt_status_new() try: lib.mongocrypt_kms_ctx_status(self.__ctx, status) exc = MongoCryptError.from_status(status) finally: lib.mongocrypt_status_destroy(status) raise exc class RewrapManyDataKeyContext(MongoCryptContext): __slots__ = () def __init__(self, ctx, kms_providers, filter, provider, master_key, callback): """Abstracts libmongocrypt's mongocrypt_ctx_t type. :Parameters: - `ctx`: A mongocrypt_ctx_t. This MongoCryptContext takes ownership of the underlying mongocrypt_ctx_t. - `kms_providers`: The KMS provider map. - `filter`: The filter to use when finding data keys to rewrap in the key vault collection.. - `provider`: (optional) The name of a different kms provider. - `master_key`: Optional document for the given provider. - `callback`: A :class:`MongoCryptCallback`. """ super().__init__(ctx, kms_providers) key_encryption_key_bson = None if provider is not None: data = dict(provider=provider) if master_key: data.update(master_key) key_encryption_key_bson = callback.bson_encode(data) try: if key_encryption_key_bson: with MongoCryptBinaryIn(key_encryption_key_bson) as binary: if not lib.mongocrypt_ctx_setopt_key_encryption_key( ctx, binary.bin ): self._raise_from_status() filter_bson = callback.bson_encode(filter) with MongoCryptBinaryIn(filter_bson) as binary: if not lib.mongocrypt_ctx_rewrap_many_datakey_init(ctx, binary.bin): self._raise_from_status() except Exception: # Destroy the context on error. self._close() raise libmongocrypt-1.19.0/bindings/python/pymongocrypt/options.py000066400000000000000000000321761521103432300244320ustar00rootroot00000000000000from pymongocrypt.compat import unicode_type class MongoCryptOptions: def __init__( self, kms_providers, schema_map=None, encrypted_fields_map=None, bypass_query_analysis=False, crypt_shared_lib_path=None, crypt_shared_lib_required=False, bypass_encryption=False, key_expiration_ms=None, enable_multiple_collinfo=False, ): """Options for :class:`MongoCrypt`. :Parameters: - `kms_providers`: Map of KMS provider options. The kms_providers map values differ by provider: - `aws`: Map with "accessKeyId" and "secretAccessKey" as strings, and optionally a "sessionToken" for temporary credentials. - `azure`: Map with "clientId" and "clientSecret" as strings. - `gcp`: Map with "email" as a string and "privateKey" as a byte array or a base64-encoded string. - `kmip`: Map with "endpoint" as a string. - `local`: Map with "key" as a 96-byte array or the equivalent base64-encoded string. KMS providers may be specified with an optional name suffix separated by a colon, for example "kmip:name". Named KMS providers do not support automatic credential lookup. - `schema_map`: Optional map of collection namespace ("db.coll") to JSON Schema. By default, a collection's JSONSchema is periodically polled with the listCollections command. But a JSONSchema may be specified locally with the schemaMap option. Supplying a `schema_map` provides more security than relying on JSON Schemas obtained from the server. It protects against a malicious server advertising a false JSON Schema, which could trick the client into sending unencrypted data that should be encrypted. Schemas supplied in the schemaMap only apply to configuring automatic encryption for client side encryption. Other validation rules in the JSON schema will not be enforced by the driver and will result in an error. - `encrypted_fields_map`: Optional map encoded to BSON `bytes`. - `bypass_query_analysis`: If ``True``, disable automatic analysis of outgoing commands. Set `bypass_query_analysis` to use explicit encryption on indexed fields without the MongoDB Enterprise Advanced licensed crypt_shared library. - `crypt_shared_lib_path`: Optional string path to the crypt_shared library. - `crypt_shared_lib_required`: Whether to require a crypt_shared library. - `bypass_encryption`: Whether to bypass encryption. - `key_expiration_ms` (int): The cache expiration time for data encryption keys. Defaults to 60000. 0 means keys never expire. .. versionadded:: 1.13 Added the ``key_expiration_ms`` parameter. .. versionremoved:: 1.11 Removed the ``enable_range_v2`` parameter. .. versionadded:: 1.10 Added the ``enable_range_v2`` parameter. .. versionadded:: 1.3 Added the ``crypt_shared_lib_path``, ``crypt_shared_lib_path``, and ``bypass_encryption`` parameters. .. versionadded:: 1.1 Support for "azure" and "gcp" kms_providers. Support for temporary AWS credentials via "sessionToken". .. versionchanged:: 1.1 For kmsProvider "local", the "key" field can now be specified as either a 96-byte array or the equivalent base64-encoded string. """ if not isinstance(kms_providers, dict): raise ValueError("kms_providers must be a dict") if not kms_providers: raise ValueError("at least one KMS provider must be configured") for name, provider in kms_providers.items(): # Account for provider names like "local:myname". provider_type = name.split(":")[0] if provider_type in ("aws", "gcp", "azure", "kmip", "local"): if not isinstance(provider, dict): raise ValueError(f"kms_providers[{name!r}] must be a dict") if provider_type == "aws": if len(provider): if ( "accessKeyId" not in provider or "secretAccessKey" not in provider ): raise ValueError( f"kms_providers[{name!r}] must contain " "'accessKeyId' and 'secretAccessKey'" ) elif provider_type == "azure": if len(provider): if "clientId" not in provider or "clientSecret" not in provider: raise ValueError( f"kms_providers[{name!r}] must contain " "'clientId' and 'clientSecret'" ) elif provider_type == "gcp": if len(provider): if "email" not in provider or "privateKey" not in provider: raise ValueError( f"kms_providers[{name!r}] must contain " "'email' and 'privateKey'" ) if not isinstance(provider["privateKey"], (bytes, unicode_type)): raise TypeError( f"kms_providers[{name!r}]['privateKey'] must " "be an instance of bytes or str" ) elif provider_type == "kmip": if "endpoint" not in provider: raise ValueError(f"kms_providers[{name!r}] must contain 'endpoint'") if not isinstance(provider["endpoint"], (str, unicode_type)): raise TypeError( f"kms_providers[{name!r}]['endpoint'] must " "be an instance of str" ) elif provider_type == "local": if "key" not in provider: raise ValueError(f"kms_providers[{name!r}] must contain 'key'") if not isinstance(provider["key"], (bytes, unicode_type)): raise TypeError( f"kms_providers[{name!r}]['key'] must be an " "instance of bytes or str" ) if schema_map is not None and not isinstance(schema_map, bytes): raise TypeError("schema_map must be bytes or None") if encrypted_fields_map is not None and not isinstance( encrypted_fields_map, bytes ): raise TypeError("encrypted_fields_map must be bytes or None") if key_expiration_ms is not None: if not isinstance(key_expiration_ms, int): raise TypeError("key_expiration_ms must be int or None") if key_expiration_ms < 0: raise ValueError("key_expiration_ms must be >=0 or None") self.kms_providers = kms_providers self.schema_map = schema_map self.encrypted_fields_map = encrypted_fields_map self.bypass_query_analysis = bypass_query_analysis self.crypt_shared_lib_path = crypt_shared_lib_path self.crypt_shared_lib_required = crypt_shared_lib_required self.bypass_encryption = bypass_encryption self.key_expiration_ms = key_expiration_ms self.enable_multiple_collinfo = enable_multiple_collinfo class ExplicitEncryptOpts: def __init__( self, algorithm, key_id=None, key_alt_name=None, query_type=None, contention_factor=None, range_opts=None, is_expression=False, text_opts=None, ): """Options for explicit encryption. :Parameters: - `algorithm` (str): The algorithm to use. - `key_id`: The data key _id. - `key_alt_name` (bytes): Identifies a key vault document by 'keyAltName'. Must be BSON encoded document in the form: { "keyAltName" : (BSON UTF8 value) } - `query_type` (str): The query type to execute. - `contention_factor` (int): The contention factor to use when the algorithm is "Indexed". - `range_opts` (bytes): Options for explicit encryption with the "range" algorithm encoded as a BSON document. - `is_expression` (boolean): True if this is an encryptExpression() context. Defaults to False. - `text_opts` (bytes): Options for explicit encryption with the "textPreview" algorithm encoded as a BSON document. .. versionchanged:: 1.3 Added the `query_type` and `contention_factor` parameters. .. versionchanged:: 1.5 Added the `range_opts` and `is_expression` parameters. .. versionchanged:: 1.16 Added the `text_opts` parameter. """ self.algorithm = algorithm self.key_id = key_id self.key_alt_name = key_alt_name if query_type is not None: if not isinstance(query_type, str): raise TypeError( f"query_type must be str or None, not: {type(query_type)}" ) self.query_type = query_type if contention_factor is not None and not isinstance(contention_factor, int): raise TypeError( f"contention_factor must be an int or None, not: {type(contention_factor)}" ) self.contention_factor = contention_factor if range_opts is not None and not isinstance(range_opts, bytes): raise TypeError( f"range_opts must be an bytes or None, not: {type(range_opts)}" ) self.range_opts = range_opts self.is_expression = is_expression if text_opts is not None and not isinstance(text_opts, bytes): raise TypeError( f"text_opts must be an bytes or None, not: {type(text_opts)}" ) self.text_opts = text_opts class DataKeyOpts: def __init__(self, master_key=None, key_alt_names=None, key_material=None): """Options for creating encryption keys. :Parameters: - `master_key`: Identifies a KMS-specific key used to encrypt the new data key. If the kmsProvider is "local" the `master_key` is not applicable and may be omitted. If the `kms_provider` is "aws" it is required and has the following fields:: - `region` (string): Required. The AWS region, e.g. "us-east-1". - `key` (string): Required. The Amazon Resource Name (ARN) to the AWS customer. - `endpoint` (string): Optional. An alternate host to send KMS requests to. May include port number, e.g. "kms.us-east-1.amazonaws.com:443". If the `kms_provider` is "azure" it is required and has the following fields:: - `keyVaultEndpoint` (string): Required. Host with optional port, e.g. "example.vault.azure.net". - `keyName` (string): Required. Key name in the key vault. - `keyVersion` (string): Optional. Version of the key to use. If the `kms_provider` is "gcp" it is required and has the following fields:: - `projectId` (string): Required. The Google cloud project ID. - `location` (string): Required. The GCP location, e.g. "us-east1". - `keyRing` (string): Required. Name of the key ring that contains the key to use. - `keyName` (string): Required. Name of the key to use. - `keyVersion` (string): Optional. Version of the key to use. - `endpoint` (string): Optional. Host with optional port. Defaults to "cloudkms.googleapis.com". If the `kms_provider` is "kmip" it is optional and has the following fields:: - `keyId` (string): Optional. `keyId` is the KMIP Unique Identifier to a 96 byte KMIP Secret Data managed object. If keyId is omitted, the driver creates a random 96 byte KMIP Secret Data managed object. - `endpoint` (string): Optional. Host with optional port, e.g. "example.vault.azure.net:". - `key_alt_names`: An optional list of bytes suitable to be passed to mongocrypt_ctx_setopt_key_alt_name. Each element must be BSON encoded document in the form: { "keyAltName" : (BSON UTF8 value) } - `key_material`: An optional binary value of 96 bytes to use as custom key material for the data key being created. If ``key_material`` is given, the custom key material is used for encrypting and decrypting data. Otherwise, the key material for the new data key is generated from a cryptographically secure random device. """ self.master_key = master_key self.key_alt_names = key_alt_names self.key_material = key_material libmongocrypt-1.19.0/bindings/python/pymongocrypt/state_machine.py000066400000000000000000000012421521103432300255310ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Alias file for import compatibility from pymongocrypt.synchronous.state_machine import * libmongocrypt-1.19.0/bindings/python/pymongocrypt/synchronous/000077500000000000000000000000001521103432300247465ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/pymongocrypt/synchronous/auto_encrypter.py000066400000000000000000000040111521103432300303570ustar00rootroot00000000000000# Copyright 2024-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. from pymongocrypt.mongocrypt import MongoCrypt from pymongocrypt.synchronous.state_machine import run_state_machine class AutoEncrypter: def __init__(self, callback, mongo_crypt_opts): """Encrypts and decrypts MongoDB commands. This class is used by a driver to support automatic encryption and decryption of MongoDB commands. :Parameters: - `callback`: A :class:`MongoCryptCallback`. - `mongo_crypt_opts`: A :class:`MongoCryptOptions`. """ self.callback = callback self.mongocrypt = MongoCrypt(mongo_crypt_opts, callback) def encrypt(self, database, cmd): """Encrypt a MongoDB command. :Parameters: - `database`: The database for this command. - `cmd`: A MongoDB command as BSON. :Returns: The encrypted command. """ with self.mongocrypt.encryption_context(database, cmd) as ctx: return run_state_machine(ctx, self.callback) def decrypt(self, response): """Decrypt a MongoDB command response. :Parameters: - `response`: A MongoDB command response as BSON. :Returns: The decrypted command response. """ with self.mongocrypt.decryption_context(response) as ctx: return run_state_machine(ctx, self.callback) def close(self): """Cleanup resources.""" self.mongocrypt.close() self.callback.close() libmongocrypt-1.19.0/bindings/python/pymongocrypt/synchronous/credentials.py000066400000000000000000000125171521103432300276230ustar00rootroot00000000000000# Copyright 2024-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. import os from collections import namedtuple from datetime import datetime, timedelta, timezone try: from pymongo_auth_aws.auth import aws_temp_credentials _HAVE_AUTH_AWS = True except ImportError: _HAVE_AUTH_AWS = False import httpx from pymongocrypt.errors import MongoCryptError _azure_creds = namedtuple("_azure_creds", ["access_token", "expires_utc"]) _azure_creds_cache = None def _get_gcp_credentials(): """Get on-demand GCP credentials""" metadata_host = os.getenv("GCE_METADATA_HOST") or "metadata.google.internal" url = ( "http://%s/computeMetadata/v1/instance/service-accounts/default/token" % metadata_host ) headers = {"Metadata-Flavor": "Google"} client = httpx.Client() try: response = client.get(url, headers=headers) except Exception as e: msg = "unable to retrieve GCP credentials: %s" % e raise MongoCryptError(msg) from e finally: client.close() if response.status_code != 200: msg = f"Unable to retrieve GCP credentials: expected StatusCode 200, got StatusCode: {response.status_code}. Response body:\n{response.content}" raise MongoCryptError(msg) try: data = response.json() except Exception as e: raise MongoCryptError( f"unable to retrieve GCP credentials: error reading response body\n{response.content}" ) from e if not data.get("access_token"): msg = ( "unable to retrieve GCP credentials: got unexpected empty accessToken from GCP Metadata Server. Response body: %s" % response.content ) raise MongoCryptError(msg) return {"accessToken": data["access_token"]} def _get_azure_credentials(): """Get on-demand Azure credentials""" global _azure_creds_cache # Credentials are considered expired when: Expiration - now < 1 mins. creds = _azure_creds_cache if creds: if creds.expires_utc - datetime.now(tz=timezone.utc) < timedelta(seconds=60): _azure_creds_cache = None else: return {"accessToken": creds.access_token} url = "http://169.254.169.254/metadata/identity/oauth2/token" url += "?api-version=2018-02-01" url += "&resource=https://vault.azure.net" headers = {"Metadata": "true", "Accept": "application/json"} client = httpx.Client() try: response = client.get(url, headers=headers) except Exception as e: msg = "Failed to acquire IMDS access token: %s" % e raise MongoCryptError(msg) from e finally: client.close() if response.status_code != 200: msg = "Failed to acquire IMDS access token." raise MongoCryptError(msg) try: data = response.json() except Exception as e: raise MongoCryptError("Azure IMDS response must be in JSON format.") from e for key in ["access_token", "expires_in"]: if not data.get(key): msg = "Azure IMDS response must contain %s, but was %s." msg = msg % (key, response.content) raise MongoCryptError(msg) try: expires_in = int(data["expires_in"]) except ValueError as e: raise MongoCryptError( 'Azure IMDS response must contain "expires_in" integer, but was %s.' % response.content ) from e expires_utc = datetime.now(tz=timezone.utc) + timedelta(seconds=expires_in) _azure_creds_cache = _azure_creds(data["access_token"], expires_utc) return {"accessToken": data["access_token"]} def _ask_for_kms_credentials(kms_providers): """Get on-demand kms credentials. This is a separate function so it can be overridden in unit tests.""" global _azure_creds_cache on_demand_aws = "aws" in kms_providers and not len(kms_providers["aws"]) on_demand_gcp = "gcp" in kms_providers and not len(kms_providers["gcp"]) on_demand_azure = "azure" in kms_providers and not len(kms_providers["azure"]) if not any([on_demand_aws, on_demand_gcp, on_demand_azure]): return {} creds = {} if on_demand_aws: if not _HAVE_AUTH_AWS: raise RuntimeError( "On-demand AWS credentials require pymongo-auth-aws: " "install with: python -m pip install 'pymongo[aws]'" ) aws_creds = aws_temp_credentials() creds_dict = { "accessKeyId": aws_creds.username, "secretAccessKey": aws_creds.password, } if aws_creds.token: creds_dict["sessionToken"] = aws_creds.token creds["aws"] = creds_dict if on_demand_gcp: creds["gcp"] = _get_gcp_credentials() if on_demand_azure: try: creds["azure"] = _get_azure_credentials() except Exception: _azure_creds_cache = None raise return creds libmongocrypt-1.19.0/bindings/python/pymongocrypt/synchronous/explicit_encrypter.py000066400000000000000000000143311521103432300312360ustar00rootroot00000000000000# Copyright 2024-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. from pymongocrypt.mongocrypt import MongoCrypt from pymongocrypt.options import DataKeyOpts, ExplicitEncryptOpts from pymongocrypt.synchronous.state_machine import run_state_machine class ExplicitEncrypter: def __init__(self, callback, mongo_crypt_opts): """Encrypts and decrypts BSON values. This class is used by a driver to support explicit encryption and decryption of individual fields in a BSON document. :Parameters: - `callback`: A :class:`MongoCryptCallback`. - `mongo_crypt_opts`: A :class:`MongoCryptOptions`. """ self.callback = callback if mongo_crypt_opts.schema_map is not None: raise ValueError("mongo_crypt_opts.schema_map must be None") self.mongocrypt = MongoCrypt(mongo_crypt_opts, callback) def create_data_key( self, kms_provider, master_key=None, key_alt_names=None, key_material=None ): """Creates a data key used for explicit encryption. :Parameters: - `kms_provider`: The KMS provider to use. Supported values are "aws", "azure", "gcp", "kmip", "local", or a named provider like "kmip:name". - `master_key`: See class:`DataKeyOpts`. - `key_alt_names` (optional): An optional list of string alternate names used to reference a key. If a key is created with alternate names, then encryption may refer to the key by the unique alternate name instead of by ``_id``. - `key_material`: (optional) See class:`DataKeyOpts`. :Returns: The _id of the created data key document. """ # CDRIVER-3275 each key_alt_name needs to be wrapped in a bson # document. encoded_names = [] if key_alt_names is not None: for name in key_alt_names: encoded_names.append(self.callback.bson_encode({"keyAltName": name})) if key_material is not None: key_material = self.callback.bson_encode({"keyMaterial": key_material}) opts = DataKeyOpts(master_key, encoded_names, key_material) with self.mongocrypt.data_key_context(kms_provider, opts) as ctx: key = run_state_machine(ctx, self.callback) return self.callback.insert_data_key(key) def rewrap_many_data_key(self, filter, provider=None, master_key=None): """Decrypts and encrypts all matching data keys with a possibly new `master_key` value. :Parameters: - `filter`: A document used to filter the data keys. - `provider`: (optional) The name of a different kms provider. - `master_key`: Optional document for the given provider. :Returns: A binary document with the rewrap data. """ with self.mongocrypt.rewrap_many_data_key_context( filter, provider, master_key ) as ctx: return run_state_machine(ctx, self.callback) def encrypt( self, value, algorithm, key_id=None, key_alt_name=None, query_type=None, contention_factor=None, range_opts=None, is_expression=False, text_opts=None, ): """Encrypts a BSON value. Note that exactly one of ``key_id`` or ``key_alt_name`` must be provided. :Parameters: - `value` (bytes): The BSON value to encrypt. - `algorithm` (string): The encryption algorithm to use. See :class:`Algorithm` for some valid options. - `key_id` (bytes): The bytes of the binary subtype 4 ``_id`` data key. For example, ``uuid.bytes`` or ``bytes(bson_binary)``. - `key_alt_name` (string): Identifies a key vault document by 'keyAltName'. - `query_type` (str): The query type to execute. - `contention_factor` (int): The contention factor to use when the algorithm is "Indexed". - `range_opts` (bytes): Options for explicit encryption with the "range" algorithm encoded as a BSON document. - `is_expression` (boolean): True if this is an encryptExpression() context. Defaults to False. - `text_opts` (bytes): Options for explicit encryption with the "textPreview" algorithm encoded as a BSON document. :Returns: The encrypted BSON value. .. versionchanged:: 1.3 Added the `query_type` and `contention_factor` parameters. .. versionchanged:: 1.5 Added the `range_opts` and `is_expression` parameters. .. versionchanged:: 1.16 Added the `text_opts` parameter. """ # CDRIVER-3275 key_alt_name needs to be wrapped in a bson document. if key_alt_name is not None: key_alt_name = self.callback.bson_encode({"keyAltName": key_alt_name}) opts = ExplicitEncryptOpts( algorithm, key_id, key_alt_name, query_type, contention_factor, range_opts, is_expression, text_opts, ) with self.mongocrypt.explicit_encryption_context(value, opts) as ctx: return run_state_machine(ctx, self.callback) def decrypt(self, value): """Decrypts a BSON value. :Parameters: - `value`: The encoded document to decrypt, which must be in the form { "v" : encrypted BSON value }}. :Returns: The decrypted BSON value. """ with self.mongocrypt.explicit_decryption_context(value) as ctx: return run_state_machine(ctx, self.callback) def close(self): """Cleanup resources.""" self.mongocrypt.close() libmongocrypt-1.19.0/bindings/python/pymongocrypt/synchronous/state_machine.py000066400000000000000000000116141521103432300301270ustar00rootroot00000000000000# Copyright 2024-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. from abc import abstractmethod from pymongocrypt.binding import lib from pymongocrypt.compat import ABC from pymongocrypt.errors import MongoCryptError from pymongocrypt.synchronous.credentials import _ask_for_kms_credentials class MongoCryptCallback(ABC): """Callback ABC to perform I/O on behalf of libbmongocrypt.""" @abstractmethod def kms_request(self, kms_context): """Complete a KMS request. :Parameters: - `kms_context`: A :class:`MongoCryptKmsContext`. :Returns: None """ @abstractmethod def collection_info(self, database, filter): """Get the collection info for a namespace. The returned collection info is passed to libmongocrypt which reads the JSON schema. :Parameters: - `database`: The database on which to run listCollections. - `filter`: The filter to pass to listCollections. :Returns: The all or first document from the listCollections command response as BSON. """ @abstractmethod def mark_command(self, database, cmd): """Mark a command for encryption. :Parameters: - `database`: The database on which to run this command. - `cmd`: The BSON command to run. :Returns: The marked command response from mongocryptd. """ @abstractmethod def fetch_keys(self, filter): """Yields one or more keys from the key vault. :Parameters: - `filter`: The filter to pass to find. :Returns: A generator which yields the requested keys from the key vault. """ @abstractmethod def insert_data_key(self, data_key): """Insert a data key into the key vault. :Parameters: - `data_key`: The data key document to insert. :Returns: The _id of the inserted data key document. """ @abstractmethod def bson_encode(self, doc): """Encode a document to BSON. A document can be any mapping type (like :class:`dict`). :Parameters: - `doc`: mapping type representing a document :Returns: The encoded BSON bytes. """ @abstractmethod def close(self): """Release resources.""" def run_state_machine(ctx, callback): """Run the libmongocrypt state machine until completion. :Parameters: - `ctx`: A :class:`MongoCryptContext`. - `callback`: A :class:`MongoCryptCallback`. :Returns: The completed libmongocrypt operation. """ while True: state = ctx.state # Check for terminal states first. if state == lib.MONGOCRYPT_CTX_ERROR: ctx._raise_from_status() elif state == lib.MONGOCRYPT_CTX_READY: return ctx.finish() elif state == lib.MONGOCRYPT_CTX_DONE: return None if state == lib.MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: list_colls_filter = ctx.mongo_operation() coll_info = callback.collection_info(ctx.database, list_colls_filter) if coll_info: if isinstance(coll_info, list): for i in coll_info: ctx.add_mongo_operation_result(i) else: ctx.add_mongo_operation_result(coll_info) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: mongocryptd_cmd = ctx.mongo_operation() result = callback.mark_command(ctx.database, mongocryptd_cmd) ctx.add_mongo_operation_result(result) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS: key_filter = ctx.mongo_operation() for key in callback.fetch_keys(key_filter): ctx.add_mongo_operation_result(key) ctx.complete_mongo_operation() elif state == lib.MONGOCRYPT_CTX_NEED_KMS: for kms_ctx in ctx.kms_contexts(): with kms_ctx: callback.kms_request(kms_ctx) ctx.complete_kms() elif state == lib.MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: creds = _ask_for_kms_credentials(ctx.kms_providers) ctx.provide_kms_providers(callback.bson_encode(creds)) else: raise MongoCryptError(f"unknown state: {state}") libmongocrypt-1.19.0/bindings/python/pymongocrypt/version.py000066400000000000000000000012101521103432300244050ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __version__ = "1.18.2.dev0" _MIN_LIBMONGOCRYPT_VERSION = "1.8.0" libmongocrypt-1.19.0/bindings/python/pyproject.toml000066400000000000000000000104721521103432300225220ustar00rootroot00000000000000[build-system] requires = ["hatchling>1.24","hatch-requirements-txt>=0.4.1"] build-backend = "hatchling.build" [project] name = "pymongocrypt" dynamic = ["version", "dependencies", "optional-dependencies"] description = "Python bindings for libmongocrypt" readme = "README.rst" license = {file="LICENSE"} requires-python = ">=3.9" authors = [ { name = "Shane Harvey", email = "mongodb-user@googlegroups.com" }, ] keywords = [ "bson", "mongo", "mongocrypt", "mongodb", "pymongo", "pymongocrypt", ] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Database", ] [project.urls] Homepage = "https://github.com/mongodb/libmongocrypt/tree/master/bindings/python" [tool.hatch.version] path = "pymongocrypt/version.py" validate-bump = false # Used to call hatch_build.py [tool.hatch.build.hooks.custom] [tool.hatch.metadata.hooks.requirements_txt] files = ["requirements.txt"] [tool.hatch.metadata.hooks.requirements_txt.optional-dependencies] test = ["requirements-test.txt"] [tool.ruff.lint] select = [ "E", "F", "W", # flake8 "B", # flake8-bugbear "I", # isort "ARG", # flake8-unused-arguments "C4", # flake8-comprehensions "EM", # flake8-errmsg "ICN", # flake8-import-conventions "G", # flake8-logging-format "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PT", # flake8-pytest-style "PTH", # flake8-use-pathlib "RET", # flake8-return "RUF", # Ruff-specific "S", # flake8-bandit "SIM", # flake8-simplify "T20", # flake8-print "UP", # pyupgrade "YTT", # flake8-2020 "EXE", # flake8-executable ] ignore = [ "PLR", # Design related pylint codes "E501", # Line too long "PT004", # Use underscore for non-returning fixture (use usefixture instead) "UP007", # Use `X | Y` for type annotation "EM101", # Exception must not use a string literal, assign to variable first "EM102", # Exception must not use an f-string literal, assign to variable first "G004", # Logging statement uses f-string" "UP006", # Use `type` instead of `Type` for type annotation" "RET505", # Unnecessary `elif` after `return` statement" "RET506", # Unnecessary `elif` after `raise` statement "SIM108", # Use ternary operator" "PTH123", # `open()` should be replaced by `Path.open()`" "SIM102", # Use a single `if` statement instead of nested `if` statements "SIM105", # Use `contextlib.suppress(OSError)` instead of `try`-`except`-`pass` "ARG002", # Unused method argument: "S101", # Use of `assert` detected "SIM114", # Combine `if` branches using logical `or` operator "PGH003", # Use specific rule codes when ignoring type issues "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "EM103", # Exception must not use a `.format()` string directly, assign to variable first "C408", # Unnecessary `dict` call (rewrite as a literal) "SIM117", # Use a single `with` statement with multiple contexts instead of nested `with` statements "PLW0603", # Using the global statement to update is discouraged "F403" # Unable to detect undefined names ] unfixable = [ "RUF100", # Unused noqa "T20", # Removes print statements "F841", # Removes unused variables ] [tool.ruff.lint.per-file-ignores] "test/*.py" = ["PT", "E402", "PLW", "SIM", "E741", "PTH", "S", "B904", "E722", "T201", "RET", "ARG", "F405", "B028", "PGH001", "B018", "F403", "RUF015", "E731", "B007", "UP031", "F401", "B023", "F811"] "pymongocrypt/crypto.py" = ["ARG001"] libmongocrypt-1.19.0/bindings/python/requirements-test.txt000066400000000000000000000001251521103432300240410ustar00rootroot00000000000000pymongo[aws]>=4 cffi>=1.12.0,<3 cryptography>=2 pytest>=7.0 unasync respx setuptools libmongocrypt-1.19.0/bindings/python/requirements.txt000066400000000000000000000000771521103432300230720ustar00rootroot00000000000000cffi>=1.12.0,<3 cryptography>=40 packaging>=21.0 httpx>=0.25.0 libmongocrypt-1.19.0/bindings/python/sbom.json000066400000000000000000000042631521103432300214420ustar00rootroot00000000000000{ "components": [ { "bom-ref": "pkg:github/mongodb/libmongocrypt@1.18.2", "externalReferences": [ { "type": "distribution", "url": "https://github.com/mongodb/libmongocrypt/archive/1.18.2.tar.gz" }, { "type": "website", "url": "https://github.com/mongodb/libmongocrypt/tree/1.18.2" } ], "group": "mongodb", "name": "libmongocrypt", "purl": "pkg:github/mongodb/libmongocrypt@1.18.2", "type": "library", "version": "1.18.2" } ], "dependencies": [ { "ref": "pkg:github/mongodb/libmongocrypt@1.18.2" } ], "metadata": { "timestamp": "2026-06-03T00:00:00.000000+00:00", "tools": [ { "externalReferences": [ { "type": "build-system", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" }, { "type": "distribution", "url": "https://pypi.org/project/cyclonedx-python-lib/" }, { "type": "documentation", "url": "https://cyclonedx-python-library.readthedocs.io/" }, { "type": "issue-tracker", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" }, { "type": "license", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" }, { "type": "release-notes", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" }, { "type": "vcs", "url": "https://github.com/CycloneDX/cyclonedx-python-lib" }, { "type": "website", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" } ], "name": "cyclonedx-python-lib", "vendor": "CycloneDX", "version": "6.4.4" } ] }, "serialNumber": "urn:uuid:fb7d4662-fe2d-495c-944f-034982ccc517", "version": 1, "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.5", "vulnerabilities": [] } libmongocrypt-1.19.0/bindings/python/scripts/000077500000000000000000000000001521103432300212715ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/scripts/build-manylinux-wheel.sh000077500000000000000000000003261521103432300260540ustar00rootroot00000000000000#!/bin/bash -ex cd /python mkdir /tmp/wheelhouse /opt/python/cp38-cp38/bin/python -m build --wheel --outdir /tmp/wheelhouse # Audit wheels and repair manylinux tags auditwheel repair /tmp/wheelhouse/*.whl -w dist libmongocrypt-1.19.0/bindings/python/scripts/libmongocrypt-version.txt000066400000000000000000000000071521103432300264020ustar00rootroot000000000000001.18.2 libmongocrypt-1.19.0/bindings/python/scripts/release.sh000077500000000000000000000125671521103432300232630ustar00rootroot00000000000000#!/bin/bash -ex # This script should be run on macOS and Cygwin on Windows. # On macOS it will create the following distributions # pymongocrypt-.tar.gz # pymongocrypt--py3-none-macosx_11_0_universal2.whl # pymongocrypt--py3-none-macosx_10_14_intel.whl # # On Windows it will create the following distribution: # pymongocrypt--py3-none-win_amd64.whl # # If docker is available on Linux or MacOS, it will also produce the following: # pymongocrypt--py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl set -o xtrace # Write all commands first to stderr set -o errexit # Exit the script with error if any of the commands fail SCRIPT_DIR=$(dirname ${BASH_SOURCE:-$0}) # The libmongocrypt git revision release to embed in our wheels. LIBMONGOCRYPT_VERSION=$(cat $SCRIPT_DIR/libmongocrypt-version.txt) REVISION=$(git rev-list -n 1 $LIBMONGOCRYPT_VERSION) # The libmongocrypt release branch. MINOR_VERSION=$(echo $LIBMONGOCRYPT_VERSION | cut -d. -f1,2) BRANCH="r${MINOR_VERSION}" # The python executable to use. PYTHON=${PYTHON:-python} pushd $SCRIPT_DIR/.. # Clean slate. rm -rf dist .venv build libmongocrypt pymongocrypt/*.so pymongocrypt/*.dll pymongocrypt/*.dylib function get_libmongocrypt() { TARGET=$1 MONGOCRYPT_SO=$2 rm -rf build libmongocrypt pymongocrypt/*.so pymongocrypt/*.dll pymongocrypt/*.dylib curl -Lo libmongocrypt.tar.gz https://github.com/mongodb/libmongocrypt/releases/download/${LIBMONGOCRYPT_VERSION}/libmongocrypt-${TARGET}-${LIBMONGOCRYPT_VERSION}.tar.gz curl -Lo libmongocrypt.asc https://github.com/mongodb/libmongocrypt/releases/download/${LIBMONGOCRYPT_VERSION}/libmongocrypt-${TARGET}-${LIBMONGOCRYPT_VERSION}.asc # Download the public key, import it, and verify the signature. # Pre-create ~/.gnupg so GnuPG >= 2.4.1 does not auto-enable keyboxd on # fresh install, as keyboxd is broken on Windows. mkdir -p ~/.gnupg chmod 700 ~/.gnupg touch ~/.gnupg/common.conf gpg --version curl -LO https://pgp.mongodb.com/libmongocrypt.pub gpg --batch --no-tty --import libmongocrypt.pub gpg --batch --no-tty --verify libmongocrypt.asc libmongocrypt.tar.gz mkdir libmongocrypt tar xzf libmongocrypt.tar.gz -C ./libmongocrypt chmod +x ${MONGOCRYPT_SO} cp ${MONGOCRYPT_SO} pymongocrypt/ rm -rf ./libmongocrypt libmongocrypt.tar.gz } function build_wheel() { python -m pip install unasync python -m pip install --upgrade pip build python -m build --wheel rm -rf build libmongocrypt pymongocrypt/*.so pymongocrypt/*.dll pymongocrypt/*.dylib } function build_manylinux_wheel() { python -m pip install unasync docker pull $1 docker run --rm -v `pwd`:/python $1 /python/scripts/build-manylinux-wheel.sh # Sudo is needed to remove the files created by docker. sudo rm -rf build libmongocrypt pymongocrypt/*.so pymongocrypt/*.dll pymongocrypt/*.dylib } function test_dist() { python -m pip uninstall -y pymongocrypt python -m pip install $1 pushd .. python -c "from pymongocrypt.binding import libmongocrypt_version, lib" popd } # Handle Windows dist. if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin $PYTHON -m venv .venv # Workaround https://bugs.python.org/issue32451: # .venv/Scripts/activate: line 3: $'\r': command not found dos2unix .venv/Scripts/activate || true . ./.venv/Scripts/activate # Use crypto-enabled libmongocrypt. get_libmongocrypt windows-x86_64 libmongocrypt/bin/mongocrypt.dll build_wheel test_dist dist/*.whl fi # Handle MacOS dists. if [ "Darwin" = "$(uname -s)" ]; then $PYTHON -m venv .venv . .venv/bin/activate # Build universal2 wheel. get_libmongocrypt macos-universal libmongocrypt/lib/libmongocrypt.dylib export MACOSX_DEPLOYMENT_TARGET=11.0 export _PYTHON_HOST_PLATFORM=macosx-11.0-universal2 build_wheel if [ "$(uname -m)" == "arm64" ]; then test_dist dist/*universal2.whl fi # Build and test sdist. python -m build --sdist test_dist dist/*.tar.gz fi # Handle manylinux dists. if [ $(command -v docker) ]; then if [ "Windows_NT" = "$OS" ]; then # docker: Error response from daemon: Windows does not support privileged mode # would be raised by the qemu command below. echo "Not supported on Windows" exit 0 fi # Set up qemu support using the method used in docker/setup-qemu-action # https://github.com/docker/setup-qemu-action/blob/2b82ce82d56a2a04d2637cd93a637ae1b359c0a7/README.md?plain=1#L46 docker run --rm --privileged tonistiigi/binfmt:latest --install all # Build the manylinux2014 x86_64 wheel. # https://github.com/pypa/manylinux # Supports CentOS 7 rh-python38, CentOS 8 python38, Fedora 32+, Ubuntu 20.04+. # When the rhel7 images go EOL we'll have to switch to the manylinux_x_y variants # and use rhel8. get_libmongocrypt linux-x86_64-glibc_2_7-nocrypto libmongocrypt/lib64/libmongocrypt.so build_manylinux_wheel quay.io/pypa/manylinux2014_x86_64:2023-12-05-e9f0345 if [ "Linux" = "$(uname -s)" ]; then $PYTHON -m venv .venv . .venv/bin/activate test_dist dist/*linux*.whl fi # Build the manylinux_2_28 aarch64 wheel. get_libmongocrypt linux-arm64-glibc_2_17-nocrypto libmongocrypt/lib64/libmongocrypt.so build_manylinux_wheel quay.io/pypa/manylinux_2_28_aarch64:2024-01-01-0e91b08 fi ls -ltr dist popd libmongocrypt-1.19.0/bindings/python/scripts/synchro.py000066400000000000000000000037571521103432300233440ustar00rootroot00000000000000# Copyright 2024-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. import os import sys from os import listdir from pathlib import Path from unasync import Rule, unasync_files replacements = { "asynchronous": "synchronous", "AsyncMongoCryptCallback": "MongoCryptCallback", "AsyncExplicitEncrypter": "ExplicitEncrypter", "AsyncAutoEncrypter": "AutoEncrypter", "AsyncClient": "Client", "AsyncMongoCrypt": "MongoCrypt", "aclose": "close", } ROOT = Path(__file__).absolute().parent.parent _base = ROOT / "pymongocrypt" async_files = [ f"{_base}/asynchronous/{f}" for f in listdir(f"{_base}/asynchronous") if (_base / "asynchronous" / f).is_file() ] unasync_files( async_files, [ Rule( fromdir=f"{_base}/asynchronous/", todir=f"{_base}/synchronous/", additional_replacements=replacements, ) ], ) sync_files = [ f"{_base}/synchronous/{f}" for f in listdir(f"{_base}/synchronous") if (_base / "synchronous" / f).is_file() ] modified_files = [f"./{f}" for f in sys.argv[1:]] for file in sync_files: if file in modified_files and "OVERRIDE_SYNCHRO_CHECK" not in os.environ: raise ValueError(f"Refusing to overwrite {file}") with open(file, "r+") as f: lines = f.readlines() for i in range(len(lines)): for s in replacements: lines[i] = lines[i].replace(s, replacements[s]) f.seek(0) f.truncate() f.writelines(lines) libmongocrypt-1.19.0/bindings/python/scripts/synchro.sh000077500000000000000000000002571521103432300233210ustar00rootroot00000000000000#!/bin/bash set -eu SCRIPT_DIR=$(dirname ${BASH_SOURCE:-$0}) python $SCRIPT_DIR/synchro.py "$@" python -m ruff check $SCRIPT_DIR/../pymongocrypt/synchronous --fix --silent libmongocrypt-1.19.0/bindings/python/scripts/update-version.sh000077500000000000000000000012111521103432300245700ustar00rootroot00000000000000#!/bin/bash set -eu SCRIPT_DIR=$(dirname ${BASH_SOURCE:-$0}) if [ -z "${1:-}" ]; then echo "Provide the new version of libmongocrypt!" exit 1 fi LIBMONGOCRYPT_VERSION=$1 echo $LIBMONGOCRYPT_VERSION > $SCRIPT_DIR/libmongocrypt-version.txt pushd $SCRIPT_DIR/.. if [ $(command -v podman) ]; then DOCKER=podman else DOCKER=docker fi echo "pkg:github/mongodb/libmongocrypt@$LIBMONGOCRYPT_VERSION" > purls.txt $DOCKER run --platform="linux/amd64" -it --rm -v $(pwd):$(pwd) artifactory.corp.mongodb.com/release-tools-container-registry-public-local/silkbomb:2.0 update --purls=$(pwd)/purls.txt -o $(pwd)/sbom.json rm purls.txt popd libmongocrypt-1.19.0/bindings/python/scripts/update_binding.py000066400000000000000000000044571521103432300246310ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Update pymongocrypt/bindings.py using mongocrypt.h. """ import re from pathlib import Path DROP_RE = re.compile(r"^\s*(#|MONGOCRYPT_EXPORT)") HERE = Path(__file__).absolute().parent # itertools.pairwise backport for Python 3.9 support. def pairwise(iterable): # pairwise('ABCDEFG') → AB BC CD DE EF FG iterator = iter(iterable) a = next(iterator, None) for b in iterator: yield a, b a = b def strip_file(content): fold = content.replace("\\\n", " ") all_lines = [*fold.split("\n"), ""] keep_lines = (line for line in all_lines if not DROP_RE.match(line)) fin = "" for line, peek in pairwise(keep_lines): if peek == "" and line == "": # Drop adjacent empty lines continue yield line fin = peek yield fin def update_bindings(): header_file = HERE.parent.parent.parent / "src/mongocrypt.h" with header_file.open(encoding="utf-8") as fp: header_lines = strip_file(fp.read()) target = HERE.parent / "pymongocrypt/binding.py" source_lines = target.read_text().splitlines() new_lines = [] skip = False for line in source_lines: if not skip: new_lines.append(line) if line.strip() == "# Start embedding from update_binding.py": skip = True new_lines.append("ffi.cdef(") new_lines.append('"""') new_lines.extend(header_lines) if line.strip() == "# End embedding from update_binding.py": new_lines.append('"""') new_lines.append(")") new_lines.append(line) skip = False with target.open("w") as f: f.write("\n".join(new_lines)) if __name__ == "__main__": update_bindings() libmongocrypt-1.19.0/bindings/python/test/000077500000000000000000000000001521103432300205615ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/__init__.py000066400000000000000000000015141521103432300226730ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import unittest sys.path[0:0] = [""] try: # Enable the fault handler to dump the traceback of each running thread # after a segfault. import faulthandler faulthandler.enable() except ImportError: pass from unittest import mock libmongocrypt-1.19.0/bindings/python/test/data/000077500000000000000000000000001521103432300214725ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/collection-info.json000066400000000000000000000020621521103432300254510ustar00rootroot00000000000000{ "type": "collection", "name": "test", "idIndex": { "ns": "test.test", "name": "_id_", "key": { "_id": { "$numberInt": "1" } }, "v": { "$numberInt": "2" } }, "options": { "validator": { "$jsonSchema": { "properties": { "ssn": { "encrypt": { "keyId": [ { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } } ], "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } }, "bsonType": "object" } } } } libmongocrypt-1.19.0/bindings/python/test/data/command-reply.json000066400000000000000000000002361521103432300251350ustar00rootroot00000000000000{ "cursor": { "firstBatch": [ { "_id": 1, "ssn": "457-55-5462" } ], "id": 0, "ns": "test.test" }, "ok": 1 } libmongocrypt-1.19.0/bindings/python/test/data/command.json000066400000000000000000000001131521103432300237760ustar00rootroot00000000000000{ "find": "test", "filter": { "ssn": "457-55-5462" } } libmongocrypt-1.19.0/bindings/python/test/data/compact/000077500000000000000000000000001521103432300231205ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/compact/success/000077500000000000000000000000001521103432300245705ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/compact/success/cmd.json000066400000000000000000000000561521103432300262270ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test" } libmongocrypt-1.19.0/bindings/python/test/data/compact/success/encrypted-field-config-map.json000066400000000000000000000024411521103432300325600ustar00rootroot00000000000000{ "db.test": { "escCollection": "esc", "ecocCollection": "ecoc", "fields": [ { "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, "path": "encrypted", "bsonType": "string", "queries": { "queryType": "equality", "contention": 0 } }, { "keyId": { "$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" } }, "path": "nested.encrypted", "bsonType": "string", "queries": { "queryType": "equality", "contention": 0 } }, { "keyId": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEw==", "subType": "04" } }, "path": "nested.notindexed", "bsonType": "string" } ] } } libmongocrypt-1.19.0/bindings/python/test/data/compact/success/encrypted-payload.json000066400000000000000000000010101521103432300310770ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test", "compactionTokens": { "nested.notindexed": { "$binary": { "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=", "subType": "00" } }, "nested.encrypted": { "$binary": { "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", "subType": "00" } }, "encrypted": { "$binary": { "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", "subType": "00" } } } } libmongocrypt-1.19.0/bindings/python/test/data/encrypted-command-reply.json000066400000000000000000000005261521103432300271320ustar00rootroot00000000000000{ "cursor" : { "firstBatch" : [ { "_id": 1, "ssn": { "$binary": "AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=", "$type": "06" } } ], "id" : 0, "ns" : "test.test" }, "ok" : 1 } libmongocrypt-1.19.0/bindings/python/test/data/encrypted-command.json000066400000000000000000000004471521103432300260030ustar00rootroot00000000000000{ "find": "test", "filter": { "ssn": { "$eq": { "$binary": { "base64": "AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=", "subType": "06" } } } } } libmongocrypt-1.19.0/bindings/python/test/data/encrypted-field-config-map.json000066400000000000000000000024121521103432300274600ustar00rootroot00000000000000{ "test.test": { "escCollection": "fle2.basic.esc", "ecocCollection": "fle2.basic.ecoc", "fields": [ { "keyId": { "$binary": { "base64": "KEY1+AAAAAAAAAAAAAAAAA==", "subType": "04" } }, "path": "firstName", "bsonType": "string", "queries": { "queryType": "equality", "contention": { "$numberLong": "0" } } } ] }, "test.test2": { "escCollection": "fle2.basic.esc", "ecocCollection": "fle2.basic.ecoc", "fields": [ { "keyId": { "$binary": { "base64": "KEY2+AAAAAAAAAAAAAAAAA==", "subType": "04" } }, "path": "firstName", "bsonType": "string", "queries": { "queryType": "equality", "contention": { "$numberLong": "0" } } } ] } } libmongocrypt-1.19.0/bindings/python/test/data/encrypted-value.json000066400000000000000000000002461521103432300254760ustar00rootroot00000000000000{ "v": { "$binary": "AWFhYWFhYWFhYWFhYWFhYWECW+zDjR/69eS6VtuMD5+O2lZw6JyiWOw3avI7mnUkdpKzPfvy8F/nlZrgZa2cGmQsb0TmLZuk5trldosnGKD91w==", "$type": "06" } } libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-range-explicit-v2/000077500000000000000000000000001521103432300264765ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-range-explicit-v2/int32/000077500000000000000000000000001521103432300274355ustar00rootroot00000000000000encrypted-payload.json000066400000000000000000000025221521103432300336760ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-range-explicit-v2/int32{ "v": { "$and": [ { "age": { "$gte": { "$binary": { "base64": "DQECAAADcGF5bG9hZACZAQAABGcAhQEAAAMwAH0AAAAFZAAgAAAAAInd0noBhIiJMv8QTjcfgRqnnVhxRJRRACLfvgT+CTR/BXMAIAAAAADm0EjqF/T4EmR6Dw6NaPLrL0OuzS4AFvm90czFluAAygVsACAAAAAA5MXcYWjYlzhPFUDebBEa17B5z2bupmaW9uCdtLjc7RkAAzEAfQAAAAVkACAAAAAA7lkNtT6RLw91aJ07K/blwlFs5wi9pQjqUXDcaCTxe98FcwAgAAAAAPwySffuLQihmF70Ot93KtaUMNU8KpmA+niyPRcvarNMBWwAIAAAAACDv6fJXXwRqwZH3O2kO+hdeLZ36U6bMZSui8kv0PsPtAADMgB9AAAABWQAIAAAAACcMWVTbZC4ox5VdjWeYKLgf4oBjpPlbTTAkucm9JPK0wVzACAAAAAA3tIww4ZTytkxFsUKyJbc3zwQ2w7DhkOqaNvX9g8pi3gFbAAgAAAAAGs9XR3Q1JpxV+HPW8P2GvCuCBF5bGZ8Kl1zHqzZcd5/AAASY20ABAAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAgAAABBzZWNvbmRPcGVyYXRvcgAEAAAAEnNwAAEAAAAAAAAAEHRmAAAAAAAQbW4AAAAAABBteADIAAAAAA==", "subType": "06" } } } }, { "age": { "$lte": { "$binary": { "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA", "subType": "06" } } } } ] } } libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-range-explicit-v2/int32/rangeopts.json000066400000000000000000000003121521103432300323260ustar00rootroot00000000000000{ "min": { "$numberInt": "0" }, "max": { "$numberInt": "200" }, "sparsity": { "$numberLong": "1" }, "trimFactor": { "$numberInt": "0" } } value-to-encrypt.json000066400000000000000000000005761521103432300334770ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-range-explicit-v2/int32{ "v": { "$and": [ { "age": { "$gte": { "$numberInt": "23" } } }, { "age": { "$lte": { "$numberInt": "35" } } } ] } } libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-rangePreview-explicit/000077500000000000000000000000001521103432300275135ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-rangePreview-explicit/int32/000077500000000000000000000000001521103432300304525ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-rangePreview-explicit/int32/rangeopts.json000066400000000000000000000002251521103432300333460ustar00rootroot00000000000000{ "min": { "$numberInt": "0" }, "max": { "$numberInt": "200" }, "sparsity": { "$numberLong": "1" } } value-to-encrypt.json000066400000000000000000000005761521103432300345140ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-find-rangePreview-explicit/int32{ "v": { "$and": [ { "age": { "$gte": { "$numberInt": "23" } } }, { "age": { "$lte": { "$numberInt": "35" } } } ] } } libmongocrypt-1.19.0/bindings/python/test/data/fle2-text-search/000077500000000000000000000000001521103432300245475ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/fle2-text-search/encrypted-payload.json000066400000000000000000000004221521103432300310640ustar00rootroot00000000000000{ "v": { "$binary": "EqQAAAADdHMAhQAAAANlAH0AAAAFZAAgAAAAACmNEIGYF35VPFzmyWuvaCXPAVtXyzAMQ3fdWSFJ3Ji9BXMAIAAAAAA3qH8Y7MnTiDaHYF8L84k4YsWj2IP25sY5lBUo5s2aOgVsACAAAAAAVdUcnSWSe5b9XPo4L/3LELFwsT0PwiNBPYNs3UIhEWQAABJjbQAAAAAAAAAAAAhjZgABCGRmAAEA", "$type": "06" } } libmongocrypt-1.19.0/bindings/python/test/data/fle2-text-search/textopts.json000066400000000000000000000002301521103432300273270ustar00rootroot00000000000000{ "caseSensitive": false, "diacriticSensitive": false, "prefix": { "strMaxQueryLength": 10, "strMinQueryLength": 2 } } libmongocrypt-1.19.0/bindings/python/test/data/key-document-azure.json000066400000000000000000000017401521103432300261170ustar00rootroot00000000000000{ "status": { "$numberInt": "1" }, "_id": { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } }, "masterKey": { "provider": "azure", "keyVaultEndpoint": "example.com", "keyName": "foo" }, "updateDate": { "$date": { "$numberLong": "1557827033449" } }, "keyMaterial": { "$binary": { "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1557827033449" } }, "keyAltNames": ["name1", "name2"] } libmongocrypt-1.19.0/bindings/python/test/data/key-document-gcp.json000066400000000000000000000020041521103432300255340ustar00rootroot00000000000000{ "status": { "$numberInt": "1" }, "_id": { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } }, "masterKey": { "provider": "gcp", "projectId": "foo", "location": "foo", "keyRing": "foo", "keyName": "foo" }, "updateDate": { "$date": { "$numberLong": "1557827033449" } }, "keyMaterial": { "$binary": { "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1557827033449" } }, "keyAltNames": ["name1", "name2"] } libmongocrypt-1.19.0/bindings/python/test/data/key-document.json000066400000000000000000000020261521103432300247710ustar00rootroot00000000000000{ "status": { "$numberInt": "1" }, "_id": { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } }, "masterKey": { "region": "us-east-1", "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "provider": "aws" }, "updateDate": { "$date": { "$numberLong": "1557827033449" } }, "keyMaterial": { "$binary": { "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1557827033449" } }, "keyAltNames": ["name1", "name2"] } libmongocrypt-1.19.0/bindings/python/test/data/key-filter.json000066400000000000000000000003641521103432300244430ustar00rootroot00000000000000{ "$or": [ { "_id": { "$in": [ { "$binary": "YWFhYWFhYWFhYWFhYWFhYQ==", "$type": "04" } ] } }, { "keyAltNames": { "$in": [] } } ] } libmongocrypt-1.19.0/bindings/python/test/data/keys/000077500000000000000000000000001521103432300224455ustar00rootroot0000000000000012345678123498761234123456789012-local-document.json000066400000000000000000000013741521103432300314570ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/keys{ "_id": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEg==", "subType": "04" } }, "keyMaterial": { "$binary": { "base64": "1ZbBTB1i/z4LcmBKi9+nnWqkVB4Wl6P4G7/TFQvXATRF2fX0lhBLIM6rT1U547FX2YgMtaP7sid+jpd4Vhz5kS+UlgtmCFfjeO4qOnJ78KEXRzeIebzKWKQz1pMhYZ3OURDL4wCtNqt3tbSr11kfTADmCMuzgp8U8P8T21RWWBU0f2XDcxiIShYncOS3poKu7GJaPCTav4r3h5h2xRklDA==", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1648914851981" } }, "updateDate": { "$date": { "$numberLong": "1648914851981" } }, "status": { "$numberInt": "0" }, "masterKey": { "provider": "local" } } 12345678123498761234123456789013-local-document.json000066400000000000000000000013741521103432300314600ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/keys{ "_id": { "$binary": { "base64": "EjRWeBI0mHYSNBI0VniQEw==", "subType": "04" } }, "keyMaterial": { "$binary": { "base64": "YQXu48YyDbXvVQ1OhPsodQQNA1gLVWZSV0udYVmCTpVrgyAZePHQmsWWnQzNZj+ZsTxRm02soje/FJCqWGLeth3gKdvIsRg15CDEUOqLdDEpHl46hadosXyJIfo0umZ/LVTkvxRhmDCDxAkd0+Dg4/vWSiG0FgNzGrlvOUsTLGbqWtNMuOdZ8pKAdnFRrqce5cwBGQmd2VVBA2OQ0/IMxQ==", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1650631142512" } }, "updateDate": { "$date": { "$numberLong": "1650631142512" } }, "status": { "$numberInt": "0" }, "masterKey": { "provider": "local" } } ABCDEFAB123498761234123456789012-local-document.json000066400000000000000000000013741521103432300316430ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/data/keys{ "_id": { "$binary": { "base64": "q83vqxI0mHYSNBI0VniQEg==", "subType": "04" } }, "keyMaterial": { "$binary": { "base64": "27OBvUqHAuYFy60nwCdvq2xmZ4kFzVySphXzBGq+HEot13comCoydEfnltBzLTuXLbV9cnREFJIO5f0jMqrlkxIuvAV8yO84p5VJTEa8j/xSNe7iA594rx7UeKT0fOt4VqM47fht8h+8PZYc5JVezvEMvwk115IBCwENxDjLtT0g+y8Hf+aTUEGtxrYToH8zf1/Y7S16mHiIc4jK3/vxHw==", "subType": "00" } }, "creationDate": { "$date": { "$numberLong": "1648915408923" } }, "updateDate": { "$date": { "$numberLong": "1648915408923" } }, "status": { "$numberInt": "0" }, "masterKey": { "provider": "local" } } libmongocrypt-1.19.0/bindings/python/test/data/kms-encrypt-reply.txt000066400000000000000000000011251521103432300256370ustar00rootroot00000000000000HTTP/1.1 200 OK x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e Content-Type: application/x-amz-json-1.1 Content-Length: 446 Connection: close {"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "CiphertextBlob": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHCPOT4UQIpMTvAVABLqnXlAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDLxAm0nO3rccdoWA6AIBEIB7HUe6+aPvgNu/4sLEXBQVDIJVBueI3q7zdOMBSkRKkgZWqEuQgA6iDuEZbhHhOVCUXPBaLX6QWRwyMmjvIy/2Bg5q+TmwnfRo6QKdw2vee1W32/FdPWIoQy1yKOoIhNy6XMWldS3JuWK8ffQOYkssEqx0V4LW6PKuFv7D"}libmongocrypt-1.19.0/bindings/python/test/data/kms-reply-azure.txt000066400000000000000000000007731521103432300253110ustar00rootroot00000000000000HTTP/1.1 200 OK x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e Content-Type: application/x-amz-json-1.1 Content-Length: 374 {"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR", "value": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/bindings/python/test/data/kms-reply-gcp.txt000066400000000000000000000005561521103432300247330ustar00rootroot00000000000000HTTP/1.1 200 OK x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e Content-Type: application/x-amz-json-1.1 Content-Length: 233 {"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/bindings/python/test/data/kms-reply.txt000066400000000000000000000005561521103432300241640ustar00rootroot00000000000000HTTP/1.1 200 OK x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e Content-Type: application/x-amz-json-1.1 Content-Length: 233 {"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "Plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/bindings/python/test/data/list-collections-filter.json000066400000000000000000000000251521103432300271340ustar00rootroot00000000000000{ "name": "test" } libmongocrypt-1.19.0/bindings/python/test/data/mongocryptd-command.json000066400000000000000000000012641521103432300263510ustar00rootroot00000000000000{ "find": "test", "filter": { "ssn": "457-55-5462" }, "jsonSchema": { "properties": { "ssn": { "encrypt": { "keyId": [ { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } } ], "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } }, "bsonType": "object" }, "isRemoteSchema": true } libmongocrypt-1.19.0/bindings/python/test/data/mongocryptd-reply.json000066400000000000000000000007501521103432300260650ustar00rootroot00000000000000{ "schemaRequiresEncryption": true, "ok": { "$numberInt": "1" }, "result": { "find": "test", "filter": { "ssn": { "$eq": { "$binary": { "base64": "ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA", "subType": "06" } } } } }, "hasEncryptedPlaceholders": true } libmongocrypt-1.19.0/bindings/python/test/data/schema-map.json000066400000000000000000000015051521103432300244010ustar00rootroot00000000000000{ "test.test": { "properties": { "ssn": { "encrypt": { "keyId": [ { "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } } ], "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" } } }, "bsonType": "object" }, "test.test2": { "properties": { "ssn": { "encrypt": { "keyId": [ { "$binary": { "base64": "AAAAAAAAAAAAAAAAAAAAAA==", "subType": "04" } } ], "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" } } }, "bsonType": "object" } } libmongocrypt-1.19.0/bindings/python/test/performance/000077500000000000000000000000001521103432300230625ustar00rootroot00000000000000libmongocrypt-1.19.0/bindings/python/test/performance/keyDocument.json000066400000000000000000000011321521103432300262410ustar00rootroot00000000000000{ "_id": { "$binary": { "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", "subType": "04" } }, "keyMaterial": { "$binary": { "base64": "ACR7Hm33dDOAAD7l2ubZhSpSUWK8BkALUY+qW3UgBAEcTV8sBwZnaAWnzDsmrX55dgmYHWfynDlJogC/e33u6pbhyXvFTs5ow9OLCuCWBJ39T/Ivm3kMaZJybkejY0V+uc4UEdHvVVz/SbitVnzs2WXdMGmo1/HmDRrxGYZjewFslquv8wtUHF5pyB+QDlQBd/al9M444/8bJZFbMSmtIg==", "subType": "00" } }, "creationDate": { "$date": "2023-08-21T14:28:20.875Z" }, "updateDate": { "$date": "2023-08-21T14:28:20.875Z" }, "status": 0, "masterKey": { "provider": "local" } } libmongocrypt-1.19.0/bindings/python/test/performance/perf_test.py000066400000000000000000000130601521103432300254270ustar00rootroot00000000000000# Copyright 2023-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. """Benchmark pymongocrypt performance.""" from __future__ import annotations import os import sys import time import unittest from concurrent.futures import ThreadPoolExecutor try: import simplejson as json except ImportError: import json # type: ignore[no-redef] import bson from bson import json_util sys.path[0:0] = [""] from test.test_mongocrypt import MockCallback from pymongocrypt.binding import lib, libmongocrypt_version from pymongocrypt.mongocrypt import MongoCrypt, MongoCryptOptions from pymongocrypt.synchronous.explicit_encrypter import ExplicitEncrypter from pymongocrypt.version import __version__ NUM_ITERATIONS = 10 MAX_TIME = 1 NUM_FIELDS = 1500 LOCAL_MASTER_KEY = ( b"\x9d\x94K\r\x93\xd0\xc5D\xa5r\xfd2\x1b\x940\x90#5s|\xf0\xf6\xc2\xf4\xda#V\xe7\x8f\x04" b"\xcc\xfa\xdeu\xb4Q\x87\xf3\x8b\x97\xd7KD;\xac9\xa2\xc6M\x91\x00>\xd1\xfaJ0\xc1\xd2" b"\xc6^\xfb\xacA\xf2H\x13<\x9bP\xfc\xa7$z.\x02c\xa3\xc6\x16%QPx>\x0f\xd8n\x84\xa6\xec" b"\x8d-$G\xe5\xaf" ) OUTPUT_FILE = os.environ.get("OUTPUT_FILE") result_data: list = [] def read(filename, **kwargs): with open(os.path.join(os.path.dirname(__file__), filename), **kwargs) as fp: return fp.read() def json_data(filename): return json_util.loads(read(filename)) def bson_data(filename): return bson.encode(json_data(filename)) def tearDownModule(): output = json.dumps(result_data, indent=4) if OUTPUT_FILE: with open(OUTPUT_FILE, "w") as opf: opf.write(output) else: print(output) class TestBulkDecryption(unittest.TestCase): def setUp(self): opts = MongoCryptOptions({"local": {"key": LOCAL_MASTER_KEY}}) callback = MockCallback(key_docs=[bson_data("keyDocument.json")]) self.mongocrypt = MongoCrypt(opts, callback) self.encrypter = ExplicitEncrypter(callback, opts) self.addCleanup(self.mongocrypt.close) self.addCleanup(self.encrypter.close) def do_task(self, encrypted, duration=MAX_TIME): start = time.monotonic() ops = 0 while time.monotonic() - start < duration: with self.mongocrypt.decryption_context(encrypted) as ctx: if ctx.state == lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS: # Key is requested on the first operation, then expected to be cached for one minute. ctx.add_mongo_operation_result(bson_data("keyDocument.json")) ctx.complete_mongo_operation() self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_READY) decrypted = ctx.finish() ops += 1 # Assert that decryption actually occurred. self.assertGreater(ops, 0) doc = bson.decode(decrypted) for val in doc.values(): self.assertIsInstance(val, str) return ops def percentile(self, percentile): sorted_results = sorted(self.results) percentile_index = int(len(sorted_results) * percentile / 100) - 1 return sorted_results[percentile_index] def runTest(self): doc = {} key_id = json_data("keyDocument.json")["_id"] for i in range(NUM_FIELDS): val = f"value {i:04}" val_encrypted = bson.decode( self.encrypter.encrypt( bson.encode({"v": val}), "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", key_id=key_id, ) )["v"] doc[f"key{i:04}"] = val_encrypted encrypted = bson.encode(doc) # Warm up benchmark and discard the result. self.do_task(encrypted, duration=2) for n_threads in [1, 2, 8, 64]: with ThreadPoolExecutor(max_workers=n_threads) as executor: self.results = [] for _ in range(NUM_ITERATIONS): start = time.monotonic() thread_results = list( executor.map(self.do_task, [encrypted] * n_threads) ) interval = time.monotonic() - start self.results.append(sum(thread_results) / interval) median = self.percentile(50) print( f"Finished {self.__class__.__name__}, threads={n_threads}, median ops_per_second={median:.2f}" ) # Remove "Test" so that TestBulkDecryption is reported as "BulkDecryption". name = self.__class__.__name__[4:] result_data.append( { "info": { "test_name": name, "args": { "threads": n_threads, }, }, "metrics": [ {"name": "ops_per_second", "type": "MEDIAN", "value": median}, ], } ) if __name__ == "__main__": print( f"Running benchmark with pymongocrypt: {__version__} libmongocrypt: {libmongocrypt_version()}" ) unittest.main() libmongocrypt-1.19.0/bindings/python/test/test_binding.py000066400000000000000000000044261521103432300236120ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test the binding module.""" import sys sys.path[0:0] = [""] from test import unittest import pymongocrypt from pymongocrypt.binding import _parse_version, ffi, lib class TestBinding(unittest.TestCase): def assertVersionLike(self, version): self.assertTrue(isinstance(version, str), msg=version) # There should be at least one dot: "1.0" or "1.0.0" not "1". self.assertGreaterEqual(len(version.split(".")), 2, msg=version) def test_pymongocrypt_version(self): self.assertVersionLike(pymongocrypt.__version__) def test_libmongocrypt_version(self): self.assertVersionLike(pymongocrypt.libmongocrypt_version()) def test_mongocrypt_new(self): data = lib.mongocrypt_new() self.assertNotEqual(data, ffi.NULL) lib.mongocrypt_destroy(data) def test_mongocrypt_binary_new(self): data = lib.mongocrypt_binary_new() self.assertNotEqual(data, ffi.NULL) lib.mongocrypt_binary_destroy(data) def test_mongocrypt_status_new(self): data = lib.mongocrypt_status_new() self.assertNotEqual(data, ffi.NULL) lib.mongocrypt_status_destroy(data) def test_parse_version(self): # Dev versions, betas, RCs should be less than stable releases. for v in ("1.1.0-beta1", "1.1.0-b2", "1.1.0-rc1", "1.1.0-beta1", "1.1.0-pre1"): self.assertLess(_parse_version(v), _parse_version("1.1.0")) # Dev versions should parse correctly. _parse_version("1.1.0-beta1+20201102git80202647fc") # Hyphenation in patch version should be disregarded. self.assertEqual(_parse_version("1.1.0-beta1"), _parse_version("1.1.0beta1")) if __name__ == "__main__": unittest.main() libmongocrypt-1.19.0/bindings/python/test/test_crypto.py000066400000000000000000000065651521103432300235260ustar00rootroot00000000000000# Copyright 2020-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. """Test the crypto module.""" import sys sys.path[0:0] = [""] import base64 from test import unittest from pymongocrypt.binary import MongoCryptBinaryIn from pymongocrypt.binding import ffi, lib from pymongocrypt.crypto import sign_rsaes_pkcs1_v1_5 class TestCrypto(unittest.TestCase): def test_sign_rsaes_pkcs1_v1_5(self): key_b64 = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4JOyv5z05cL18ztpknRC7CFY2gYol4DAKerdVUoDJxCTmFMf39dVUEqD0WDiw/qcRtSO1/FRut08PlSPmvbyKetsLoxlpS8lukSzEFpFK7+L+R4miFOl6HvECyg7lbC1H/WGAhIz9yZRlXhRo9qmO/fB6PV9IeYtU+1xYuXicjCDPp36uuxBAnCz7JfvxJ3mdVc0vpSkbSb141nWuKNYR1mgyvvL6KzxO6mYsCo4hRAdhuizD9C4jDHk0V2gDCFBk0h8SLEdzStX8L0jG90/Og4y7J1b/cPo/kbYokkYisxe8cPlsvGBf+rZex7XPxc1yWaP080qeABJb+S88O//LAgMBAAECggEBAKVxP1m3FzHBUe2NZ3fYCc0Qa2zjK7xl1KPFp2u4CU+9sy0oZJUqQHUdm5CMprqWwIHPTftWboFenmCwrSXFOFzujljBO7Z3yc1WD3NJl1ZNepLcsRJ3WWFH5V+NLJ8Bdxlj1DMEZCwr7PC5+vpnCuYWzvT0qOPTl9RNVaW9VVjHouJ9Fg+s2DrShXDegFabl1iZEDdI4xScHoYBob06A5lw0WOCTayzw0Naf37lM8Y4psRAmI46XLiF/Vbuorna4hcChxDePlNLEfMipICcuxTcei1RBSlBa2t1tcnvoTy6cuYDqqImRYjp1KnMKlKQBnQ1NjS2TsRGm+F0FbreVCECgYEA4IDJlm8q/hVyNcPe4OzIcL1rsdYN3bNm2Y2O/YtRPIkQ446ItyxD06d9VuXsQpFp9jNACAPfCMSyHpPApqlxdc8z/xATlgHkcGezEOd1r4E7NdTpGg8y6Rj9b8kVlED6v4grbRhKcU6moyKUQT3+1B6ENZTOKyxuyDEgTwZHtFECgYEA0fqdv9h9s77d6eWmIioP7FSymq93pC4umxf6TVicpjpMErdD2ZfJGulN37dq8FOsOFnSmFYJdICj/PbJm6p1i8O21lsFCltEqVoVabJ7/0alPfdG2U76OeBqI8ZubL4BMnWXAB/VVEYbyWCNpQSDTjHQYs54qa2I0dJB7OgJt1sCgYEArctFQ02/7H5Rscl1yo3DBXO94SeiCFSPdC8f2Kt3MfOxvVdkAtkjkMACSbkoUsgbTVqTYSEOEc2jTgR3iQ13JgpHaFbbsq64V0QP3TAxbLIQUjYGVgQaF1UfLOBv8hrzgj45z/ST/G80lOl595+0nCUbmBcgG1AEWrmdF0/3RmECgYAKvIzKXXB3+19vcT2ga5Qq2l3TiPtOGsppRb2XrNs9qKdxIYvHmXo/9QP1V3SRW0XoD7ez8FpFabp42cmPOxUNk3FK3paQZABLxH5pzCWI9PzIAVfPDrm+sdnbgG7vAnwfL2IMMJSA3aDYGCbF9EgefG+STcpfqq7fQ6f5TBgLFwKBgCd7gn1xYL696SaKVSm7VngpXlczHVEpz3kStWR5gfzriPBxXgMVcWmcbajRser7ARpCEfbxM1UJyv6oAYZWVSNErNzNVb4POqLYcCNySuC6xKhs9FrEQnyKjyk8wI4VnrEMGrQ8e+qYSwYk9Gh6dKGoRMAPYVXQAO0fIsHF/T0a" ciphertext_b64 = "VocBRhpMmQ2XCzVehWSqheQLnU889gf3dhU4AnVnQTJjsKx/CM23qKDPkZDd2A/BnQsp99SN7ksIX5Raj0TPwyN5OCN/YrNFNGoOFlTsGhgP/hyE8X3Duiq6sNO0SMvRYNPFFGlJFsp1Fw3Z94eYMg4/Wpw5s4+Jo5Zm/qY7aTJIqDKDQ3CNHLeJgcMUOc9sz01/GzoUYKDVODHSxrYEk5ireFJFz9vP8P7Ha+VDUZuQIQdXer9NBbGFtYmWprY3nn4D3Dw93Sn0V0dIqYeIo91oKyslvMebmUM95S2PyIJdEpPb2DJDxjvX/0LLwSWlSXRWy9gapWoBkb4ynqZBsg==" value = b"data to sign" with MongoCryptBinaryIn(b"1" * 256) as output, MongoCryptBinaryIn( base64.b64decode(key_b64) ) as key, MongoCryptBinaryIn(value) as value: retval = sign_rsaes_pkcs1_v1_5( ffi.NULL, key.bin, value.bin, output.bin, lib.mongocrypt_status_new() ) self.assertTrue(retval) self.assertEqual(output.to_bytes(), base64.b64decode(ciphertext_b64)) if __name__ == "__main__": unittest.main() libmongocrypt-1.19.0/bindings/python/test/test_mongocrypt.py000066400000000000000000001720521521103432300244020ustar00rootroot00000000000000# Copyright 2019-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test the mongocrypt module.""" import base64 import copy import os import sys import bson import httpx from bson import json_util from bson.binary import Binary, UuidRepresentation from bson.codec_options import CodecOptions from bson.json_util import JSONOptions from bson.raw_bson import RawBSONDocument from bson.son import SON import pymongocrypt.mongocrypt from pymongocrypt.binary import MongoCryptBinaryIn, MongoCryptBinaryOut from pymongocrypt.options import MongoCryptOptions sys.path[0:0] = [""] import unittest import unittest.mock import respx from pymongo import MongoClient from pymongo_auth_aws.auth import AwsCredential from pymongocrypt.asynchronous.auto_encrypter import AsyncAutoEncrypter from pymongocrypt.asynchronous.explicit_encrypter import AsyncExplicitEncrypter from pymongocrypt.asynchronous.state_machine import AsyncMongoCryptCallback from pymongocrypt.binding import lib from pymongocrypt.compat import PY3, unicode_type from pymongocrypt.errors import MongoCryptError from pymongocrypt.mongocrypt import MongoCrypt from pymongocrypt.synchronous.auto_encrypter import AutoEncrypter from pymongocrypt.synchronous.explicit_encrypter import ExplicitEncrypter from pymongocrypt.synchronous.state_machine import MongoCryptCallback # Data for testing libbmongocrypt binding. DATA_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "data")) def to_base64(data): b64 = base64.b64encode(data) if not PY3: return unicode_type(b64) return b64.decode("utf-8") class TestMongoCryptBinary(unittest.TestCase): def test_mongocrypt_binary_in(self): with MongoCryptBinaryIn(b"1\x0023") as binary: self.assertIsNotNone(binary.bin) self.assertEqual(binary.to_bytes(), b"1\x0023") self.assertIsNone(binary.bin) with MongoCryptBinaryIn(b"") as binary: self.assertIsNotNone(binary.bin) self.assertEqual(binary.to_bytes(), b"") self.assertIsNone(binary.bin) # Memoryview with MongoCryptBinaryIn(memoryview(b"1\x0023")) as binary: self.assertIsNotNone(binary.bin) self.assertEqual(binary.to_bytes(), b"1\x0023") self.assertIsNone(binary.bin) def test_mongocrypt_binary_out(self): with MongoCryptBinaryOut() as binary: self.assertIsNotNone(binary.bin) self.assertEqual(binary.to_bytes(), b"") self.assertIsNone(binary.bin) class TestMongoCryptOptions(unittest.TestCase): def test_mongocrypt_options(self): schema_map = bson_data("schema-map.json") valid = [ ({"local": {"key": b"1" * 96}}, None), ({"aws": {}}, schema_map), ({"aws": {"accessKeyId": "", "secretAccessKey": ""}}, schema_map), ({"aws": {"accessKeyId": "foo", "secretAccessKey": "foo"}}, None), ( { "aws": { "accessKeyId": "foo", "secretAccessKey": "foo", "sessionToken": "token", } }, None, ), ( { "aws": {"accessKeyId": "foo", "secretAccessKey": "foo"}, "local": {"key": b"1" * 96}, }, None, ), ({"local": {"key": to_base64(b"1" * 96)}}, None), ({"local": {"key": Binary(b"1" * 96)}}, None), ({"azure": {}}, None), ({"azure": {"clientId": "foo", "clientSecret": "bar"}}, None), ({"gcp": {}}, None), ({"gcp": {"email": "foo@bar.baz", "privateKey": b"1"}}, None), ({"gcp": {"email": "foo@bar.baz", "privateKey": to_base64(b"1")}}, None), ({"gcp": {"email": "foo@bar.baz", "privateKey": Binary(b"1")}}, None), ({"kmip": {"endpoint": "localhost"}}, None), ] # Add tests for named KMS providers. for kms_providers, schema_map in valid: for name, val in list(kms_providers.items()): kms_providers[f"{name}:named"] = val for kms_providers, schema_map in valid: opts = MongoCryptOptions(kms_providers, schema_map) self.assertEqual(opts.kms_providers, kms_providers, msg=kms_providers) self.assertEqual(opts.schema_map, schema_map) self.assertIsNone(opts.encrypted_fields_map) self.assertFalse(opts.bypass_query_analysis) encrypted_fields_map = bson_data("encrypted-field-config-map.json") opts = MongoCryptOptions( valid[0][0], schema_map, encrypted_fields_map=encrypted_fields_map, bypass_query_analysis=True, ) self.assertEqual(opts.encrypted_fields_map, encrypted_fields_map) self.assertTrue(opts.bypass_query_analysis) for expiration in [0, 1, 1000000]: opts = MongoCryptOptions( valid[0][0], schema_map, key_expiration_ms=expiration ) self.assertEqual(opts.key_expiration_ms, expiration) def test_mongocrypt_options_validation(self): with self.assertRaisesRegex( ValueError, "at least one KMS provider must be configured" ): MongoCryptOptions({}) for invalid_kms_providers in [ {"aws": {"accessKeyId": "foo"}}, {"aws": {"secretAccessKey": "foo"}}, {"aws:foo": {"accessKeyId": "foo"}}, {"aws:foo": {"secretAccessKey": "foo"}}, ]: name = next(iter(invalid_kms_providers)) with self.assertRaisesRegex( ValueError, rf"kms_providers\[{name!r}\] must contain " "'accessKeyId' and 'secretAccessKey'", ): MongoCryptOptions(invalid_kms_providers) with self.assertRaisesRegex( TypeError, r"kms_providers\['local'\]\['key'\] must be an " r"instance of bytes or str", ): MongoCryptOptions({"local": {"key": None}}) with self.assertRaisesRegex( TypeError, r"kms_providers\['gcp'\]\['privateKey'\] must be an " r"instance of bytes or str", ): MongoCryptOptions({"gcp": {"email": "foo@bar.baz", "privateKey": None}}) with self.assertRaisesRegex( ValueError, r"kms_providers\['kmip'\] must contain 'endpoint'" ): MongoCryptOptions({"kmip": {}}) with self.assertRaisesRegex( TypeError, r"kms_providers\['kmip'\]\['endpoint'\] must be an instance of str", ): MongoCryptOptions({"kmip": {"endpoint": None}}) valid_kms = {"aws": {"accessKeyId": "", "secretAccessKey": ""}} with self.assertRaisesRegex(TypeError, "schema_map must be bytes or None"): MongoCryptOptions(valid_kms, schema_map={}) with self.assertRaisesRegex( TypeError, "encrypted_fields_map must be bytes or None" ): MongoCryptOptions(valid_kms, encrypted_fields_map={}) with self.assertRaisesRegex(TypeError, "key_expiration_ms must be int or None"): MongoCryptOptions(valid_kms, key_expiration_ms="123") with self.assertRaisesRegex( ValueError, "key_expiration_ms must be >=0 or None" ): MongoCryptOptions(valid_kms, key_expiration_ms=-1) class TestMongoCrypt(unittest.TestCase): maxDiff = None def test_mongocrypt(self): kms_providers = {"aws": {"accessKeyId": "foo", "secretAccessKey": "foo"}} opts = MongoCryptOptions(kms_providers) mc = MongoCrypt(opts, MockCallback()) mc.close() mc.close() def test_mongocrypt_aws_session_token(self): kms_providers = { "aws": { "accessKeyId": "foo", "secretAccessKey": "foo", "sessionToken": "token", } } opts = MongoCryptOptions(kms_providers) mc = MongoCrypt(opts, MockCallback()) mc.close() def test_mongocrypt_validation(self): callback = MockCallback() options = MongoCryptOptions({"local": {"key": b"\x00" * 96}}) with self.assertRaisesRegex(TypeError, "options must be a MongoCryptOptions"): MongoCrypt({}, callback) with self.assertRaisesRegex(TypeError, "options must be a MongoCryptOptions"): MongoCrypt(None, callback) with self.assertRaisesRegex( TypeError, "callback must be a MongoCryptCallback or AsyncMongoCryptCallback", ): MongoCrypt(options, {}) with self.assertRaisesRegex( TypeError, "callback must be a MongoCryptCallback or AsyncMongoCryptCallback", ): MongoCrypt(options, None) invalid_key_len_opts = MongoCryptOptions({"local": {"key": b"1"}}) with self.assertRaisesRegex(MongoCryptError, "local key must be 96 bytes"): MongoCrypt(invalid_key_len_opts, callback) def test_setopt_kms_provider_base64_or_bytes(self): test_fields = [("local", "key"), ("gcp", "privateKey")] callback = MockCallback() base_kms_dict = { "local": {"key": b"\x00" * 96}, "gcp": {"email": "foo@bar.baz", "privateKey": b"\x00"}, } for f1, f2 in test_fields: kms_dict = copy.deepcopy(base_kms_dict) # Case 1: pass key as string containing bytes (valid) kms_dict[f1][f2] = b"\x00" * 96 options = MongoCryptOptions(kms_dict) mc = MongoCrypt(options, callback) mc.close() # Case 2: pass key as base64-encoded unicode literal (valid) kms_dict[f1][f2] = to_base64(b"\x00" * 96) options = MongoCryptOptions(kms_dict) mc = MongoCrypt(options, callback) mc.close() # Case 3: pass key as unicode string containing bytes (invalid) kms_dict[f1][f2] = unicode_type(b"\x00" * 96) options = MongoCryptOptions(kms_dict) with self.assertRaisesRegex( MongoCryptError, "unable to parse base64 from UTF-8 field" ): MongoCrypt(options, callback) # Case 4: pass key as base64-encoded string (invalid) # Only applicable to "local" as key length is not validated for gcp. kms_dict = copy.deepcopy(base_kms_dict) kms_dict["local"]["key"] = base64.b64encode(b"\x00" * 96) options = MongoCryptOptions(kms_dict) with self.assertRaisesRegex(MongoCryptError, "local key must be 96 bytes"): MongoCrypt(options, callback) @staticmethod def create_mongocrypt(**kwargs): return MongoCrypt( MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, }, **kwargs, ), MockCallback(), ) def _test_kms_context(self, ctx): key_filter = ctx.mongo_operation() self.assertEqual(key_filter, bson_data("key-filter.json")) ctx.add_mongo_operation_result(bson_data("key-document.json")) ctx.complete_mongo_operation() self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_KMS) km_contexts = list(ctx.kms_contexts()) self.assertEqual(len(km_contexts), 1) with km_contexts[0] as kms_ctx: self.assertEqual(kms_ctx.kms_provider, "aws") self.assertEqual(kms_ctx.endpoint, "kms.us-east-1.amazonaws.com:443") self.assertEqual(len(kms_ctx.message), 790) self.assertEqual(kms_ctx.bytes_needed, 1024) kms_ctx.feed(http_data("kms-reply.txt")) self.assertEqual(kms_ctx.bytes_needed, 0) self.assertEqual(kms_ctx.kms_provider, "aws") ctx.complete_kms() def test_encrypt(self): mc = self.create_mongocrypt() self.addCleanup(mc.close) if mc.crypt_shared_lib_version is not None: self.skipTest("this test must be skipped when crypt_shared is loaded") with mc.encryption_context("text", bson_data("command.json")) as ctx: self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_COLLINFO) list_colls_filter = ctx.mongo_operation() self.assertEqual( list_colls_filter, bson_data("list-collections-filter.json") ) ctx.add_mongo_operation_result(bson_data("collection-info.json")) ctx.complete_mongo_operation() self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) mongocryptd_cmd = ctx.mongo_operation() self.assertEqual( bson.decode(mongocryptd_cmd, OPTS), json_data("mongocryptd-command.json"), ) self.assertEqual(mongocryptd_cmd, bson_data("mongocryptd-command.json")) ctx.add_mongo_operation_result(bson_data("mongocryptd-reply.json")) ctx.complete_mongo_operation() self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS) self._test_kms_context(ctx) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_READY) encrypted = ctx.finish() self.assertEqual( bson.decode(encrypted, OPTS), json_data("encrypted-command.json") ) self.assertEqual(encrypted, bson_data("encrypted-command.json")) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_DONE) def test_decrypt(self): mc = self.create_mongocrypt() self.addCleanup(mc.close) with mc.decryption_context(bson_data("encrypted-command-reply.json")) as ctx: self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS) self._test_kms_context(ctx) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_READY) encrypted = ctx.finish() self.assertEqual( bson.decode(encrypted, OPTS), json_data("command-reply.json") ) self.assertEqual(encrypted, bson_data("command-reply.json")) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_DONE) def test_encrypt_encrypted_fields_map(self): encrypted_fields_map = bson_data( "compact/success/encrypted-field-config-map.json" ) mc = self.create_mongocrypt(encrypted_fields_map=encrypted_fields_map) self.addCleanup(mc.close) with mc.encryption_context("db", bson_data("compact/success/cmd.json")) as ctx: self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS) ctx.mongo_operation() ctx.add_mongo_operation_result( bson_data("keys/12345678123498761234123456789012-local-document.json") ) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS) ctx.mongo_operation() ctx.add_mongo_operation_result( bson_data("keys/ABCDEFAB123498761234123456789012-local-document.json") ) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_NEED_MONGO_KEYS) ctx.mongo_operation() ctx.add_mongo_operation_result( bson_data("keys/12345678123498761234123456789013-local-document.json") ) ctx.complete_mongo_operation() self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_READY) encrypted = ctx.finish() self.assertEqual( bson.decode(encrypted, OPTS), json_data("compact/success/encrypted-payload.json"), ) self.assertEqual( encrypted, bson_data("compact/success/encrypted-payload.json") ) self.assertEqual(ctx.state, lib.MONGOCRYPT_CTX_DONE) def test_pymongo_imports(self): from pymongocrypt.auto_encrypter import AutoEncrypter # type:ignore[import] from pymongocrypt.errors import MongoCryptError # type:ignore[import] from pymongocrypt.explicit_encrypter import ( ExplicitEncrypter, # type:ignore[import] ) from pymongocrypt.mongocrypt import MongoCryptOptions # type:ignore[import] from pymongocrypt.state_machine import MongoCryptCallback # type:ignore[import] class MockCallback(MongoCryptCallback): def __init__( self, list_colls_result=None, mongocryptd_reply=None, key_docs=None, kms_reply=None, ): self.list_colls_result = list_colls_result self.mongocryptd_reply = mongocryptd_reply self.key_docs = key_docs self.kms_reply = kms_reply self.kms_endpoint = None def kms_request(self, kms_context): self.kms_endpoint = kms_context.endpoint kms_context.feed(self.kms_reply) def collection_info(self, ns, filter): return self.list_colls_result def mark_command(self, ns, cmd): return self.mongocryptd_reply def fetch_keys(self, filter): return self.key_docs def insert_data_key(self, data_key): raise NotImplementedError def bson_encode(self, doc): return bson.encode(doc) def close(self): pass class MockAsyncCallback(AsyncMongoCryptCallback): def __init__( self, list_colls_result=None, mongocryptd_reply=None, key_docs=None, kms_reply=None, ): self.list_colls_result = list_colls_result self.mongocryptd_reply = mongocryptd_reply self.key_docs = key_docs self.kms_reply = kms_reply self.kms_endpoint = None async def kms_request(self, kms_context): self.kms_endpoint = kms_context.endpoint kms_context.feed(self.kms_reply) async def collection_info(self, ns, filter): return self.list_colls_result async def mark_command(self, ns, cmd): return self.mongocryptd_reply async def fetch_keys(self, filter): for doc in self.key_docs: yield doc async def insert_data_key(self, data_key): raise NotImplementedError def bson_encode(self, doc): return bson.encode(doc) async def close(self): pass class TestMongoCryptCallback(unittest.TestCase): maxDiff = None @staticmethod def mongo_crypt_opts(): return MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } ) @unittest.skipUnless( os.getenv("TEST_CRYPT_SHARED"), "this test requires TEST_CRYPT_SHARED=1" ) def test_crypt_shared(self): if sys.platform == "darwin": raise unittest.SkipTest("Skipping due to SERVER-101020") kms_providers = { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } mc = MongoCrypt(MongoCryptOptions(kms_providers), MockCallback()) self.addCleanup(mc.close) self.assertIsNotNone(mc.crypt_shared_lib_version) # Test that we can pick up crypt_shared automatically encrypter = AutoEncrypter( MockCallback(), MongoCryptOptions( kms_providers, bypass_encryption=False, crypt_shared_lib_required=True ), ) self.addCleanup(encrypter.close) encrypter = AutoEncrypter( MockCallback(), MongoCryptOptions( kms_providers, crypt_shared_lib_path=os.environ["CRYPT_SHARED_PATH"], crypt_shared_lib_required=True, ), ) self.addCleanup(encrypter.close) with self.assertRaisesRegex(MongoCryptError, "/doesnotexist"): AutoEncrypter( MockCallback(), MongoCryptOptions( kms_providers, crypt_shared_lib_path="/doesnotexist", crypt_shared_lib_required=True, ), ) def test_encrypt(self): encrypter = AutoEncrypter( MockCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) encrypted = encrypter.encrypt("test", bson_data("command.json")) self.assertEqual( bson.decode(encrypted, OPTS), json_data("encrypted-command.json") ) self.assertEqual(encrypted, bson_data("encrypted-command.json")) def test_decrypt(self): encrypter = AutoEncrypter( MockCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) decrypted = encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertEqual(bson.decode(decrypted, OPTS), json_data("command-reply.json")) self.assertEqual(decrypted, bson_data("command-reply.json")) def test_need_kms_aws_credentials(self): kms_providers = {"aws": {}} opts = MongoCryptOptions(kms_providers) callback = MockCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ) encrypter = AutoEncrypter(callback, opts) self.addCleanup(encrypter.close) with unittest.mock.patch( "pymongocrypt.synchronous.credentials.aws_temp_credentials" ) as m: m.return_value = AwsCredential("example", "example", None) decrypted = encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(m.called) self.assertEqual(bson.decode(decrypted, OPTS), json_data("command-reply.json")) self.assertEqual(decrypted, bson_data("command-reply.json")) def test_need_kms_gcp_credentials(self): kms_providers = {"gcp": {}} opts = MongoCryptOptions(kms_providers) callback = MockCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document-gcp.json")], kms_reply=http_data("kms-reply-gcp.txt"), ) encrypter = AutoEncrypter(callback, opts) self.addCleanup(encrypter.close) with respx.mock(using="httpx") as router: data = {"access_token": "foo"} url = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" router.add( respx.get(url=url).mock(return_value=httpx.Response(200, json=data)) ) decrypted = encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertEqual(bson.decode(decrypted, OPTS), json_data("command-reply.json")) self.assertEqual(decrypted, bson_data("command-reply.json")) if sys.version_info >= (3, 8, 0): # noqa: UP036 class TestAsyncMongoCryptCallback(unittest.IsolatedAsyncioTestCase): maxDiff = None @staticmethod def mongo_crypt_opts(): return MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } ) @unittest.skipUnless( os.getenv("TEST_CRYPT_SHARED"), "this test requires TEST_CRYPT_SHARED=1" ) async def test_crypt_shared(self): if sys.platform == "darwin": raise unittest.SkipTest("Skipping due to SERVER-101020") kms_providers = { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } mc = MongoCrypt(MongoCryptOptions(kms_providers), MockAsyncCallback()) self.addCleanup(mc.close) self.assertIsNotNone(mc.crypt_shared_lib_version) # Test that we can pick up crypt_shared automatically encrypter = AsyncAutoEncrypter( MockAsyncCallback(), MongoCryptOptions( kms_providers, bypass_encryption=False, crypt_shared_lib_required=True, ), ) self.addAsyncCleanup(encrypter.close) encrypter = AsyncAutoEncrypter( MockAsyncCallback(), MongoCryptOptions( kms_providers, crypt_shared_lib_path=os.environ["CRYPT_SHARED_PATH"], crypt_shared_lib_required=True, ), ) self.addAsyncCleanup(encrypter.close) with self.assertRaisesRegex(MongoCryptError, "/doesnotexist"): AsyncAutoEncrypter( MockAsyncCallback(), MongoCryptOptions( kms_providers, crypt_shared_lib_path="/doesnotexist", crypt_shared_lib_required=True, ), ) async def test_encrypt(self): encrypter = AsyncAutoEncrypter( MockAsyncCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addAsyncCleanup(encrypter.close) encrypted = await encrypter.encrypt("test", bson_data("command.json")) self.assertEqual( bson.decode(encrypted, OPTS), json_data("encrypted-command.json") ) self.assertEqual(encrypted, bson_data("encrypted-command.json")) async def test_decrypt(self): encrypter = AsyncAutoEncrypter( MockAsyncCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addAsyncCleanup(encrypter.close) decrypted = await encrypter.decrypt( bson_data("encrypted-command-reply.json") ) self.assertEqual( bson.decode(decrypted, OPTS), json_data("command-reply.json") ) self.assertEqual(decrypted, bson_data("command-reply.json")) async def test_need_kms_aws_credentials(self): kms_providers = {"aws": {}} opts = MongoCryptOptions(kms_providers) callback = MockAsyncCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ) encrypter = AsyncAutoEncrypter(callback, opts) self.addAsyncCleanup(encrypter.close) with unittest.mock.patch( "pymongocrypt.asynchronous.credentials.aws_temp_credentials" ) as m: m.return_value = AwsCredential("example", "example", None) decrypted = await encrypter.decrypt( bson_data("encrypted-command-reply.json") ) self.assertTrue(m.called) self.assertEqual( bson.decode(decrypted, OPTS), json_data("command-reply.json") ) self.assertEqual(decrypted, bson_data("command-reply.json")) async def test_need_kms_gcp_credentials(self): kms_providers = {"gcp": {}} opts = MongoCryptOptions(kms_providers) callback = MockAsyncCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document-gcp.json")], kms_reply=http_data("kms-reply-gcp.txt"), ) encrypter = AsyncAutoEncrypter(callback, opts) self.addAsyncCleanup(encrypter.close) with respx.mock(using="httpx") as router: data = {"access_token": "foo"} url = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" router.add( respx.get(url=url).mock(return_value=httpx.Response(200, json=data)) ) decrypted = await encrypter.decrypt( bson_data("encrypted-command-reply.json") ) self.assertTrue(len(router.calls)) self.assertEqual( bson.decode(decrypted, OPTS), json_data("command-reply.json") ) self.assertEqual(decrypted, bson_data("command-reply.json")) class TestAsyncExplicitEncryption(unittest.IsolatedAsyncioTestCase): maxDiff = None @staticmethod def mongo_crypt_opts(): return MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } ) async def _test_encrypt_decrypt(self, key_id=None, key_alt_name=None): encrypter = AsyncExplicitEncrypter( MockAsyncCallback( key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) val = {"v": "hello"} encoded_val = bson.encode(val) algo = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" encrypted = await encrypter.encrypt( encoded_val, algo, key_id=key_id, key_alt_name=key_alt_name ) self.assertEqual( bson.decode(encrypted, OPTS), json_data("encrypted-value.json") ) self.assertEqual(encrypted, bson_data("encrypted-value.json")) decrypted = await encrypter.decrypt(encrypted) self.assertEqual(bson.decode(decrypted, OPTS), val) self.assertEqual(encoded_val, decrypted) async def test_encrypt_decrypt(self): key_id = json_data("key-document.json")["_id"] await self._test_encrypt_decrypt(key_id=key_id) async def test_encrypt_decrypt_key_alt_name(self): key_alt_name = json_data("key-document.json")["keyAltNames"][0] await self._test_encrypt_decrypt(key_alt_name=key_alt_name) async def test_encrypt_errors(self): key_id = json_data("key-document.json")["_id"] encrypter = AsyncExplicitEncrypter( MockAsyncCallback(key_docs=[]), self.mongo_crypt_opts() ) self.addCleanup(encrypter.close) val = {"v": "value123"} encoded_val = bson.encode(val) # Invalid algorithm. with self.assertRaisesRegex(MongoCryptError, "algorithm"): await encrypter.encrypt(encoded_val, "Invalid", key_id) # Invalid query_type type. with self.assertRaisesRegex(TypeError, "query_type"): await encrypter.encrypt(encoded_val, "Indexed", key_id, query_type=42) # Invalid query_type string. with self.assertRaisesRegex(MongoCryptError, "query_type"): await encrypter.encrypt( encoded_val, "Indexed", key_id, query_type="invalid query type string", ) # Invalid contention_factor type. with self.assertRaisesRegex(TypeError, "contention_factor"): await encrypter.encrypt( encoded_val, "Indexed", key_id, contention_factor="not an int" ) with self.assertRaisesRegex(MongoCryptError, "contention"): await encrypter.encrypt( encoded_val, "Indexed", key_id, contention_factor=-1 ) # Invalid: Unindexed + query_type is an error. with self.assertRaisesRegex(MongoCryptError, "query"): await encrypter.encrypt( encoded_val, "Unindexed", key_id, query_type="equality" ) # Invalid: Unindexed + contention_factor is an error. with self.assertRaisesRegex(MongoCryptError, "contention"): await encrypter.encrypt( encoded_val, "Unindexed", key_id, contention_factor=1 ) async def test_encrypt_indexed(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = AsyncExplicitEncrypter( MockAsyncCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) val = {"v": "value123"} encoded_val = bson.encode(val) for kwargs in [ dict(algorithm="Indexed", contention_factor=0), dict(algorithm="Indexed", query_type="equality", contention_factor=0), dict(algorithm="Indexed", contention_factor=100), dict(algorithm="Unindexed"), ]: kwargs["key_id"] = key_id encrypted = await encrypter.encrypt(encoded_val, **kwargs) encrypted_val = bson.decode(encrypted, OPTS)["v"] self.assertIsInstance(encrypted_val, Binary) self.assertEqual(encrypted_val.subtype, 6) # Queryable Encryption find payloads cannot be round-tripped. if "query_type" not in kwargs: decrypted = await encrypter.decrypt(encrypted) self.assertEqual(bson.decode(decrypted, OPTS), val) self.assertEqual(encoded_val, decrypted) async def test_data_key_creation(self): mock_key_vault = AsyncKeyVaultCallback( kms_reply=http_data("kms-encrypt-reply.txt") ) opts = MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "aws:named": { "accessKeyId": "example", "secretAccessKey": "example", }, "local": {"key": b"\x00" * 96}, "local:named": {"key": b"\x01" * 96}, } ) encrypter = AsyncExplicitEncrypter(mock_key_vault, opts) self.addCleanup(encrypter.close) valid_args = [ ("local", None, ["first", "second"]), ("local:named", None, ["local:named"]), ("aws", {"region": "region", "key": "cmk"}, ["third", "forth"]), ("aws:named", {"region": "region", "key": "cmk"}, ["aws:named"]), # Unicode region and key ("aws", {"region": "region-unicode", "key": "cmk-unicode"}, []), # Endpoint ( "aws", { "region": "region", "key": "cmk", "endpoint": "kms.us-east-1.amazonaws.com:443", }, [], ), ] for kms_provider, master_key, key_alt_names in valid_args: key_id = await encrypter.create_data_key( kms_provider, master_key=master_key, key_alt_names=key_alt_names ) self.assertIsInstance(key_id, Binary) self.assertEqual(key_id.subtype, 4) data_key = bson.decode(mock_key_vault.data_key, OPTS) # CDRIVER-3277 The order of key_alt_names is not maintained. for name in key_alt_names: self.assertIn(name, data_key["keyAltNames"]) # Assert that the custom endpoint is passed to libmongocrypt. master_key = {"region": "region", "key": "key", "endpoint": "example.com"} key_material = base64.b64decode( "xPTAjBRG5JiPm+d3fj6XLi2q5DMXUS/f1f+SMAlhhwkhDRL0kr8r9GDLIGTAGlvC+HVjSIgdL+RKwZCvpXSyxTICWSXTUYsWYPyu3IoHbuBZdmw2faM3WhcRIgbMReU5" ) if not PY3: key_material = Binary(key_material) await encrypter.create_data_key( "aws", master_key=master_key, key_material=key_material ) self.assertEqual("example.com:443", mock_key_vault.kms_endpoint) async def test_data_key_creation_bad_key_material(self): mock_key_vault = AsyncKeyVaultCallback( kms_reply=http_data("kms-encrypt-reply.txt") ) encrypter = AsyncExplicitEncrypter(mock_key_vault, self.mongo_crypt_opts()) self.addCleanup(encrypter.close) key_material = Binary(b"0" * 97) with self.assertRaisesRegex( MongoCryptError, "keyMaterial should have length 96, but has length 97" ): await encrypter.create_data_key("local", key_material=key_material) async def test_rewrap_many_data_key(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_path2 = "keys/12345678123498761234123456789012-local-document.json" encrypter = AsyncExplicitEncrypter( MockAsyncCallback(key_docs=[bson_data(key_path), bson_data(key_path2)]), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) result = await encrypter.rewrap_many_data_key({}) raw_doc = RawBSONDocument(result) assert len(raw_doc["v"]) == 2 async def test_range_query_int32(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = AsyncExplicitEncrypter( MockAsyncCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) range_opts = bson_data("fle2-find-range-explicit-v2/int32/rangeopts.json") value = bson_data("fle2-find-range-explicit-v2/int32/value-to-encrypt.json") expected = json_data( "fle2-find-range-explicit-v2/int32/encrypted-payload.json" ) encrypted = await encrypter.encrypt( value, "range", key_id=key_id, query_type="range", contention_factor=4, range_opts=range_opts, is_expression=True, ) encrypted_val = bson.decode(encrypted, OPTS) self.assertEqual( encrypted_val, adjust_range_counter(encrypted_val, expected) ) async def test_text_query(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = AsyncExplicitEncrypter( MockAsyncCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) text_opts = bson_data("fle2-text-search/textopts.json") expected = bson_data("fle2-text-search/encrypted-payload.json") value = bson.encode({"v": "foo"}) encrypted = await encrypter.encrypt( value, "string", key_id=key_id, query_type="suffix", contention_factor=0, text_opts=text_opts, ) self.assertEqual(encrypted, expected) class TestNeedKMSAzureCredentials(unittest.TestCase): maxDiff = None def get_encrypter(self, clear_cache=True): if clear_cache: pymongocrypt.synchronous.credentials._azure_creds_cache = None kms_providers = {"azure": {}} opts = MongoCryptOptions(kms_providers) callback = MockCallback( list_colls_result=bson_data("collection-info.json"), mongocryptd_reply=bson_data("mongocryptd-reply.json"), key_docs=[bson_data("key-document-azure.json")], kms_reply=http_data("kms-reply-azure.txt"), ) encrypter = AutoEncrypter(callback, opts) self.addCleanup(encrypter.close) return encrypter def test_success(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: data = {"access_token": "foo", "expires_in": 4000} url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock(return_value=httpx.Response(200, json=data)) ) decrypted = encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertEqual(bson.decode(decrypted, OPTS), json_data("command-reply.json")) self.assertEqual(decrypted, bson_data("command-reply.json")) self.assertIsNotNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_empty_json(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock(return_value=httpx.Response(200, json={})) ) with self.assertRaisesRegex( MongoCryptError, "Azure IMDS response must contain" ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_bad_json(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock(return_value=httpx.Response(200, text="a'")) ) with self.assertRaisesRegex( MongoCryptError, "Azure IMDS response must be in JSON format" ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_http_404(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add(respx.get(url=url).mock(return_value=httpx.Response(404))) with self.assertRaisesRegex( MongoCryptError, "Failed to acquire IMDS access token." ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_http_500(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add(respx.get(url=url).mock(return_value=httpx.Response(500))) with self.assertRaisesRegex( MongoCryptError, "Failed to acquire IMDS access token." ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_slow_response(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock(side_effect=httpx._exceptions.ConnectTimeout) ) with self.assertRaisesRegex( MongoCryptError, "Failed to acquire IMDS access token: " ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_cache(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: data = {"access_token": "foo", "expires_in": 4000} url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock( return_value=httpx.Response(status_code=200, json=data) ) ) encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNotNone(pymongocrypt.synchronous.credentials._azure_creds_cache) # Should use the cached value. decrypted = encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertEqual(decrypted, bson_data("command-reply.json")) self.assertIsNotNone(pymongocrypt.synchronous.credentials._azure_creds_cache) def test_cache_expires_soon(self): encrypter = self.get_encrypter() with respx.mock(using="httpx") as router: data = {"access_token": "foo", "expires_in": 10} url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock( return_value=httpx.Response(status_code=200, json=data) ) ) encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNotNone(pymongocrypt.synchronous.credentials._azure_creds_cache) # Should not use the cached value. encrypter = self.get_encrypter(False) self.assertIsNotNone(pymongocrypt.synchronous.credentials._azure_creds_cache) with respx.mock(using="httpx") as router: url = "http://169.254.169.254/metadata/identity/oauth2/token" router.add( respx.get(url=url).mock(side_effect=httpx._exceptions.ConnectTimeout) ) with self.assertRaisesRegex( MongoCryptError, "Failed to acquire IMDS access token: " ): encrypter.decrypt(bson_data("encrypted-command-reply.json")) self.assertTrue(len(router.calls)) self.assertIsNone(pymongocrypt.synchronous.credentials._azure_creds_cache) class KeyVaultCallback(MockCallback): def __init__(self, kms_reply=None): super().__init__(kms_reply=kms_reply) self.data_key = None def fetch_keys(self, filter): return self.data_key def insert_data_key(self, data_key): self.data_key = data_key return bson.decode(data_key, OPTS)["_id"] def adjust_range_counter(encrypted_val, expected): """Workaround for the internal range payload counter in libmongocrypt.""" if encrypted_val != expected: _payload1 = expected["v"]["$and"][0]["age"]["$gte"] _payload2 = expected["v"]["$and"][1]["age"]["$lte"] _decoded1 = bson.decode(_payload1[1:]) _decoded2 = bson.decode(_payload2[1:]) for _ in range(10): _decoded1["payloadId"] += 1 expected["v"]["$and"][0]["age"]["$gte"] = Binary( _payload1[0:1] + bson.encode(_decoded1), 6 ) _decoded2["payloadId"] += 1 expected["v"]["$and"][1]["age"]["$lte"] = Binary( _payload2[0:1] + bson.encode(_decoded2), 6 ) if encrypted_val == expected: break return expected class AsyncKeyVaultCallback(MockAsyncCallback): def __init__(self, kms_reply=None): super().__init__(kms_reply=kms_reply) self.data_key = None async def fetch_keys(self, filter): return self.data_key async def insert_data_key(self, data_key): self.data_key = data_key return bson.decode(data_key, OPTS)["_id"] class TestExplicitEncryption(unittest.TestCase): maxDiff = None @staticmethod def mongo_crypt_opts(): return MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, } ) def _test_encrypt_decrypt(self, key_id=None, key_alt_name=None): encrypter = ExplicitEncrypter( MockCallback( key_docs=[bson_data("key-document.json")], kms_reply=http_data("kms-reply.txt"), ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) val = {"v": "hello"} encoded_val = bson.encode(val) algo = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" encrypted = encrypter.encrypt( encoded_val, algo, key_id=key_id, key_alt_name=key_alt_name ) self.assertEqual( bson.decode(encrypted, OPTS), json_data("encrypted-value.json") ) self.assertEqual(encrypted, bson_data("encrypted-value.json")) decrypted = encrypter.decrypt(encrypted) self.assertEqual(bson.decode(decrypted, OPTS), val) self.assertEqual(encoded_val, decrypted) def test_encrypt_decrypt(self): key_id = json_data("key-document.json")["_id"] self._test_encrypt_decrypt(key_id=key_id) def test_encrypt_decrypt_key_alt_name(self): key_alt_name = json_data("key-document.json")["keyAltNames"][0] self._test_encrypt_decrypt(key_alt_name=key_alt_name) def test_encrypt_errors(self): key_id = json_data("key-document.json")["_id"] encrypter = ExplicitEncrypter( MockCallback(key_docs=[]), self.mongo_crypt_opts() ) self.addCleanup(encrypter.close) val = {"v": "value123"} encoded_val = bson.encode(val) # Invalid algorithm. with self.assertRaisesRegex(MongoCryptError, "algorithm"): encrypter.encrypt(encoded_val, "Invalid", key_id) # Invalid query_type type. with self.assertRaisesRegex(TypeError, "query_type"): encrypter.encrypt(encoded_val, "Indexed", key_id, query_type=42) # Invalid query_type string. with self.assertRaisesRegex(MongoCryptError, "query_type"): encrypter.encrypt( encoded_val, "Indexed", key_id, query_type="invalid query type string" ) # Invalid contention_factor type. with self.assertRaisesRegex(TypeError, "contention_factor"): encrypter.encrypt( encoded_val, "Indexed", key_id, contention_factor="not an int" ) with self.assertRaisesRegex(MongoCryptError, "contention"): encrypter.encrypt(encoded_val, "Indexed", key_id, contention_factor=-1) # Invalid: Unindexed + query_type is an error. with self.assertRaisesRegex(MongoCryptError, "query"): encrypter.encrypt(encoded_val, "Unindexed", key_id, query_type="equality") # Invalid: Unindexed + contention_factor is an error. with self.assertRaisesRegex(MongoCryptError, "contention"): encrypter.encrypt(encoded_val, "Unindexed", key_id, contention_factor=1) def test_encrypt_indexed(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = ExplicitEncrypter( MockCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) val = {"v": "value123"} encoded_val = bson.encode(val) for kwargs in [ dict(algorithm="Indexed", contention_factor=0), dict(algorithm="Indexed", query_type="equality", contention_factor=0), dict(algorithm="Indexed", contention_factor=100), dict(algorithm="Unindexed"), ]: kwargs["key_id"] = key_id encrypted = encrypter.encrypt(encoded_val, **kwargs) encrypted_val = bson.decode(encrypted, OPTS)["v"] self.assertIsInstance(encrypted_val, Binary) self.assertEqual(encrypted_val.subtype, 6) # Queryable Encryption find payloads cannot be round-tripped. if "query_type" not in kwargs: decrypted = encrypter.decrypt(encrypted) self.assertEqual(bson.decode(decrypted, OPTS), val) self.assertEqual(encoded_val, decrypted) def test_data_key_creation(self): mock_key_vault = KeyVaultCallback(kms_reply=http_data("kms-encrypt-reply.txt")) opts = MongoCryptOptions( { "aws": {"accessKeyId": "example", "secretAccessKey": "example"}, "aws:named": {"accessKeyId": "example", "secretAccessKey": "example"}, "local": {"key": b"\x00" * 96}, "local:named": {"key": b"\x01" * 96}, } ) encrypter = ExplicitEncrypter(mock_key_vault, opts) self.addCleanup(encrypter.close) valid_args = [ ("local", None, ["first", "second"]), ("local:named", None, ["local:named"]), ("aws", {"region": "region", "key": "cmk"}, ["third", "forth"]), ("aws:named", {"region": "region", "key": "cmk"}, ["aws:named"]), # Unicode region and key ("aws", {"region": "region-unicode", "key": "cmk-unicode"}, []), # Endpoint ( "aws", { "region": "region", "key": "cmk", "endpoint": "kms.us-east-1.amazonaws.com:443", }, [], ), ] for kms_provider, master_key, key_alt_names in valid_args: key_id = encrypter.create_data_key( kms_provider, master_key=master_key, key_alt_names=key_alt_names ) self.assertIsInstance(key_id, Binary) self.assertEqual(key_id.subtype, 4) data_key = bson.decode(mock_key_vault.data_key, OPTS) # CDRIVER-3277 The order of key_alt_names is not maintained. for name in key_alt_names: self.assertIn(name, data_key["keyAltNames"]) # Assert that the custom endpoint is passed to libmongocrypt. master_key = {"region": "region", "key": "key", "endpoint": "example.com"} key_material = base64.b64decode( "xPTAjBRG5JiPm+d3fj6XLi2q5DMXUS/f1f+SMAlhhwkhDRL0kr8r9GDLIGTAGlvC+HVjSIgdL+RKwZCvpXSyxTICWSXTUYsWYPyu3IoHbuBZdmw2faM3WhcRIgbMReU5" ) if not PY3: key_material = Binary(key_material) encrypter.create_data_key( "aws", master_key=master_key, key_material=key_material ) self.assertEqual("example.com:443", mock_key_vault.kms_endpoint) def test_data_key_creation_bad_key_material(self): mock_key_vault = KeyVaultCallback(kms_reply=http_data("kms-encrypt-reply.txt")) encrypter = ExplicitEncrypter(mock_key_vault, self.mongo_crypt_opts()) self.addCleanup(encrypter.close) key_material = Binary(b"0" * 97) with self.assertRaisesRegex( MongoCryptError, "keyMaterial should have length 96, but has length 97" ): encrypter.create_data_key("local", key_material=key_material) def test_rewrap_many_data_key(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_path2 = "keys/12345678123498761234123456789012-local-document.json" encrypter = ExplicitEncrypter( MockCallback(key_docs=[bson_data(key_path), bson_data(key_path2)]), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) result = encrypter.rewrap_many_data_key({}) raw_doc = RawBSONDocument(result) assert len(raw_doc["v"]) == 2 def test_range_query_int32(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = ExplicitEncrypter( MockCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) range_opts = bson_data("fle2-find-range-explicit-v2/int32/rangeopts.json") value = bson_data("fle2-find-range-explicit-v2/int32/value-to-encrypt.json") expected = json_data("fle2-find-range-explicit-v2/int32/encrypted-payload.json") encrypted = encrypter.encrypt( value, "range", key_id=key_id, query_type="range", contention_factor=4, range_opts=range_opts, is_expression=True, ) encrypted_val = bson.decode(encrypted, OPTS) self.assertEqual(encrypted_val, adjust_range_counter(encrypted_val, expected)) def test_rangePreview_query_int32(self): # Expect error attempting to use 'rangePreview' with self.assertRaisesRegex( MongoCryptError, "Algorithm 'rangePreview' is deprecated, please use 'range'", ): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = ExplicitEncrypter( MockCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) range_opts = bson_data( "fle2-find-rangePreview-explicit/int32/rangeopts.json" ) value = bson_data( "fle2-find-rangePreview-explicit/int32/value-to-encrypt.json" ) encrypter.encrypt( value, "rangePreview", key_id=key_id, query_type="rangePreview", contention_factor=4, range_opts=range_opts, is_expression=True, ) def test_text_query(self): key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json" key_id = json_data(key_path)["_id"] encrypter = ExplicitEncrypter( MockCallback( key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt") ), self.mongo_crypt_opts(), ) self.addCleanup(encrypter.close) text_opts = bson_data("fle2-text-search/textopts.json") expected = bson_data("fle2-text-search/encrypted-payload.json") value = bson.encode({"v": "foo"}) encrypted = encrypter.encrypt( value, "string", key_id=key_id, query_type="suffix", contention_factor=0, text_opts=text_opts, ) self.assertEqual(encrypted, expected) def read(filename, **kwargs): with open(os.path.join(DATA_DIR, filename), **kwargs) as fp: return fp.read() OPTS = CodecOptions(uuid_representation=UuidRepresentation.UNSPECIFIED) JSON_OPTS = JSONOptions( document_class=dict, uuid_representation=UuidRepresentation.UNSPECIFIED ) def json_data(filename): return json_util.loads(read(filename), json_options=JSON_OPTS) def bson_data(filename): return bson.encode(json_data(filename), codec_options=OPTS) def http_data(filename): data = read(filename, mode="rb") return data.replace(b"\n", b"\r\n") if __name__ == "__main__": unittest.main() libmongocrypt-1.19.0/cmake/000077500000000000000000000000001521103432300155445ustar00rootroot00000000000000libmongocrypt-1.19.0/cmake/FetchMongoC.cmake000066400000000000000000000036001521103432300207010ustar00rootroot00000000000000include (FetchContent) # Set the tag that we will fetch. # When updating the version of libbson, also update the version in etc/purls.txt and .evergreen/prep_c_driver_source.sh set (MONGOC_FETCH_TAG_FOR_LIBBSON "2.3.0" CACHE STRING "The Git tag of mongo-c-driver that will be fetched to obtain libbson") # Add an option to disable patching if a patch command is unavailable. option (LIBBSON_PATCH_ENABLED "Whether to apply patches to the libbson library" ON) set (patch_disabled OFF) if (NOT LIBBSON_PATCH_ENABLED) set (patch_disabled ON) endif () include (Patch) make_patch_command (patch_command STRIP_COMPONENTS 1 DIRECTORY "" DISABLED "${patch_disabled}" PATCHES ${PROJECT_SOURCE_DIR}/etc/libbson-remove-GCC-diagnostic-pragma.patch ${PROJECT_SOURCE_DIR}/etc/mongo-common-test-harness.patch ) # Fetch the source archive for the requested tag from GitHub FetchContent_Declare ( embedded_mcd URL "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/${MONGOC_FETCH_TAG_FOR_LIBBSON}.tar.gz" PATCH_COMMAND ${patch_command} --verbose SOURCE_SUBDIR "NO_ADD_SUBDIRECTORY" # add_subdirectory() is handled by ImportBSON.cmake. ) # Populate it: FetchContent_GetProperties (embedded_mcd) if (NOT embedded_mcd_POPULATED) message (STATUS "Downloading mongo-c-driver ${MONGOC_FETCH_TAG_FOR_LIBBSON} for libbson") if("${CMAKE_VERSION}" VERSION_LESS "3.18.0") # SOURCE_SUBDIR is not yet supported. FetchContent_Populate(embedded_mcd) else() FetchContent_MakeAvailable(embedded_mcd) endif() endif () # Store the directory path to the external mongoc project: get_filename_component (MONGOCRYPT_MONGOC_DIR "${embedded_mcd_SOURCE_DIR}" ABSOLUTE) # The project wants a VERSION_CURRENT file. We know that based on the tag. file (WRITE "${embedded_mcd_SOURCE_DIR}/VERSION_CURRENT" "${MONGOC_FETCH_TAG_FOR_LIBBSON}") libmongocrypt-1.19.0/cmake/ImportBSON.cmake000066400000000000000000000256131521103432300205110ustar00rootroot00000000000000#[[ This file defines, exports, and installs two INTERFACE targets: '_mongocrypt::libbson_for_static' and '_mongocrypt::libbson_for_shared', that are used to link libbson correctly for the build configuration of libmongocrypt. At find_package() time, we can resolve these interface targets to link to the appropriate libbson based on the build configurations of libmongocrypt. mongo::mongocrypt must link to _mongocrypt::libbson_for_shared, and mongo::mongocrypt_static must link to _mongocrypt::libbson_for_static. At configure+build time, these target will create BUILD_INTERFACE-only usage requirements appropriate for libmongocrypt to build against a libbson. Once these targets are installed, they retain no usage requirements defined here. Instead, the installed version of these targets will be manipulated in mongocrypt-config.cmake based on user settings and build configuration options of the installed libmongocrypt in order to ensure that users have satisfied the linking requirements of libmongocrypt. Refer to mongocrypt-config.cmake for more information This file calls add_subdirectory(EXCLUDE_FROM_ALL) on a mongo-c-driver project directory. This will expose libbson targets that we can link and use for the libmongocrypt build. The boolean option USE_SHARED_LIBBSON controls the behavior of libbson_for_shared: If USE_SHARED_LIBBSON=FALSE: - libbson_for_shared will transitively link the static libbson from the MONGOCRYPT_MONGOC_DIR. - The result is that mongo::mongocrypt (which is a SHARED library) will have the translation units of libbson directly embedded into the resulting binary. - The symbols from libbson that are merged into mongo::mongocrypt will be suppressed using linker scripts such that consumers of mongo::mongocrypt will not see the libbson symbols that were statically linked into the shared library. This allows consumers to link against a completely independent libbson without interfering with the libbson symbols that were merged into mongo::mongocrypt - The installed libbson_for_shared will have no usage requirements. If USE_SHARED_LIBBSON=TRUE: - libbson_for_shared will transitively use the shared libbson library from the MONGOCRYPT_MONGOC_DIR. - mongo::mongocrypt will be built with a dynamic link requirement on a libbson dynamic library, which must be resolved at runtime by consumers. The translation units from the MONGOCRYPT_MONGOC_DIR *will not* be included in the mongo::mongocrypt library. - The installed libbson_for_shared will dynamically link to a libbson on the user's system by using a find_library() call. In both of the above cases, libbson_for_static will require that the final consumer provide their own definitions of the libbson symbols, regardless of the value of USE_SHARED_LIBBSON. ]] set (init OFF) if (DEFINED ENABLED_SHARED_BSON) message (STATUS "ENABLE_SHARED_BSON is now named USE_SHARED_LIBBSON") set (init "${ENABLE_SHARED_BSON}") endif () option (USE_SHARED_LIBBSON "Dynamically link libbson for the libmongocrypt dynamic library (default is static)" ${init}) if (NOT DEFINED MONGOCRYPT_MONGOC_DIR) # The user did not provide a MONGOCRYPT_MONGOC_DIR, so we'll get one include (FetchContent OPTIONAL) if (NOT COMMAND FetchContent_Declare) # We need FetchContent in order to download the project. message (FATAL_ERROR "No MONGOCRYPT_MONGOC_DIR setting was defined, and the FetchContent.cmake " "module is not available. Upgrade your CMake version, or provide a " "MONGOCRYPT_MONGOC_DIR path to a mongo-c-driver directory (This is required " "for libmongocrypt to find a libbson to use and link against).") endif () include (FetchMongoC) # The FetchMongoC module defines a MONGOCRYPT_MONGOC_DIR for us to use endif () function (_import_bson) if (MONGOCRYPT_MONGOC_DIR STREQUAL "USE-SYSTEM" AND USE_SHARED_LIBBSON AND NOT ENABLE_ONLINE_TESTS) message (STATUS "NOTE: Using system-wide libbson library. This is intended only for package maintainers.") find_library (_MONGOCRYPT_SYSTEM_LIBBSON_SHARED "${CMAKE_SHARED_LIBRARY_PREFIX}bson2${CMAKE_SHARED_LIBRARY_SUFFIX}") find_library (_MONGOCRYPT_SYSTEM_LIBBSON_STATIC "${CMAKE_STATIC_LIBRARY_PREFIX}bson2${CMAKE_STATIC_LIBRARY_SUFFIX}") find_package (bson 2.0 REQUIRED) get_target_property (_BSON_SHARED_INCL_DIR_PROP bson::shared INTERFACE_INCLUDE_DIRECTORIES) set (_MONGOCRYPT_SYSTEM_LIBBSON_INCLUDE_DIR "${_BSON_SHARED_INCL_DIR_PROP}" CACHE FILEPATH "Internal use only") add_library (bson_shared SHARED IMPORTED) add_library (bson_static STATIC IMPORTED) set_target_properties (bson_shared bson_static PROPERTIES IMPORTED_CONFIGURATIONS "Release" INTERFACE_INCLUDE_DIRECTORIES "${_MONGOCRYPT_SYSTEM_LIBBSON_INCLUDE_DIR}" ) set_property (TARGET bson_shared PROPERTY IMPORTED_LOCATION "${_MONGOCRYPT_SYSTEM_LIBBSON_SHARED}") set_property (TARGET bson_static PROPERTY IMPORTED_LOCATION "${_MONGOCRYPT_SYSTEM_LIBBSON_STATIC}") set_property ( CACHE _MONGOCRYPT_SYSTEM_LIBBSON_SHARED _MONGOCRYPT_SYSTEM_LIBBSON_INCLUDE_DIR PROPERTY ADVANCED TRUE ) else () message (STATUS "Using [${MONGOCRYPT_MONGOC_DIR}] as a sub-project for libbson") # Disable AWS_AUTH, to prevent it from building the kms-message symbols, which we build ourselves set (ENABLE_MONGODB_AWS_AUTH OFF CACHE BOOL "Disable kms-message content in mongoc for libmongocrypt" FORCE) # Disable install() for the libbson static library. We'll do it ourselves set (ENABLE_STATIC BUILD_ONLY) # Disable zlib, which isn't necessary for libmongocrypt and isn't necessarily available. set (ENABLE_ZLIB OFF CACHE BOOL "Toggle zlib for the mongoc subproject (not required by libmongocrypt)") # Disable libzstd, which isn't necessary for libmongocrypt and isn't necessarily available. set (ENABLE_ZSTD OFF CACHE BOOL "Toggle libzstd for the mongoc subproject (not required by libmongocrypt)") # Disable snappy, which isn't necessary for libmongocrypt and isn't necessarily available. set (ENABLE_SNAPPY OFF CACHE BOOL "Toggle snappy for the mongoc subproject (not required by libmongocrypt)") # Disable deprecated automatic init and cleanup. (May be overridden by the user) set (ENABLE_AUTOMATIC_INIT_AND_CLEANUP OFF CACHE BOOL "Enable automatic init and cleanup (GCC only)") # We don't want the subproject to find libmongocrypt set (ENABLE_CLIENT_SIDE_ENCRYPTION OFF CACHE BOOL "Disable client-side encryption for the libmongoc subproject") # Clear `BUILD_VERSION` so C driver does not use a `BUILD_VERSION` meant for libmongocrypt. # Both libmongocrypt and C driver support setting a `BUILD_VERSION` to override the version. if (DEFINED CACHE{BUILD_VERSION}) set (saved_cached_build_version "${BUILD_VERSION}") unset (BUILD_VERSION CACHE) # Undefine cache variable. endif () if (DEFINED BUILD_VERSION) set (saved_build_version "${BUILD_VERSION}") unset (BUILD_VERSION) # Undefine normal variable. endif () # Disable building tests in C driver: set (ENABLE_TESTS OFF) set (BUILD_TESTING OFF) # Disable counters in C driver. Counters are not supported on all platforms. set (ENABLE_SHM_COUNTERS OFF) # Add the subdirectory as a project. EXCLUDE_FROM_ALL to inhibit building and installing of components unless requested # SYSTEM (on applicable CMake versions) to prevent warnings (particularly from -Wconversion/-Wsign-conversion) from the C driver code if (CMAKE_VERSION VERSION_GREATER 3.25) add_subdirectory ("${MONGOCRYPT_MONGOC_DIR}" _mongo-c-driver EXCLUDE_FROM_ALL SYSTEM) else () add_subdirectory ("${MONGOCRYPT_MONGOC_DIR}" _mongo-c-driver EXCLUDE_FROM_ALL) endif () if (DEFINED saved_cached_build_version) set (BUILD_VERSION "${saved_cached_build_version}" CACHE STRING "Library version") endif () if (DEFINED saved_build_version) set (BUILD_VERSION "${saved_build_version}") endif () if (TARGET mongoc_static) # Workaround: Embedded mongoc_static does not set its INCLUDE_DIRECTORIES for user targets target_include_directories (mongoc_static PUBLIC "$" "$" ) endif () endif () endfunction () # Do the import in a function to isolate variable scope _import_bson () # Define interface targets to be used to control the libbson used at both build and import time. # Refer to mongocrypt-config.cmake to see how these targets are used by consumers add_library (_mongocrypt-libbson_for_static INTERFACE) add_library (_mongocrypt-libbson_for_shared INTERFACE) add_library (_mongocrypt::libbson_for_static ALIAS _mongocrypt-libbson_for_static) add_library (_mongocrypt::libbson_for_shared ALIAS _mongocrypt-libbson_for_shared) install ( TARGETS _mongocrypt-libbson_for_static _mongocrypt-libbson_for_shared EXPORT mongocrypt_targets ) # Link to the requested libbson, only exporting that usage for the local build tree. # The mongocrypt-config file will later add the appropriate link library for downstream # users during find_package() if (USE_SHARED_LIBBSON) target_link_libraries (_mongocrypt-libbson_for_shared INTERFACE $) else () target_link_libraries (_mongocrypt-libbson_for_shared INTERFACE $) endif () # libbson_for_static always links to the static libbson: target_link_libraries (_mongocrypt-libbson_for_static INTERFACE $) if (TARGET mongoc_static) # And an alias to the mongoc target for use in some test cases add_library (_mongocrypt::mongoc ALIAS mongoc_static) endif () # Put the libbson dynamic library into the current binary directory (plus possible config suffix). # This ensures that libbson DLL will resolve on Windows when it searches during tests set_property (TARGET bson_shared PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") if (ENABLE_STATIC) # We are going to build a static libmongocrypt. # We want the static libbson target from the embedded mongoc. Enable the static library as # part of "all", and install the archive alongside the rest of our static libraries. # (Useful for some users for convenience of static-linking libmongocrypt: CDRIVER-3187) set_target_properties (bson_static PROPERTIES EXCLUDE_FROM_ALL FALSE OUTPUT_NAME bson-static-for-libmongocrypt ) install ( FILES $ DESTINATION "${CMAKE_INSTALL_LIBDIR}" RENAME ${CMAKE_STATIC_LIBRARY_PREFIX}bson-static-for-libmongocrypt${CMAKE_STATIC_LIBRARY_SUFFIX} ) endif () libmongocrypt-1.19.0/cmake/ImportDFP.cmake000066400000000000000000000064771521103432300203700ustar00rootroot00000000000000#[[ This file handles importing the DFP (decimal floating point) library for decimal128 support. It is patterned after ImportBSON in this same directory. Initially, the only supported DFP implementation is Intel DFP. However, this module will allow for the future addition of support for libdfp. This file defines, exports, and installs one INTERFACE target: mongocrypt::intel_dfp. The target(s) from this file are used to link the DFP library correctly for the build configuration of libmongocrypt. At find_package() time, we can resolve these interface targets to link to the DFP library based on the build configurations of libmongocrypt. In the initial implementation both mongo::mongocrypt and mongo::mongocrypt_static must link to mongocrypt::intel_dfp (this is because if we link to the Intel DFP which is vendored with libmongocrypt then we will link the object files directly and if we use the system Intel DFP then we will be linking with .a static library archives). The default behavior is to use the Intel DFP which is vendored in this repository. By setting MONGOCRYPT_DFP_DIR=USE-SYSTEM the build will assume that an appropriate Intel DFP implementation can be found in a location where it has been installed system-wide (most likely under /usr or /usr/local). ]] if (DEFINED MONGOCRYPT_DFP_DIR AND NOT MONGOCRYPT_DFP_DIR STREQUAL "USE-SYSTEM") message (FATAL_ERROR "The only valid value for MONGOCRYPT_DFP_DIR is USE-SYSTEM") endif () function (_import_dfp) find_library (_MONGOCRYPT_SYSTEM_INTEL_DFP_STATIC "${CMAKE_STATIC_LIBRARY_PREFIX}bidgcc000${CMAKE_STATIC_LIBRARY_SUFFIX}") find_path (_MONGOCRYPT_SYSTEM_INTEL_DFP_INCLUDE_DIR bid_conf.h) add_library (intel_dfp STATIC IMPORTED) set_target_properties (intel_dfp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_MONGOCRYPT_SYSTEM_INTEL_DFP_INCLUDE_DIR}" ) set_property (TARGET intel_dfp PROPERTY IMPORTED_LOCATION "${_MONGOCRYPT_SYSTEM_INTEL_DFP_STATIC}") set_property ( CACHE _MONGOCRYPT_SYSTEM_INTEL_DFP_INCLUDE_DIR PROPERTY ADVANCED TRUE ) endfunction () if (NOT DEFINED MONGOCRYPT_DFP_DIR) # The user did not provide a MONGOCRYPT_DFP_DIR, so we'll set one up include (IntelDFP) elseif (MONGOCRYPT_DFP_DIR STREQUAL "USE-SYSTEM") message (STATUS "NOTE: Using system-wide Intel DFP library. This is intended only for package maintainers.") set (USE_SYSTEM_INTEL_DFP "ON") # Do the import in a function to isolate variable scope _import_dfp () # Define interface targets to be used to control the DFP used at both build and import time. # Refer to mongocrypt-config.cmake to see how these targets are used by consumers add_library (_mongocrypt-intel_dfp INTERFACE) add_library (mongocrypt::intel_dfp ALIAS _mongocrypt-intel_dfp) install ( TARGETS _mongocrypt-intel_dfp EXPORT mongocrypt_targets ) # Link to Intel DFP, only exporting that usage for the local build tree. # The mongocrypt-config file will later add the appropriate link library for downstream # users during find_package() target_link_libraries (_mongocrypt-intel_dfp INTERFACE $) # Notify in-tree consumers that IntelDFP is available: target_compile_definitions (_mongocrypt-intel_dfp INTERFACE $) endif () libmongocrypt-1.19.0/cmake/IntelDFP.cmake000066400000000000000000000200761521103432300201600ustar00rootroot00000000000000 include (FetchContent) # When updating the version of IntelDFP, also update the version in etc/purls.txt set (_default_url "${PROJECT_SOURCE_DIR}/third-party/IntelRDFPMathLib20U2.tar.xz") set (INTEL_DFP_LIBRARY_URL "${_default_url}" CACHE STRING "The URL of an Intel DFP library to use") set (INTEL_DFP_LIBRARY_URL_HASH "SHA256=ac157e69c05556f3fa468ab34caeb1114a3b88ae18241bd41cc57b85a02dd314" CACHE STRING "The hash of the archive that lives at INTEL_DFP_LIBRARY_URL (Spelled: =)") option (INTEL_DFP_LIBRARY_PATCH_ENABLED "Whether to apply patches to the Intel DFP library" ON) set (_hash_arg) if (NOT INTEL_DFP_LIBRARY_URL_SHA256 STREQUAL "no-verify") set (_hash_arg URL_HASH "${INTEL_DFP_LIBRARY_URL_HASH}") endif () set (patch_disabled OFF) if (NOT INTEL_DFP_LIBRARY_PATCH_ENABLED) set (patch_disabled ON) endif () include (Patch) make_patch_command (patch_command STRIP_COMPONENTS 4 DIRECTORY "" DISABLED "${patch_disabled}" PATCHES "${PROJECT_SOURCE_DIR}/etc/mongo-inteldfp-s390x.patch" "${PROJECT_SOURCE_DIR}/etc/mongo-inteldfp-MONGOCRYPT-571.patch" "${PROJECT_SOURCE_DIR}/etc/mongo-inteldfp-libmongocrypt-pr-625.patch" "${PROJECT_SOURCE_DIR}/etc/mongo-inteldfp-alpine-arm-fix.patch" ) # NOTE: The applying of the patch expects the correct input directly from the # expanded archive. If the patch needs to be reapplied, you may see errors # about trying to update the intel_dfp component. If you are seeing such # errors, delete the `_deps/` subdirectory in the build tree and # re-run CMake the project. FetchContent_Declare ( intel_dfp URL "${_default_url}" ${_hash_arg} PATCH_COMMAND ${patch_command} --verbose SOURCE_SUBDIR "NO_ADD_SUBDIRECTORY" # Targets are manually defined below. ) FetchContent_GetProperties (intel_dfp) if (NOT intel_dfp_POPULATED) message (STATUS "Obtaining Intel Decimal FP library: ${INTEL_DFP_LIBRARY_URL}") if("${CMAKE_VERSION}" VERSION_LESS "3.18.0") # SOURCE_SUBDIR is not yet supported. FetchContent_Populate(intel_dfp) else() FetchContent_MakeAvailable(intel_dfp) endif() endif () # This list of sources was generated by copying the MongoDB server and removing any unnecessary. # Carefully add sources if more functionality is needed. Bundled sources are checked by static analysis, and may result in a larger binary. # The "" prefix is replaced below. # Refer: https://github.com/mongodb/mongo/blob/e9be40f47a77af1931773ad671d4927c0fe6969a/src/third_party/IntelRDFPMathLib20U1/SConscript set (_dfp_sources "/float128/dpml_exception.c" "/float128/dpml_ux_bid.c" "/float128/dpml_ux_log.c" "/float128/dpml_ux_ops.c" "/float128/dpml_ux_ops_64.c" "/src/bid128.c" "/src/bid128_2_str_tables.c" "/src/bid128_add.c" "/src/bid128_compare.c" "/src/bid128_div.c" "/src/bid128_fma.c" "/src/bid128_fmod.c" "/src/bid128_log10.c" "/src/bid128_log2.c" "/src/bid128_modf.c" "/src/bid128_mul.c" "/src/bid128_noncomp.c" "/src/bid128_round_integral.c" "/src/bid128_scalb.c" "/src/bid128_scalbl.c" "/src/bid128_string.c" "/src/bid128_to_int64.c" "/src/bid64_to_bid128.c" "/src/bid_binarydecimal.c" "/src/bid_convert_data.c" "/src/bid_decimal_data.c" "/src/bid_flag_operations.c" "/src/bid_round.c" ) # Put in the actual library path: string (REPLACE "" "${intel_dfp_SOURCE_DIR}/LIBRARY" _dfp_sources "${_dfp_sources}") #[[ Intel DFP gives us a very blunt yet powerful hammer to avoid symbol collision, since other library may also want a conflicting DFP version: Just rename everything! All function names are #defined with a `bid` or `binary` prefix, and are aliased to their "actual" names with a `__bid` or `__binary` prefix, respectively. So we can ship our own decimal library without worry, we'll rename those hidden symbols. ]] file (READ "${intel_dfp_SOURCE_DIR}/LIBRARY/src/bid_conf.h" dfp_conf_content) string (REGEX REPLACE #[[ Match every "#define X Y" where X begins with `"bid" or "binary", and Y begins with "__bid" or "__binary". X and Y must be separated by one or more spaces. ]] "#define ((bid|binary)[^ ]+ +)__(bid|binary)([^ +])" # Replace Y with "__mongocrypt_bid" or "__mongocrypt_binary" as the new prefix. "#define \\1 __mongocrypt_\\3\\4" new_content "${dfp_conf_content}" ) if (NOT new_content STREQUAL dfp_conf_content) # Only rewrite the file if we changed anything, otherwise we update build # input timestamps and will trigger a rebuild of DFP. file (WRITE "${intel_dfp_SOURCE_DIR}/LIBRARY/src/bid_conf.h" "${new_content}") endif () # Define the object library add_library (intel_dfp_obj OBJECT ${_dfp_sources}) # Build with -fPIC, since these objects may go into a static OR dynamic library. set_property (TARGET intel_dfp_obj PROPERTY POSITION_INDEPENDENT_CODE TRUE) # DFP needs information about the build target platform. Compute that: set (proc_lower $) set (ia32_list i386 i486 i586 i686 pentium3 pentium4 athlon geode emscripted x86 arm) set (efi2_list aarch64 arm64 x86_64 ppc64le riscv64) set (is_linux $) set (is_windows $) set (is_unix $) # These compiler definitions may seem a bit strange, but the whole DFP library's # config process is strange. These options match those used in MongoDB server. target_compile_definitions (intel_dfp_obj PUBLIC DECIMAL_CALL_BY_REFERENCE=0 DECIMAL_GLOBAL_ROUNDING=0 DECIMAL_GLOBAL_EXCEPTION_FLAGS=0 UNCHANGED_BINARY_STATUS_FLAGS=0 USE_COMPILER_F128_TYPE=0 USE_COMPILER_F80_TYPE=0 USE_NATIVE_QUAD_TYPE=0 $<${is_unix}:LINUX=1> $<$:mach=1> $<$:freebsd=1> $<$:linux=1> $<${is_windows}: WINDOWS=1 WNT=1 winnt=1 > $<$: IA32=1 ia32=1 > $<$: EFI2=1 efi2=1 > $<$: s390x=1 BID_BIG_ENDIAN=1 > ) # Suppress warnings in the Intel library, as it generates a lot that aren't of interest target_compile_options (intel_dfp_obj PRIVATE -w) target_include_directories(intel_dfp_obj PUBLIC ${intel_dfp_SOURCE_DIR}/LIBRARY/src) # Define an interface library that attaches the built TUs to the consumer add_library (_mongocrypt_intel_dfp INTERFACE) add_library (mongocrypt::intel_dfp ALIAS _mongocrypt_intel_dfp) # Notify in-tree consumers that IntelDFP is available: target_compile_definitions (_mongocrypt_intel_dfp INTERFACE $) target_sources (_mongocrypt_intel_dfp #[[ For targets *within this build* that link with mongocrypt::intel_dfp, inject the generated TUs (object files) from the intel_dfp_obj library. This will be stripped out of the interface library when it is installed, since we don't want to ship the DFP object separately. Instead, users will link to libmongocrypt, which will contain the necessary TUs for the library (because they link to this interface library). ]] INTERFACE $> ) target_link_libraries (_mongocrypt_intel_dfp INTERFACE $ # We do want to propagate an interface requirement: Some platforms need a # separate link library to support special math functions. $<$:m> ) # Give the installed target a name to indicate its hidden-ness set_property (TARGET _mongocrypt_intel_dfp PROPERTY EXPORT_NAME private::intel_dfp_interface) install (TARGETS _mongocrypt_intel_dfp EXPORT mongocrypt_targets) libmongocrypt-1.19.0/cmake/LTO.cmake000066400000000000000000000034651521103432300172140ustar00rootroot00000000000000 set (MONGO_LTO "OFF" CACHE STRING "Enable cross-translation unit optimizations (A.K.A. IPO/LTO/LTCG) [OFF/DEFAULT/FAT/THIN]" ) set_property (CACHE MONGO_LTO PROPERTY STRINGS OFF DEFAULT FAT THIN) if (MONGO_LTO STREQUAL "OFF") # Nothing to do return () endif () # CMake will know if LTO is supported at any basic level include (CheckIPOSupported) check_ipo_supported (RESULT supported OUTPUT out) if (NOT supported) message (SEND_ERROR "LTO is not supported by the compiler (requested by MONGO_LTO=${MONGO_LTO}):\n${out}") return () endif () # Set the appropriate compile/link flags for LTO: set (_c_flags) set (_link_flags) if (MONGO_LTO STREQUAL "DEFAULT") # Just use CMake's default INTERPROCEDURAL_OPTIMIZATION message (STATUS "Enabling INTERPROCEDURAL_OPTIMIZATION") set_property (DIRECTORY PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) elseif (MONGO_LTO STREQUAL "THIN") set (_c_flags -flto=thin) set (_link_flags -flto=thin) elseif (MONGO_LTO STREQUAL "FAT") set (_c_flags -flto -ffat-lto-objects) set (_link_flags -flto=auto) else () message (SEND_ERROR "Unknown MONGO_LTO setting '${MONGO_LTO}'") return () endif () # We need try_compile(), because we need more than one source file to accurately # check for LTO support try_compile ( MONGO_HAVE_LTO_${MONGO_LTO} "${CMAKE_CURRENT_BINARY_DIR}/_mongo-lto-check/${MONGO_LTO}" SOURCES "${CMAKE_CURRENT_LIST_DIR}/ltocheck-lib.c" "${CMAKE_CURRENT_LIST_DIR}/ltocheck-main.c" COMPILE_DEFINITIONS ${_c_flags} LINK_LIBRARIES ${_link_flags} OUTPUT_VARIABLE out ) if (NOT MONGO_HAVE_LTO_${MONGO_LTO}) message (SEND_ERROR "MONGO_LTO=${MONGO_LTO} is not supported by the current compiler:\n${out}") return () endif () add_compile_options (${_c_flags}) link_libraries (${_link_flags}) libmongocrypt-1.19.0/cmake/MongoC-Warnings.cmake000066400000000000000000000055611521103432300215250ustar00rootroot00000000000000#[[ This file sets warning options for the directories in which it is include()'d These warnings are intended to be ported to each supported platform, and especially for high-value warnings that are very likely to catch latent bugs early in the process before the code is even run. ]] set (__is_gnu "$") set (__is_clang "$,$>") set (__is_gnu_like "$") set (__is_msvc "$") # "Old" GNU is GCC < 5, which is missing several warning options set (__is_old_gnu "$,5>>") set (__not_old_gnu "$") #[[ Define additional compile options, conditional on the compiler being used. Each option should be prefixed by `gnu:`, `clang:`, `msvc:`, or `gnu-like:`. Those options will be conditionally enabled for GCC, Clang, or MSVC. These options are attached to the source directory and its children. ]] function (mongoc_add_platform_compile_options) foreach (opt IN LISTS ARGV) if (NOT opt MATCHES "^(gnu-like|gnu|clang|msvc):(.*)") message (SEND_ERROR "Invalid option '${opt}' (Should be prefixed by 'msvc:', 'gnu:', 'clang:', or 'gnu-like:'") continue () endif () if (CMAKE_MATCH_1 STREQUAL "gnu-like") add_compile_options ("$<${__is_gnu_like}:${CMAKE_MATCH_2}>") elseif (CMAKE_MATCH_1 STREQUAL gnu) add_compile_options ("$<${__is_gnu}:${CMAKE_MATCH_2}>") elseif (CMAKE_MATCH_1 STREQUAL clang) add_compile_options ("$<${__is_clang}:${CMAKE_MATCH_2}>") elseif (CMAKE_MATCH_1 STREQUAL "msvc") add_compile_options ("$<${__is_msvc}:${CMAKE_MATCH_2}>") else () message (SEND_ERROR "Invalid option to mongoc_add_platform_compile_options(): '${opt}'") endif () endforeach () endfunction () set (is_c_lang "$") # These below warnings should always be unconditional hard errors, as the code is # almost definitely broken mongoc_add_platform_compile_options ( # Implicit function or variable declarations gnu-like:$<${is_c_lang}:-Werror=implicit> msvc:/we4013 msvc:/we4431 # Missing return types/statements gnu-like:-Werror=return-type msvc:/we4716 # Incompatible pointer types gnu-like:$<$:-Werror=incompatible-pointer-types> msvc:/we4113 # Integral/pointer conversions gnu-like:$<$:-Werror=int-conversion> msvc:/we4047 # Discarding qualifiers gnu:$<$:-Werror=discarded-qualifiers> clang:$<${is_c_lang}:-Werror=ignored-qualifiers> msvc:/we4090 # Definite use of uninitialized value gnu-like:-Werror=uninitialized msvc:/we4700 # Aside: Disable CRT insecurity warnings msvc:/D_CRT_SECURE_NO_WARNINGS ) libmongocrypt-1.19.0/cmake/Patch.cmake000066400000000000000000000034541521103432300176130ustar00rootroot00000000000000find_program(GIT_EXECUTABLE git) find_program(PATCH_EXECUTABLE patch) #[[ Form a new Patch-applying command for the given inputs make_patch_command( [DISABLED ] [DIRECTORY ] [STRIP_COMPONENTS ] PATCHES [ ...] ) ]] function(make_patch_command out) cmake_parse_arguments(PARSE_ARGV 1 patch "" "DIRECTORY;STRIP_COMPONENTS;DISABLED" "PATCHES") if(patch_DISABLED) # Use a placeholder "no-op" patch command. set(cmd "${CMAKE_COMMAND}" "-E" "true") elseif(GIT_EXECUTABLE) # git ... set(cmd ${GIT_EXECUTABLE}) if(patch_DIRECTORY) # git --work-tree=... list(APPEND cmd --work-tree=${patch_DIRECTORY}) endif() # git ... apply ... list(APPEND cmd apply) # git ... apply -pN ... if(patch_STRIP_COMPONENTS) list(APPEND cmd -p${patch_STRIP_COMPONENTS}) endif() # Ignore whitespace errors to fix patch errors on Windows: The patch file may be converted to \r\n by git, but libbson fetched with \n. list(APPEND cmd "--ignore-whitespace") # git accepts patch filepaths as positional arguments list(APPEND cmd ${patch_PATCHES}) else() # patch ... set(cmd ${PATCH_EXECUTABLE}) if(patch_DIRECTORY) # patch --dir=... list(APPEND cmd --dir=${patch_DIRECTORY}) endif() # patch ... -pN ... if(patch_STRIP_COMPONENTS) list(APPEND cmd -p${patch_STRIP_COMPONENTS}) endif() # Prepend "--input=" to each patch filepath and add them to the argv list(TRANSFORM patch_PATCHES PREPEND "--input=") list(APPEND cmd ${patch_PATCHES}) endif() set("${out}" "${cmd}" PARENT_SCOPE) endfunction() libmongocrypt-1.19.0/cmake/Platform.cmake000066400000000000000000000027311521103432300203350ustar00rootroot00000000000000#[[ Defines a platform-support target _mongocrypt::platform. This target sets certain internal-only compile definitions, and defines usage requirements on certain platform features required by libmongocrypt (Threads, dlopen(), math) ]] add_library (lmc-platform INTERFACE) add_library (mongocrypt::platform ALIAS lmc-platform) install (TARGETS lmc-platform EXPORT mongocrypt_targets) set_property ( TARGET lmc-platform PROPERTY EXPORT_NAME mongocrypt::platform ) # Threads: find_package (Threads REQUIRED) # Special math: if (NOT APPLE) find_library (M_LIBRARY m) endif () # Special runtime: find_library (RT_LIBRARY rt) # Endian detection: if (DEFINED CMAKE_C_BYTE_ORDER) # Newer CMake knows this immediately: set (MONGOCRYPT_ENDIAN_DEF "MONGOCRYPT_${CMAKE_C_BYTE_ORDER}") else () include (TestBigEndian) test_big_endian (_is_big) set (MONGOCRYPT_ENDIAN_DEF "MONGOCRYPT_$_ENDIAN") endif () target_compile_definitions (lmc-platform INTERFACE "$" ) target_link_libraries (lmc-platform INTERFACE Threads::Threads # These are build-interface libs, but still required. These will be added # to the platform library in mongocrypt-config.cmake using the same # find_library() calls: $ $:${M_LIBRARY}>> $:${RT_LIBRARY}>> ) libmongocrypt-1.19.0/cmake/libmongocrypt-hidden-symbols.map000066400000000000000000000001431521103432300240500ustar00rootroot00000000000000mongocrypt { local: bson*; bcon*; jsonsl*; _*; kms*; };libmongocrypt-1.19.0/cmake/libmongocrypt-hidden-symbols.txt000066400000000000000000000000401521103432300241060ustar00rootroot00000000000000_bson* _bcon* _jsonsl* __* _kms*libmongocrypt-1.19.0/cmake/libmongocrypt-static.pc.in000066400000000000000000000004411521103432300226510ustar00rootroot00000000000000Name: ${PROJECT_NAME} Description: ${PROJECT_DESCRIPTION} Version: ${PROJECT_VERSION} Requires: ${PKG_CONFIG_STATIC_REQUIRES} prefix=${CMAKE_INSTALL_PREFIX} includedir=${PKG_CONFIG_INCLUDEDIR} libdir=${PKG_CONFIG_LIBDIR} Libs: ${PKG_CONFIG_STATIC_LIBS} Cflags: ${PKG_CONFIG_STATIC_CFLAGS} libmongocrypt-1.19.0/cmake/libmongocrypt.pc.in000066400000000000000000000004751521103432300213730ustar00rootroot00000000000000Name: ${PROJECT_NAME} Description: ${PROJECT_DESCRIPTION} Version: ${PROJECT_VERSION} Requires: ${PKG_CONFIG_REQUIRES} Requires.private: ${PKG_CONFIG_REQUIRES_PRIVATE} prefix=${CMAKE_INSTALL_PREFIX} includedir=${PKG_CONFIG_INCLUDEDIR} libdir=${PKG_CONFIG_LIBDIR} Libs: ${PKG_CONFIG_LIBS} Cflags: ${PKG_CONFIG_CFLAGS} libmongocrypt-1.19.0/cmake/ltocheck-lib.c000066400000000000000000000000661521103432300202520ustar00rootroot00000000000000 extern int answer (int a, int b) { return a + b; }libmongocrypt-1.19.0/cmake/ltocheck-main.c000066400000000000000000000001331521103432300204230ustar00rootroot00000000000000extern int answer (int, int); int main () { int a = answer (3, 4); return a != 7; } libmongocrypt-1.19.0/cmake/mongocrypt-config.cmake000066400000000000000000000050571521103432300222210ustar00rootroot00000000000000include(CMakeFindDependencyMacro) find_dependency(kms_message 0.0.1) include("${CMAKE_CURRENT_LIST_DIR}/mongocrypt_targets.cmake") if (DEFINED MONGOCRYPT_LIBBSON_STATIC_USE) # The user has named a library that should be linked as the static libbson library set_property ( TARGET mongo::_mongocrypt-libbson_for_static APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$" ) endif () # BOOL: Whether the libmongocrypt dynamic library in this package needs to link to an external libbson. # In the default configuration, the shared lib will include the TUs for a pinned version of libbson # and will use linker scripts to "hide" these symbols from the outside world. # # If the libmongocrypt package was built to link against a shared libbson library, then the # libmongocrypt dynamic library will contain pending references to libbson symbols that will # need to be resolved before the library can be used. # # (Note: static libmongocrypt *always* needs to link against an external libbson, as it does not # embed the libbson symbols.) set (_using_shared_libbson "@USE_SHARED_LIBBSON@") if (_using_shared_libbson AND DEFINED MONGOCRYPT_LIBBSON_SHARED_USE) # The user has named a library that should be linked as the shared libbson library set_property ( TARGET mongo::_mongocrypt-libbson_for_shared APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$" ) endif () set (_using_system_intel_dfp "@USE_SYSTEM_INTEL_DFP@") if (_using_system_intel_dfp) find_library (_MONGOCRYPT_SYSTEM_INTEL_DFP_STATIC "${CMAKE_STATIC_LIBRARY_PREFIX}bidgcc000${CMAKE_STATIC_LIBRARY_SUFFIX}") set_property ( TARGET mongo::_mongocrypt-intel_dfp PROPERTY IMPORTED_LOCATION "${_MONGOCRYPT_SYSTEM_INTEL_DFP_STATIC}" ) endif () find_dependency(Threads) # Link for dlopen(): set_property (TARGET mongo::mongocrypt::platform APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) # Link for special math functions: if (NOT APPLE) find_library (_MONGOCRYPT_M_LIBRARY m) mark_as_advanced (_MONGOCRYPT_M_LIBRARY) if (_MONGOCRYPT_M_LIBRARY) set_property (TARGET mongo::mongocrypt::platform APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${_MONGOCRYPT_M_LIBRARY}") endif () endif () # Special runtime: find_library (_MONGOCRYPT_RT_LIBRARY rt) mark_as_advanced (_MONGOCRYPT_RT_LIBRARY) if (_MONGOCRYPT_RT_LIBRARY) set_property (TARGET mongo::mongocrypt::platform APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${_MONGOCRYPT_RT_LIBRARY}") endif () libmongocrypt-1.19.0/doc/000077500000000000000000000000001521103432300152315ustar00rootroot00000000000000libmongocrypt-1.19.0/doc/img/000077500000000000000000000000001521103432300160055ustar00rootroot00000000000000libmongocrypt-1.19.0/doc/img/cli-icon.png000066400000000000000000000016341521103432300202140ustar00rootroot00000000000000‰PNG  IHDR ¤[AÔ cHRMz&€„ú€èu0ê`:˜pœºQ<PLTE§¦®~}‰UTcùøúÐÏÔÿÿÿkîbKGDøoéÇtIMEè "D”¿t zTXtRaw profile type xmp(ÏuRKrÅ ÛsŠÈÆ&ÇI°ëL—=~e’¾¤y-žül#K"éëã3½ÅH’‡WÙ¼ZólÍŠ«-Èñmë.Q“ØbjÃ`E¶=ÿìr:a˜|-¥j+Yņs#²ºäyAzÞ#H7Ù T“Úmþ^ Õ•‘eãÌás¡;›ÐçÇEÖŒ$YÀxo;Ÿâ+aIÛ+Z ˆòÉåÎHÕJr1abÒVºÐÉðh /àl: iÄ/ kxMQdÉšœúi7ßéŸ5 >ö„´eš8^©?ü Ø4œY­²ÒéÔ#¨ÿ7Þ½Á¸º³›“®î\ölŽÃU‹Ú̾üuQIßI-›aËàorNTÏ¢wš/IDAT×c`!% P„SŒŠYL©‚)a&0e äâV)(¨ˆ¦b>;»~iµOPeXIfMM*‡i&    AÖ%tEXtdate:create2024-06-11T12:34:10+00:00Eß6ë%tEXtdate:modify2024-06-11T12:34:10+00:004‚ŽW(tEXtdate:timestamp2024-06-11T12:34:19+00:00öê[tEXtexif:ColorSpace1›ItEXtexif:ExifOffset38­¸¾#tEXtexif:PixelXDimension23æ°fÀtEXtexif:PixelYDimension25ÒEptEXttiff:Orientation1·«ü;IEND®B`‚libmongocrypt-1.19.0/doc/img/reference-targets.png000066400000000000000000000362331521103432300221270ustar00rootroot00000000000000‰PNG  IHDR޲>`‚sBITÛáOà’zTXtRaw profile type APP1™UŽK Ã0 D÷>EŽ0úX¶ŽS‚¥ ¹ÿ¢rí.2ƒ1ˆ‡ÒÞ>í>ÖåºÏíx·´üäšÔÕù bˆ!PO”YÙHóZ ±ÐìÊqabTbþ,ÀGÇ@8Œjν'½®O–R•þÝd‹IaËáüj[Ñô®Š,ød„Ï„ IDATxœíÝy\Tåþðï9³Â 0ì «È¢¦"®i^Í%µp7µÍÌn«Õï–-×ÛbjYni«f¶jjeJæR¢)¹ç®€" ¢‚ƒ #0Ìœùýqj"gÆ9ƒŸ÷Ë?fžsæ{žgÎgÎ:ŒÑ`f"bˆl ÑÍf#"þ1¸ëî@SÕ‚†¨4ñµMf³¹\«3LüAk¸9†Q(¼T2™ìïÆz§•™Íæ¢ó—Ôê`?–Å67ÀÍÃqœNWYRR¥¶§uý0.×êÔêà€rà&cY6 @¥V—ku7Ö›I¯7ªT¾7·cð7•ÊW¯7ÚŸÖj›Í†íi7bY¶îébHeACT¢@ÐÕ‚†¨4D5€ !ª Q Dǹ» ×W[[Ûô ­cÄYnJGbÕ—5=C+Y·ª~®ÜË`0˜L¦šš×-",,,==ÝÁ"*•*$$$""¢Á©­cd(#³Žjª\JSB»eÏf¬áÝf€ïmü{>ÓàÔV².naتŽã*++]ú©ê,:.77·ÁI­cÄY¨ªØµ9í$†s¿—þòlƒ“Zɺ¸µ!ª„¥uÜÙ·uŒ‚ØÖ°ß±•¬‹[›ûÿÍæ®ªºZo6×´ôh ˲2™T©T¨T>2™ÔE=p#7GuIIYEÅU??Ÿ @™LÚÒoÇ™Í5ÕzC~þy?µ:ØEýp·í±X,ùùç-KÛØ¨à //ù ì¥aYÖËKÐ66Ê^н½ùúôéÕÖb±ÔÔÔDEµíÛw@Óó?~bÓ¦ÍD´jÕš¨¨¶~ø1¿ùæì3grˆhÞ¼QQmׯÿÉõ}‡Ö@"‘„„„ÄÄÄ„„„ˆD"ww§e|}}Õjµ——WÝFµZ­P(ÜÕ«ñi?Ö/yŠ2aøµ“”‰#ü’§ø´}ó{ucX–U«ÕÁÁØ”ºqnÛª.*º¤ðö P¥¦nÎÎÎkbÎÆÑt5–eCC‚´Z]QÑ¥¸¸h§öÔ3¼òÊÿâGŽüǵsçï_½B©T¶oßÎ]¬U«Ö”–^~úé'•JÏøì¾™âã㣢¢ìOÍfóéÓ§u:]/”°°°ˆˆˆ3gÎÿâopppLLL^^ž^¯wcßš)dð»Ò ö6kMÞ¢p«±ÜÞ.òм/•a%fMVUÎÏnìaó‰ÅâöíÛ›L¦²²ë\QqOT—””I¥’€egçeg;甿€U­ÅRRRv«í ?uêtNNîÿþ÷J½ö¸/>>¾k×.né•À}÷Ý™“'?äéQ““£Ñh|||:vì(‘H/uõêÕ³gÏZ­ÖàààØØØŽ;Š›Ïf³]ÌÏ*<}$4:!¶C‰LîxMF$õíü@Å¡Oì-~dX×¾?­`]´>nˆj³¹¦¢âjÛØ¨ëÏÚ¤²²r•Ê·Þ_RpP@Aaq@€_cg™Y­Ö/¾ø*,,¬[·äM›¶TTèúöí3`À…iiÛ¬VnìØÑáöùÏŸ?¿{÷Þ‚‚ÂÄÄ„1cFÉår"ª­­ýê«o""»wï¾qã&£Ñ8vì蘘û«vîüýÀ?üýUãÆùí·R©tâÄñü$N·wï¾cÇN„‡«SRî WÛ_uòdÆöí;X–5ª½^ùù7nª­­8p`ÏžÝëNJMÝà?`Àõ^röl~VV–\.ëÞ½ßÂqÖuëROŸ>ݦM›qãÆò)uèÐáãÇO¤¤ÜsòäÉãÇOªÕa£G ؼyKfæ©Ûoï5pàuwfddîÝ»¯ªªª[·®C†ÜÅ7mݺ­Gî‰dûöAAcÆŒòóóã§ÖÖÖ¦¦nÈÉɉ½ûîa©©?ÝvÛmýúõm¢ }Ò®]{**´:u=z¤H$â×`HHHbbÂÏ?o˜õz½J¥ò÷÷ ¼|ù²3z]ŸÓGáéë>ÕWU´ï>àìÉ}gOìK™2ÝÁ‚6‹‘³˜UÉSþÕÉS¬ÆrVêã`ñÆ´ŽuÑúÜ`TçŸ-Š‹iNãµtº*??ŸzG¦ÇÞüÍ몪êùó?Òë f³y̘”¡CÚ'±,ëçç£ÓU…†6øZ«Õ:gÎ\•Je±X ÇqË–-ôÑG~üq=ÿtÁ‚÷>ûlé=÷ #¢;ÒüI‹ÅÊ'ħŸ~¶fÍ·áfsÍœ9sCBB,–Zî*Çq‹øé§Ÿ ~Í»`éÒe|g–/ÿ¢²²*::Šêââ cÆŒ×h4|ÁE‹>øì³%ýû÷#¢M›6ÿç?Ó,+˲Ÿ~úY½n—••¥¤Œ2™LÇ-Y²lÞ¼wxà>ûˆ6lØ8r䱸þÚûìówßÏqœX,²X¬ß~»zíÚ56›mΜ¹þþþ55f½Þ½~ýOÙÙgz÷¾½cÇÛˆèõ×ß,-½èõœsú}V°.œH¯7j4W o…Â[¡ðºávÇÝÈieùg‹–.]•–¶»ncZÚî¥KWåŸ-ºîË««õJ…÷µí¯¿>­C‡Äæt`×®ý‰dñâ·Ÿ|ò‘ŸÞZoªRá]]}cQz½~É’òòNÏœù}ýõŠéÓ_ÎÉÉZ´hÍfÛ¼ù"ºxñÒÔ©Ï©Tª/¿üìøñC=öhAAáo¼i/¢Õ–¿ýö¬¼¼Ó ̵Ùl[¶üJDgÏž]¶ly``À²eKrr²žxâñº·Ê›2å1F3sæYYÇçÏ·¦¦fÚ´Wª«õf³yÖ¬9V+÷Ö[3³³3Þ{o>ÿ*>8‰Èd2½óÎ[gÎd¾÷Þ|–eæÎ]`ÿÜÜ·o¿F£¹÷Þ±ÍyëbbbŽ9ðûï¿ýë_wäää®\¹Ê>)11>3óØìËÍÍ+**:p`ÏáÃû»tIÊÎ>“Ÿ_@D[¶üúÉ'Ÿvíšü믛vîÜÖ¯_ß;Ò¿üòk{N·iÓ†Ó§3xྚššmÛ~#¢ï¿_{øð‘¤¤Î[¶üœ‘q´K—¿wÈ7QðìÙüy󄇇¯_ÿÉGFŽqäÈÑï¾ûaEEŘ1£×¬ùvðàcÆŒ"¢´´íDtútö¥K%·ßÞ+44”Ÿ“a˜;ÒŽ90aÂø«W¯.\¸¨}ûvgÎduêÔ‘ˆöîMŸ5kFsÞ:â8N¯×‘D"ñõõ­ªrôfüç²Ùl®Û¨×ëKJJ/Þ WŒ‚~[ü"\D"óz|ö ¹·™ô•ro¥Lî膕\=ù-©’§ð-üƒÊÌ5DŽÞݬA®[žH¯7ëõF¦œpcíNq#Q3tX¿mi{ìi–¶{[Úž¡Ãú5g«Úl®¹vïtjê–ììÜf¦õ°a_{í†a ƒOýÿ2™Ôl¾Î}yÔjõ A¤RéäÉ“† œ2e²\.¿÷Þ±‰dß¾}DtàÀF£qèÐ!C‡ñ÷÷íµé^^^;wþnÏÈ6mÚŒ5R*•N˜p¯X,>pàíÜù;ÇqC‡>ü¹\þÈ#Ã0Dtñâ¥ÜÜ¼ØØ6?þo¥Rùàƒ÷÷ìÙ]£Ñ9r$33K£Ñ$$Ä?úè#ÞÞÞ£GT«ÕöQTTÔøñã¼¼¼î»oB§N***²²Nñ“Ö¯ÿ)6¶M×®É×}߈hòäI¡¡¡mÛÆ>ùäãD´{÷û¤‘#GøùùED„ß}÷P"zøá‡"##BCC‡ LDûöí'¢;Ó‰èßÿžÒ±ãmqqm_xá?DijáÝ}÷ÐN:*ÞcÇŽæßC"Ú±c'=þø¿“’:«TªûîoZ·oÿÍb±~O¯^=ýüüþûß—yäaûõðpõ¼yïôë×7,,lôèQ ÃlÛ¶ˆ~ûmõ÷éuýúý+11!88øÍ7_cf÷î½ö/@žŽ¿ÿ”Túçÿ&¹\îø-”ù}37ó~Ñ®O©TÖáíÝÀ‚ÓÙ8nÇKoö1NHSsÙ)³&Ó/iÊVì×ù!SɱšrWÝÏËuëÂéõÿؤÑ\¹±v§¸ÁàÆõ'¢mi~Ðó9Í7^Çq×^—Õ¡Cb‡‰©©››³œ?>]^®]½zýÔ©SêMåwu6§'D$•J†aÙ?ÄŠD"©TÂ'ýÉ“Dtûí½ìsvë–¼oßüü‚ØØØºEÄb±D"©©©%¢ .QÏž=®]V½‚DÔ§Oï}ûäææ………Qݯ}ÕµºwïvâÄÉK—JˆÈ`0lݺmêÔ'›9ÞºEˆˆ/R<Þ~pš¿â¥Á÷¤[·®R©´Áò‹-}Oê,*:ODö£òmÚÄÌ™3›þú4‹ÿ>G!""¼gχ..¾°}û±Xĉ¨G¥RÅÅÅ={öÊ•òà`n`Ý4üÿ#Žãø•Õàÿ¬–2™LôW`ß®/77·¤äï¿ð¶mÛÖ=¡ÄEvý´\éØñö!N©ÆJ¼uÇ¿ ö¾2!…ˆDв]o±W}çpݺðDõ¢Wñ×Îà–¶;Åÿ‡¬›ÖÍÏiú+Jëý¼þú´ÔÔÍ©©[øÇööwÞy¿Á"UUÕóæ}”’rW§NêMrÖŸÊUEE…½…~{·1¾¾¾D¤Ó]m¼àßW¼ðÃÃÕJ¥’ˆ®^màU×*//'"oo/"Úºu›Á`7nLs^øÏ"Zú+P›ïgEE…ZFDeeWjjj¢£¯suÜ_ï‰.2²þ]ø›(È¿J£iÖÕcÆŒ:tèðêÕkNžÌ0 ¿¿¿ÿµópWZZJÎþ/äFR©T,WTTð{,u:]½+‰o¿Ã³ÞFXXXHHHii©F£q°þµ\1 w9´ý‡«å¥#{ÃY‘ìjƪ! ü’§‘ÍbªÌ\#ò pVýzZÓºp\HHPaa±ý©ýs£¥íNáP¤ Öè°~-Êijdõ¤ISùœ&¢ÔÔ-ö V0™Ì ~Ò£Gòðá |ump{ÓÜ#ÊŸ£´gÏ^þé¥K%EEç###ƒ‚>a—OD{öü¹¿¡î¥¨íÚ%ŠD¢þàOرÙlüqˆ’““ãââ†9xðÕj%"“Éd0üãp{uu•}Çû‰¨mÛX"Z¿þ§ž=»_7,íìgðîßÀ^¤A ¾'·ÝÖê¼'|‘ëî{ÿë=ùóUu¿¬4Q°]»D":xð°}RJʨo¾YÙà"FŒH‹ÅË—i³ÙFYwRuu5ÿ ;ûLuuuXXŸCü÷9‹ÅÚtçN­VØl¶ââb‘HÔàw”1™L&“I¥Rñ_•ˆˆa˜ØØØÀÀ@×]©åôQ¸Å©ƒÛ‹²¥<ò?VäÌ}VÕêœMÊÄÊÄUg6XM×ZǺp …Â+66*$$`?M¬¥íNáèŸT‹Bš§T*ªõ/¯F/:¼î>ð;÷]ºtyûö]DôÜs%'w²O­Öœr¥ì=÷Ü}ÛmvìH?þþ;îè³rå·D4mÚš~UJÊÝáé黯›0hРõëS靨 }øá‡¾ùfå€wMœ8~ãÆÍçÎM˜p/¿­9dÈ]Û¶m4hèøñ÷îÜ™®ÕVP¼Ôj+xàá!CîÚ´ióåË—ûôé¯ÑhöíÛÏïn¦E‹>¸r¥¼¦¦æûï×ÑäÉ“Zôž<÷Ü3ë֥λàÌ™ooïï¿_«T*üѦ_õØc~÷Ýóæ-wê_Ë£¸ÉDbés Ö»¨¸þìVKÕ%W«/Üá¢Eعn]ˆD¢z?DŸŸÏïG„ëbŒ3ÃCdc&7¯°cǪsî±+”””Y,–Ð "rÊEí.k®ˆÅbçÞ­¬¶¶¶²²*0°¹Ç‡ ƒ··÷•+å>>ÊÝ»÷üûßOxçÊ•_Ñd³Ù4ýj¢º*++YVÔØ^ƒÁPSS£R©ø§#GŽˆ_¶lI D—/_ räû2¿W¹^(6Á`0xyyi4šààà9sÞýüó¯^{múÔ©O5§ Éd2Œ×Ù·jÕšW_}ãÓO?1âÏ›6ìÚµ{Ò¤)÷Ý7á½÷æó«C&“Õ{•F£ ÚfLÝ¥ÂÂÂÒÓÓ›ž‡eY±X|Ýß98p`ƒíE356 º|Ì)õ›Ú-{¶s>o;Ìlx#µ•¬‹[LVVnbŸÇ(ÝscQµ:8?ÿ¼V« P5?†¯K«ÕÕÔÔFE5uÚ× H$ÍÏi‹Årÿý“Ú¶?þÞ’’KË–}ND))ÿ8!™a˜sšþ:—ª1ÞÞÞuÏ÷Ù´é{£±¥7_óCšˆŠ‹/Œ5î‘G&ÝqGŸÔÔ k×®—H$ü5`Í)(—Ëù“ÒsåJùáÃGÞ{oqddd½²vaҌܢ8Ž»nNÀMඟ눉 /*ºTk±8~Â6ÇqeW´55µ11áןەÄbñøñãÞzëþ7¬Äbñ3Ï<}ÿýÝÛ+÷ :ô®Å‹?\´è"ò÷÷ÿðÃEñññΪÿý÷kçÏ_(‹>þøƒk·›<Û¢Z,ÇÅE—””ûùù(ÞŽü^õÕ«Uþþ~Nßž¾1“'Oºÿþ‰§N6íÚµkþyk%“ÉæÏ÷õ×ÿ—™yÊÛÛ«]»DçÞŒbøð{¢£#ûõûW½³U““»üøãwøé=ðtn‹jžZà§ÓU])¯0›kZz$–ee2©R©ˆ‹‹néZ.%•J›yû°[‡¯¯oß¾}\Q96¶Mll›kÛýüüZzÞ€¹9ª‰H&“††6öë·š›ySO×i£ ÎU—’ßL­d]ÜÚÜÕP˲¾¾¾&“Iø§t©TªÆÎËk£ VL>QdÖQе»ÍßÛ¾Œ»•¬‹[›{.Ö€&Ô½XëÖ½;€G@T¢@ÐÕ‚†¨4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ !ª Q hˆjACTšÇDuuµ~ݺÔW_}£¤¤ÔÝ}iþ8pàw÷<˜Ø-KíÐ!‰ã8"’ËewÜÑgР))) …w/™8ñÓ§³“’:›Íæ›ÔKgxÿýˆ¨OŸÞîî¸Ù Ï¿!—ËæÍŸáçqOTÆèè¨É“'™Í5éé¿¿øâÓÓw-]úqcóŸ={633륗^xá…ÿÜÌ~8‹Ù\Ã0Œ»{É=QMDááêÇÿ7=ûìÓýûúõ×­µµµ‰Äf³effíßÀßß?%ånŸœœÜ+¾%¢K—J6oÞ2bÄp"*..NOßeµZ CDûö¸téRÏž=¶lùuüø±¡¡¡:n÷î=.\ìÕ«g݉(/ïìáÃGî¾{ØîÝ{®\¹2bDJXXß›ÍöÛo;rròz÷îÕ½{7û§êµ p\QÑ…ŒŒÓmÚDÝv[¢H$âµÚŠ#GNú(É];{yɉ(#ãtùíÀAÿ"¢«W«Ž>ž˜ADÕÕú“'O])+oß!±]»8¾‚ÕÊegçžOêÜ!¦M”›N涨®Ëßß_£Ñðß}wþ²e˃‚‚ÊËË?øàãM›~ÊÌÌÚ±c'¥§ïº|ùòˆÃûmÇã?­P(Ìfóܹ V¬ø²OŸÞß}÷ÃæÍ[üü|µÚŠÞ½{ÕÔÔŒu¯N§óññ™;wÁk¯MŸ:õ©}ûöϘ1kéÒez½A«ÕΙ3wóæ :uä8nÊ”ÇÒÓw.Xð^BB|ZÚ±XÜà‚ÜúVdzÙlïÌyÿÔ©œ6±Q?|ÿ“——×;ï¾´îÇM?þ¸1**âêÕ«z½qÚ´§zöêš––~üX&ÕšËeŸ}¶ò‘)÷EEGh4W^íÝêj½··×êÕëš4~ôè»+*t¯½úŽÑh Yõí‰íâfÎ|Y,Äÿqp„ÛN+«¬¬:zôؾ}ÞzëcÇŽ¼ÿèÑ?”Jå»ïÎçkZ­Ö§Ÿ~òðáýIIŸþ¥ÚÚÚv;vð®»/^ü¡N§ãg›8qüñã‡6nLµZ­iiۉ觟~NOßõÒK/=zð‡VçææmÙòk jÚĉ6x*ÙLœø sÞ>ðX.”ddœ=úî¹sßxsæË¾¾>§‰è×_w$¶‹{oѬ?™yìXFE>ùøK‹Å²dé¼åŸ/êÞ£Ëk®®Ö:x¼¼¼â¥—¦Î›?cÊ£÷ôÆâóoÖ°À…Üö;33k̘ñüã1cF-Z´€ˆø„óññY»v©TªŒŒúX'NdÆÈȈ7QttÔÉ“üIjDôÔSO°,k6›?ѹs§ßßMDþþþ&“)77Ÿ‡ß2îÒ%I*•ž:ušˆ!63#ûË/V÷îÓãݹ¯7q`»¶¶6/¯ mÛ˜'²ˆÈG©¬©©-.¾Ô¶m ˲«V­4¨_Ÿ>=î¹gðM¸˜Û¢ºk×äE‹”––Nš4Åd2K¥R"**:OD©©ø©`ßz/,**"¢þ8yòÏoÛ6V¯××çâÅK‹¥¨¨èóÏ¿ä[Ìæšz¥ìŸ†:N,‡††ðO{õêIå÷µ òññizh/¾ø<ñiÍ·ØsšŸ·2©T2oþë×mIKKÿñÇ ‰m_~ùY¿_œúSê–ޤ¥¥ûû«^|iªýt=W®h­Vki©fËæí|KTT¸¥¶¶C‡„o¾ôË–í+W®ýê«5ö}òÉÉ"‘Ç\ q[T{{{%$Ä'$Ä;zýúŸŽ;Þ­[×èè("š5ëþýû‘ÕjµŸqcMDcÆŒš>ý¾åÚÙ"##D"Qllì† ëêΓŸŸß`g¢££-KffVrr«ÕúÍ7+›³ ÆØÓšˆúô霻ÒR͹sÅOžðäSïÙýÇ’%_mݺsôè»32Nß9àŽû[Xx~ö¬…«¾]ù?×2©IDATûöœW½½¼¬V«Ù\#“Imdã+²,«V‡ÎyçU¾…ßÙ“››oÐ^ùïs&“yÕ·?nÛö{ß¾½’’nsßXÀ9ÜÿûÅŸ‹Åüaà‰ÇËd²™3ßZ·.uÛ¶í#GŽýá‡ëÍß»w¯„„„+V-]ºlÏž}Ï>ûüË/O¯7T*8qüÑ£Çf̘µwïþÅ‹?:4Åh46Ö‡ î‰D³gÏÙ¶mûìÙsfÍz[«Õ6gAM‹ßÝ}àÀÈi°«ªª^¼èÓ/¿X]|þ¢Él&"™L*•J–}úÍû‹—åääWWëm6’ÊdD”œÜÑf³­ýaÃÑ£«W¯ç+H$âûæææõåšÌÌì×n|ååYfsMöéÜ… —lܸµLsÅb±‘L*uãHÀYÜvhttôý÷O\µjÍŽéƒüúë/^{íiÓ^–J¥ýûÿkذ!õæ—H$_½ü¿ÿ}mþü÷ˆ¨}ûv‹/¼¶ìÌ™3†Y³æûo¾Y®ž>ý//¯ÆúЮ]â'Ÿ|0cÆìÇ{ÊÏÏï¿ÿ}yôè‘DÔœ5ÁÏÈi°KHhûôÔG¾[óÓÞ½ý‡0bÄP±XüÆŒ—.ùúÍóäryRR‡º—ˆºvKêû¯^[·îüå—}ûöÊþ«È#Sî'†Ù±c÷Ö­;|pœL&1rhyyÅúu›W}».&&rò#ÛµwãHÀY£ÁÌ0DÄÙ†ÉÍ+ìØ1êÇu N§P($IóF«ÕªT*›˜Çjµ^½ZàßÌå–—küë½9 h©ªªjŸúTƒQ&“Õ;Àl4š$ñµ—]Y­œÁ`¨WÄf³ Ʀïý—••›˜Ë?vÿVuƒT*Õuçib+ÙN$5?§‰(00àÆÐR׿4y{7ðÇÆßåZ"{m†aÓ­ŒûU@Õ‚†¨4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ !ª Q h üfVV.¹û÷ª€×@Tó¿e¨p—ܼBûcì4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ !ª Q hˆjACT¢@Ðø½ê`6›Ëµ:½Þh³ÙœRÀS0 £Px¨d2™+ê;!ªÍfóùâµ:8::œe±™·ŽãtºÊóÅ%ÑQjW¤µ¢º\«S«ƒTŽ—ð8,Ëò!X®Õ…«C_ßñz½Q¥òu¼€çR©|õz£+*;!ªm6ö{À-ŽeY°…ˆ4D5€ !ª Q hˆjACT¢@ÐՂ期ë¸u ½^oµZ]·ˆ°°°ôôt‹Èåò¨¨¨ÈÈȧ¶ŽQAC Yk\JSB»eÏf¬!Qµ ¸ýù€Þ/48µ•¬ p%lU´Œ«?UÅd276µuŒÂå9í$µºsÚƒ66µ•¬ p%D5@Ëxħ*Ïd256©uŒÂ#ršW«;ר¤V².À•Õ‚†¨4œVày$‰¿¿¿———Ñh,//÷ =¨Däëë«P(t:Ñø÷Ïúøø(•ÊÊÊJ½^ïÆ¾5“Oû±¬ÜϪ/«ÎÛRo’2q„È;ˆ3VTåüì–¾µ˲¡¡¡‹¥¬¬ÌÝ}F!ª›²jÕšÒÒËO?ý¤R©pw_þej6›OŸ>­ÓéÜØ¥ ‹ˆˆ8sæLݨމ‰ÉËËóˆ¨ü®4¨½ÍZ“·(Üj,··‹¼ƒ"ïKeX‰Y“å)Q-‹Û·oo2™ÕB&”àùg‹ÜÝ…|÷Ý~ø±ÁàŸ L999{öì9qâDmm­S FFFFEE]½zõèÑ£‡*,,”Éd;v‹]øµÛé£p›íb~ÖùÜΪLj¤¾¨Ûâ×ùA†•8«~ƒZɺ€ÄVuþÙ¢¥KW Öoذþ-z¡Õjý⋯ºuKÞ´iKE…®oß>ÜYPP˜–¶ÍjåÆŽnŸ?77o÷î=eeW èß«WO‘Hd¯sðà¡={övèÐ~àÀ>>>ååÚuëÖó_3W®\}ûí½úõëKD‹å?íÝ»×ÇÇwÈÁ‰‰ DT\|á—_~MNîRUUµÿçž{6 Àßiïx¬cÇŽ$%%\ºt)%%ÅÁ‚ ÃÄÅÅY­ÖŒŒ ‹ÅBDz½^¥Rùûû^¾|Ù½®Ïé£p‹Ìý[íÚ`6Tw¾ãžèÄdÇ Ú,FÎbV%O©8ô‰½Ñ/yŠÕXÎJ}¯ß Ö±.à"ªãâc†ë·-mµ(­­Vëœ9sU*•Åb1 Ç-[¶üÑGùñÇõüÓ Þûì³¥÷Ü3Œˆ¾ÿ~í«¯¾a³Ù”JåÒ¥ËÂÃÕ›7o¾t©d̘{KJJÃÂÂ._¾,‰,˜Û¹s§÷ßÿÐh4Ñòå_TWW÷ë××jµ>úè¿ÿ¾K,Y,ÖÅ‹?øøãRRî.((˜3gnddÄ… ‰hÒ¤‡Õ@DgΜ¹óÎ;#""bccW¬XQUUåããЇ¸B¡`Y¶¬¬ŒÏi^^^ž¯¯¯Á`p¸¿ sú(ÜÂKé;îé·ïrÞNiV\™õ©²ÎfM&ÉB“äa]µ‡>èõœÓ–òO­c]8‹^oÔh®(Þ …·BáuÃíA(;À‡ ëϧuZÚî–¾V¯×/YòQ^Þé™3ß ¢¯¿^1}úË99Y‹-°Ùl›7ÿBDV«uþü÷¼½½Øsòä‘É“']ºT²zõwD´rå·%%¥³g¿yøðþ_ÝdµZ.\Ô¾}»3g²:uêHD{÷¦Ïš5ƒˆ,Xôûï»&Ož”‘qlÆu,ËΜùVMÍŸWv––^~ãW×®]£V‡9ñÅqœ^¯ ""‰Dâëë[UUå`MþsÙl6×mÔëõ%%%Žo+FÁ ¿­~®Ÿt‡° 2¬äêÉo‰H•<…oáTf®!rôîf rݺðDz½±°°X¯7j4åüƒk÷B‰jr ­Õjõ A¤RéäÉ“† œ2e²\.¿÷Þ±‰dß¾}D$‰Ž?têÔ ??ßóç‹ù >w®ˆˆüüüˆèÈ‘c……ç:v¼íäÉ#¿þº¹ÁýöÛ©T:mÚóÞÞÞÉÉ]XZZš—w–Ÿ:jÔˆ§žz¢OŸÞ^^ö} \ÿ'•Jù§r¹Üñƒ‹üiŽã¬Ó|®O©TÖáíí픲7“¹ì”Y“é—4‰aÅ +öëü©äXMy®‹çºuá‰ôúìFÒh®ÜX»§Äpg‘J¥ ðìŸG E"‘T*1›ÿÜê=|øè¬YoeddÚçç?ò&MzðàÁC›6mÞ´ióm·u5jäOüûÚâz½áìÙ|ŽãºvíY·ýâÅ‹2™Œˆ$מNž…eY"â8Ž?%‚ã8¾Åü½¢\zY=®/77·¤¤Äþ´mÛ¶111N©|Ó°oÝñ¯B‡½¯LH!"‘"¤l×[¬ÄUß9\·.úì³Ï?úè…Âû‰'ÿàƒrrr‰è¥—^°Ù¸Õ«¿KOß4~ü¸ÿû¿gùW?nïÞ}©©ˆhРC‡ùôÓçÌ™»bÅ·"‘¨C‡öË—*—Ë]×mðh*•j„ 555öSWUU•™™β¬Õj-..>wê_Ë£p—þcwzMýÙ­–ªK6®V_¸ÃéÅëqݺ‰Du[òóó=놵­c4˜†ˆ"Ã0¹y… ñmè¯ÃÍ‘›WØ©Sâõç†òr­Jå×à·Ñ²²²   k®Ñh뾤¢¢B*•yÜÑpŠÒÒRW/",,,==½éyX–‹Åök3pàÀÛ2Šfjltù˜Sê7%´[ölç\|Õafé­d]eeå&&Ä:¥Tn^¡½” ¶ªo¦ÀÀF$7|ÙeHHH½–[ùÇq×ÍihnÝsý<¢@ÐÕ‚†¨hºZ©‰ËZÇ(Hä1'¥KTm›ÔJÖ¸¢ e …G|¶Êåò¨¨¨Æ¦¶ŽQwˆG¤µDÕ&àöç›ÚJÖ¸Ò-w±€‹¸èb-lU¢@ÐÕ‚†¨4D5€ !ª Q hˆjAsBT3 Ãqœãu<ÇqÍ¿{X‹8!ª /®Òñ:žK§«T(¼\QÙ Q *))Ójuض€[ÇqZ­®¤¤,0@åŠúbÇKÈd²è(u¹VWRRf³Ù/àA†Q(¼¢£Ô2™ÌõÕD$“ÉÂÕ¡N)uá pACT¢@ÐÕ‚†¨4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ !ªÍ9¿Wm6›Ëµ:½Þh³ÙœRÀS0 £Px¨d2™+ê;!ªÍfóùâµ:8::œe±™·ŽãtºÊóÅ%ÑQjW¤µ¢º\«S«ƒTŽ—ð8,Ëò!X®Õ…«C_ßñz½Q¥òu¼€çR©|õz£+*;!ªm6ö{À-ŽeY°…ˆ4D5€ !ª Q hˆjACT¢@ÐՂ期ë¸u ½^oµZ]·ˆ°°°ôôt‹Èåò¨¨¨ÈÈȧ¶ŽQAC Yk\JSB»eÏf¬!Qµ ¸ýù€Þ/48µ•¬ p%lU´Œ«?UÅd276µuŒÂå9í$µºsÚƒ66µ•¬ p%D5@Ëxħ*Ïd256©uŒÂ#ršW«;ר¤V².À•Õ‚†¨´Ö|Z™N§;u*»oß>×óÈ‘£ÑÑQ!!!ö›Í¶k×îS§²U*¿>}z·mëÊž´ŒD"ñ÷÷÷òò2ååå´•ˆ|}} …N§3ÿþ¹@¥RYYY©×ëÝØ·fòi?–•ûYõeÕy[êMR&ŽyqÆŠªœŸÝÒ·–bY644Ôb±”••¹»/ШVÕ‹eçÎôuëRûmgÏž=šˆêâââõëZ¿þ§sçŠÖ­ûÞÕµµµO<ñôŽž0)•Jßá¨Q#oFï®'>>>**ÊþÔl6Ÿ>}Z§Ó¹±K-qæÌ™ºQ“——çQ2ø]iP{›µ&oQ¸ÕXnoyEޗʰ³&ËS¢Z,·oßÞd2!ª…L(Q¶(.>Æ)¥úõtá‰DR[[ÛÄl?þ¸î¥—¦Ûl6‰DRoÒ† wìHï×ï_³g¿yâÄÉÙ³ç̘1kÈ»¼¼¼œÒC¸uäääh4ŸŽ;^û—v"##£¢¢®^½zöìY«ÕÛ±cǃZ,Çë7Èé£p›íbÁ)«Õ˜ì”zŒHêÛùŠCŸØ[ü:?Ȱ®}Zɺ€ıêü³EK—®JKÛÝÒZ­ÖÏ>ûü§Ÿ~>uêô»ïÎ?~üÝqGïeË–lÞ¼ˆ˜Æ¯‡T(”Ó¦ý'=}ûðá÷SgÖŸÞDDÏ=75!!~„{Ç£ÕVìÝ»¯å#ƒ[Ú±cÇ233CCCKKK·oßîxA†aâââ¬VkFF¿¯øÜ¹sR©400Ðñú rú(Ü"sÿÖó¦nùfÞÅüSN)h³­&*yJÝF¿ä)Vc¹Íe祷Žu7@[Õqñ1C‡õÛ–¶‡ˆ† ëßüZ­Ö9sæúûû×Ô˜õzCLLt׮ɋ- ¢ÂÂsM¿6%åî””»œtñâ%†a’’’ø§±±mˆèÂ…‹Íï9sæÎ;ˆˆ]±bEUU•# ˲eeeu7 óòò|}} ƒÃým˜ÓGá^JßqO¿}|—óvJ³âʬoü{L•…t6k2‰Hš$ëª=ôq@¯çœ¶”jëÂYôz£FsE¡ðV(¼ ¯n÷‚ˆjú+¡o ­‰¨¢¢â¡‡><%!!Î))//—ÉdJ¥‚êííMDW®”7ù"€à8N¯×‘D"ñõõuüƒ•¹Ùl®Û¨×ë]w|×£à…‡‡ûûûÛŸ*•JÇk6!>éçdXÉÕ“ßú÷˜ªJžryÛKDÄoaWf® èõÎ]ÏuëÂéõÆÂÂbþQyllŸ¾-m÷B‰jr ­ÃÃÕóæ½ãÄžØl¶ºûÙ&v£4¢¦¦†ˆ¤R)ÿT.—7}òDsˆÅb"â8ÎÁ:ÍçŠQð”J%ÿ%˜'‰œRöf2—2k2ý’&i~›ND~2•«)ÏuÑâ\·.<‘^ÿÝHÍ•ØØ¨h÷Šê&;ùÜ ‰DRww"ÿÉ(“Éœ»hÝX–%"Žãøâ8Žoq¯(>°oWŒ‚—››[RRbÚ¶mÛ˜çœXzÓ°oÝñ¯B‡½¯LH!"‘"¤l×[¬Äûº/¼ÁŹl]x¢zÑ«PxßX»§КNKÛ½-mÏÐaýZºÜéÂÃÕ555ö´®¨¨ "µ:Ô­#•JÅb1ÿÇCD:Îñ+ªªªè¯#2vaaaIIIuï àD®E«ÁˆdW3VÙ¸Z¿ä)~ÉSlSeæFäªïôXu…„Õ}jÞ–¶{ ¡DµpršˆÚ·oGDû÷ÿÁ?=tèµoßÞ}¤V« l6[qq±H$ª{höƘL&“ɤR©|}}ù†abcc]w¥–ÓGÑšX Wªs6)G(GTÙ`5U¸tqXv …WllTHH ÿÀ~๥ížB;ÀóÏÝ„œ6))£Û´‰ùúëÏ›žó±Ç]»vÝÓO?ûÈ#gggïÙ³¯{÷n;wr]ß UêÝ»÷öíÛóòò¬Vkÿþýr86++«k×®]ºt)--µÙlr¹\«ÕjµZÇ‹7È£hMtÇ¿òé0Žàêe¹n]H$’ÄÄĺ-yyy6›ÍYõ]A¡ðR(¼ˆê_¦ØÒv ˆ¨Ž‹yæ™IκJc¬V«N§»rEqÝ9;th¿|ù§¯¿þæòå_ˆD¢ýëŽ>zߥ}ƒVI¥RM˜0¡¦¦Æ~*㪪ª233£££ÃÃÃY–µZ­ÅÅÅçÎsVýk¹bîÒÌãN¯©?»ÕRuÉÆÕê w8½x=®["‘(""¢nK~~¾gݰ¶ucŒ3ÃCdc&7¯0!¾ µä´çܼÂN¯?ŸX­V–e›?´²²+>>J¹\îÒ^g)--uõ"ÂÂÂÒÓÓ›ž‡eY±XÌŸÜ„6Ø.Q4Sc£ ËÇœR¿)¡Ý²g;ç3ÞHm%눈(++71Á9¿‘›Wh/%ˆ­ê›¦¥û‹‚ƒƒ®?€;pwÝœ€ÖA(§•@ƒÕ‚†¨4D5@ËxÐÕJMœÙ:FA"9)]¢jÓØ¤V².À•Õ-£P(<â³U.—GE5z—ãÖ1 òñˆ´–¨ÚÜþ|cS[ɺWºµ.Öp]¬…­jACT¢@ÐÕ‚†¨4D5€ !ª Q hNˆj†a8Žs¼€çâ8®ùwk'DµBá¥ÓU:^Àsét• …—+*;!ªT%%eZ­ÛÖp â8N«Õ•””¨\Q_ìx ™L¥.×êJJÊl6›ã<Ã0 …Wt”Z&“¹¢¾¢šˆd2Y¸:Ô)¥ .œ hˆjACT¢@ÐÕ‚†¨4D5€ !ª Q hˆjACT¢@ÐÕ‚†¨4D5€ 5ð{ÕR™„ˆ†¹é€ú°U hˆjACT¢@ÐêG5Ã0ǹ¥+@DÇÕ=¹»~T+^:]åÍíüM§«T(¼ìOëGu`€ª¤¤L«ÕaÛà&ã8N«Õ•””¨ìŒÑ`f"bˆlüæ¶Éd*×ê “Ífs[gn= Ã(^*™LöwãµQÍ'4n 8@ÐÕ‚öÿRï« I‡‹YIEND®B`‚libmongocrypt-1.19.0/doc/releasing.md000066400000000000000000000246341521103432300175350ustar00rootroot00000000000000# Releasing libmongocrypt These steps describe releasing the libmongocrypt C library (not the language bindings). ## Version number scheme ## Version numbers of libmongocrypt must follow the format 1.[0-9].[0-9] for releases and 1.[0-9].[0-9]-(alpha|beta|rc)[0-9] for pre-releases. This ensures that Linux distribution packages built from each commit are published to the correct location. ## Steps to release ## ### Check for Vulnerabilities Snyk and Silk are used to satisfy vulnerability scanning requirements of [DRIVERS-714](https://jira.mongodb.org/browse/DRIVERS-714). Prior to releasing, ensure necessary reported vulnerabilities meet requirements described in: [MongoDB Software Security Development Lifecycle Policy](https://docs.google.com/document/d/1u0m4Kj2Ny30zU74KoEFCN4L6D_FbEYCaJ3CQdCYXTMc/edit?tab=t.0#bookmark=id.l09k96qt24jm). #### Check Snyk Go to [Snyk](https://app.snyk.io/) and select the `dev-prod` organization. If access is needed, see [Snyk Onboarding](https://docs.google.com/document/d/1A38HvDvVFOwLtJQfQwIGcy5amAIpDwHUkNInwezLwXY/edit#heading=h.9ayipd2nt7xg). Check the CLI target named `mongodb/libmongocrypt`. The CLI targets may be identified by this icon: ![CLI icon](img/cli-icon.png). There are reference targets for each tracked branch: ![Reference Targets](img/reference-targets.png) ##### Update Snyk Update the Snyk reference target tracking the to-be-released branch. For a patch release (e.g. x.y.z), check-out the `rx.y` branch and update the `rx.y` reference target. For a non-patch release (e.g. x.y.0), check out the `master` branch and update the `master` reference target. Run `cmake` to ensure generated source files are present: ```bash cmake -S. -Bcmake-build -D BUILD_TESTING=OFF cmake --build cmake-build --target mongocrypt ``` Print dependencies found by Snyk and verify libbson is found: ```bash snyk test --unmanaged --print-dep-paths ``` Copy the organization ID from [Snyk settings](https://app.snyk.io/org/dev-prod/manage/settings). Create the new Snyk reference target to track the newly created release branch: ```bash snyk auth snyk monitor \ --org=$ORGANIZATION_ID \ --target-reference="" \ --unmanaged \ --remote-repo-url=https://github.com/mongodb/libmongocrypt.git ``` Check the updated reference targets in Snyk for detected vulnerabilities. #### Check the Augmented SBOM Examine the Augmented SBOM from a recent execution of the `sbom` task in an Evergreen patch or commit build. Evergreen CLI may be used to schedule only the `sbom` task: ```bash # Ensure `-p` matches the correct Evergreen project for the current branch! evergreen patch -y -p libmongocrypt -t all -v sbom -f ``` Check the contents of the "vulnerabilities" field (if present) in the Augmented SBOM. ### Release Do the following when releasing: - If this is a feature release (e.g. `x.y.0` or `x.0.0`), follow these steps: [Creating SSDLC static analysis reports](https://docs.google.com/document/d/1rkFL8ymbkc0k8Apky9w5pTPbvKRm68wj17mPJt2_0yo/edit). - Join the [releases team](https://github.com/orgs/mongodb/teams/dbx-c-cxx-releases) on GitHub via [MANA](https://mana.corp.mongodb.com/resources/68029673d39aa9f7de6399f9). - Check out the release branch. For a release `x.y.z`, the release branch is `rx.y`. If this is a new non-patch release (`x.y.0`), create the release branch. - Update CHANGELOG.md with the version being released. - Ensure `etc/purls.txt` is up-to-date. - Update `etc/third_party_vulnerabilities.md` with any updates to new or known vulnerabilities for third party dependencies that must be reported. - If this is a new non-patch release (e.g. `x.y.0`): - Update the [libmongocrypt-release](https://spruce.mongodb.com/project/libmongocrypt-release/settings/general) Evergreen project (requires auth) to set `Branch Name` to `rx.y`. - Commit the changes on the `rx.y` branch with a message like "Release x.y.z". - Tag the commit with `git tag -a `. - Push both the branch ref and tag ref in the same command: `git push origin master 1.8.0-alpha0` or `git push origin r1.8 1.8.4` - Pushing the branch ref and the tag ref in the same command eliminates the possibility of a race condition in Evergreen (for building resources based on the presence of a release tag) - Note that in the future (e.g., if we move to a PR-based workflow for releases, or if we simply want to take better advantage of advanced Evergreen features), it is possible to use Evergreen's "Trigger Versions With Git Tags" feature by updating both `config.yml` and the project's settings in Evergreen - Ensure the version on Evergreen with the tagged commit is scheduled. This may require clicking "Force Repotracker Run" in [project settings](https://spruce.corp.mongodb.com/project/libmongocrypt-release/settings/general). The following tasks must pass to complete the release: - `upload-all` - All `upload-release` tasks. - All `publish-packages` tasks. - If the `publish-packages` tasks fail with an error like `[curator] 2024/01/02 13:56:17 [p=emergency]: problem submitting repobuilder job: 404 (Not Found)`, this suggests the published path does not yet exist. Barque (the Linux package publishing service) has protection to avoid unintentional publishes. File a DEVPROD ticket ([example](https://jira.mongodb.org/browse/DEVPROD-15320)) and assign to the team called Release Infrastructure to request the path be created. Then re-run the failing `publish-packages` task. Ask in the slack channel `#ask-devprod-release-tools` for further help with `Barque` or `curator`. - Create the release from the GitHub releases page from the new tag. - Attach the tarball and signature files from the `upload-release` tasks. Use `etc/download-artifacts.py` to download all such files. Obtain the version ID from the Evergreen URL: ```bash # Example: the Evergreen URL: https://spruce.corp.mongodb.com/version/69cfada41e1f8400073c971e has VERSION_ID=69cfada41e1f8400073c971e # Downloads to _build/artifacts uv run etc/download-artifacts.py ${VERSION_ID:?} ``` - Attach the Augmented SBOM file to the release as `cyclonedx.augmented.sbom.json`. Download the Augmented SBOM from a recent execution of the `sbom` task in an Evergreen patch or commit build. - Attach `etc/third_party_vulnerabilities.md` to the release. - Attach `etc/ssdlc_compliance_report.md` to the release. - Check out the release branch (`rx.y`). Generate a new unique SBOM serial number for the next upcoming patch release (e.g. for `1.13.1` following the release of `1.13.0`): ```bash ./.evergreen/earthly.sh +sbom-generate-new-serial-number ``` Commit resulting `etc/cyclonedx.sbom.json` and push to `rx.y`. - Remove yourself from the [releases team](https://github.com/orgs/mongodb/teams/dbx-c-cxx-releases) on GitHub via [MANA](https://mana.corp.mongodb.com/resources/68029673d39aa9f7de6399f9). - If this is a new non-patch release (e.g. `x.y.0`): - File a DOCSP ticket to update the installation instructions on [Install libmongocrypt](https://www.mongodb.com/docs/manual/core/csfle/reference/libmongocrypt/). ([Example](https://jira.mongodb.org/browse/DOCSP-52575)) - Create a new Snyk reference target. The following instructions use the example branch `rx.y`: Run `cmake` to ensure generated source files are present: ```bash cmake -S. -Bcmake-build -D BUILD_TESTING=OFF cmake --build cmake-build --target mongocrypt ``` Print dependencies found by Snyk and verify libbson is found: ```bash snyk test --unmanaged --print-dep-paths ``` Copy the organization ID from [Snyk settings](https://app.snyk.io/org/dev-prod/manage/settings). Create the new Snyk reference target to track the newly created release branch: ```bash snyk auth snyk monitor \ --org=$ORGANIZATION_ID \ --target-reference=rx.y \ --unmanaged \ --remote-repo-url=https://github.com/mongodb/libmongocrypt.git ``` Snyk reference targets for older release branches may be removed if no further releases are expected on the branch. - Update the [Github Webhook](https://wiki.corp.mongodb.com/display/INTX/Githook) to include the new branch. - Navigate to the [Webhook Settings](https://github.com/mongodb/libmongocrypt/settings/hooks). - Click `Edit` on the hook for `https://githook.mongodb.com/`. - Add the new release branch to the `Payload URL`. Remove unmaintained release branches. - Make a PR to to the `master` branch: - Apply changes from the "Release x.y.z" commit. - If this was a non-patch release (e.g. `x.y.0`), generate a new unique SBOM serial number for the next upcoming non-patch release (e.g. for `1.14.0` following the release of `1.13.0`): ```bash ./.evergreen/earthly.sh +sbom-generate-new-serial-number ``` Commit resulting `etc/cyclonedx.sbom.json`. - Update the release on the [Jira releases page](https://jira.mongodb.org/projects/MONGOCRYPT/versions). - Record the release on [C/C++ Release Info](https://docs.google.com/spreadsheets/d/1yHfGmDnbA5-Qt8FX4tKWC5xk9AhzYZx1SKF4AD36ecY/edit?usp=sharing). This is done to leave commentary about the process. - Add a link to the Evergreen waterfall for the tagged commit to [libmongocrypt Security Testing Summary](https://docs.google.com/document/d/1dc7uvBzu3okAIsA8LSW5sVQGkYIvwpBVdg5v4wb4c4s/edit#heading=h.5t79jwe4p0ss). ## Homebrew steps ## Submit a PR to update the Homebrew package https://github.com/mongodb/homebrew-brew/blob/master/Formula/libmongocrypt.rb. ([Example](https://github.com/mongodb/homebrew-brew/pull/249)). If not on macOS, request a team member to do this step. Request review by posting in #ask-devprod-build. ## Debian steps ## If you are not a Debian maintainer on the team, request a team member to do the steps in this section. Refer to the [Debian](https://github.com/mongodb/mongo-c-driver/blob/master/docs/dev/debian.rst) steps. For a non-patch release (e.g. x.y.0), submit a merge request to the [extrepo-data](https://salsa.debian.org/extrepo-team/extrepo-data) project in Debian to update the PPA. The change would look something like this: ``` diff --git a/repos/debian/libmongocrypt.yaml b/repos/debian/libmongocrypt.yaml index 609dc0b..f7530a9 100644 --- a/repos/debian/libmongocrypt.yaml +++ b/repos/debian/libmongocrypt.yaml @@ -4,7 +4,7 @@ libmongocrypt: source: Types: deb URIs: https://libmongocrypt.s3.amazonaws.com/apt/debian - Suites: /libmongocrypt/1.12 + Suites: /libmongocrypt/1.13 Components: main Architectures: amd64 arm64 suites: ``` libmongocrypt-1.19.0/etc/000077500000000000000000000000001521103432300152375ustar00rootroot00000000000000libmongocrypt-1.19.0/etc/calc_release_version.py000066400000000000000000000307161521103432300217670ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright 2018-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. # """ A script that calculates the release version number (based on the current Git branch and/or recent tags in history) to assign to a tarball generated from the current Git commit. """ # XXX NOTE XXX NOTE XXX NOTE XXX # After modifying this script it is advisable to execute the self-test. # # This is done by starting in the directory containing this script and then # executing a separate self-test script, like this: # # $ bash ./calc_release_version_selftest.sh # # The self-test script will emit diagnostic output. If tracing of the execution # of each command is desired, then add the -x option to the bash invocation. # XXX NOTE XXX NOTE XXX NOTE XXX import datetime import re import subprocess import sys class Version: def __init__(self, s): pat = r'(\d+)\.(\d+)\.(\d+)(\-\S+)?' match = re.match(pat, s) assert match, "Unrecognized version string %s" % s self.major, self.minor, self.micro = ( map(int, (match.group(1), match.group(2), match.group(3)))) if match.group(4): self.prerelease = match.group(4)[1:] else: self.prerelease = '' def __lt__(self, other): if self.major != other.major: return self.major < other.major if self.minor != other.minor: return self.minor < other.minor if self.micro != other.micro: return self.micro < other.micro if self.prerelease != other.prerelease: if self.prerelease != '' and other.prerelease == '': # Consider a prerelease less than non-prerelease. return True # For simplicity, compare prerelease versions lexicographically. return self.prerelease < other.prerelease # Versions are equal. return False def __eq__(self, other): self_tuple = self.major, self.minor, self.micro, self.prerelease other_tuple = other.major, other.minor, other.micro, other.prerelease return self_tuple == other_tuple def parse_version(ver): return Version(ver) DEBUG = len(sys.argv) > 1 and '-d' in sys.argv if DEBUG: print('Debugging output enabled.') # This options indicates to output the next minor release version NEXT_MINOR = len(sys.argv) > 1 and '--next-minor' in sys.argv RELEASE_TAG_RE = re.compile('(?P(?P[0-9]+)\\.(?P[0-9]+)' '\\.(?P[0-9]+)(?:-(?P.*))?)') RELEASE_BRANCH_RE = re.compile('(?:(?:refs/remotes/)?origin/)?(?Pr' '(?P[0-9]+)\\.(?P[0-9]+))') def check_output(args): """ Delegates to subprocess.check_output() if it is available, otherwise provides a reasonable facsimile. """ if 'check_output' in dir(subprocess): out = subprocess.check_output(args) else: proc = subprocess.Popen(args, stdout=subprocess.PIPE) out, err = proc.communicate() ret = proc.poll() if ret: raise subprocess.CalledProcessError(ret, args[0], output=out) if type(out) is bytes: # git isn't guaranteed to always return UTF-8, but for our purposes # this should be fine as tags and hashes should be ASCII only. out = out.decode('utf-8') return out def check_head_tag(): """ Checks the current HEAD to see if it has been tagged with a tag that matches the pattern for a release tag. Returns release version calculated from the tag, or None if there is no matching tag associated with HEAD. """ found_tag = False version_str = '0.0.0' version_parsed = parse_version(version_str) # have git tell us if any tags that look like release tags point at HEAD; # based on our policy, a commit should never have more than one release tag tags = check_output(['git', 'tag', '--points-at', 'HEAD', '--list', '1.*']).split() tag = '' if len(tags) == 1: tag = tags[0] elif len(tags) > 1: raise Exception('Expected 1 or 0 tags on HEAD, got: {}'.format(tags)) release_tag_match = RELEASE_TAG_RE.match(tag) if release_tag_match: new_version_str = release_tag_match.group('ver') new_version_parsed = parse_version(new_version_str) if new_version_parsed > version_parsed: if DEBUG: print('HEAD release tag: ' + new_version_str) version_str = new_version_str version_parsed = new_version_parsed found_tag = True if found_tag: if DEBUG: print('Calculated version: ' + version_str) return version_str return None def get_next_minor(prerelease_marker): """ get_next_minor does the following: - Inspect the branches that fit the convention for a release branch. - Choose the latest and increment the minor version. - Append .0 to form the new version (e.g., r1.21 becomes 1.22.0) - Append a pre-release marker. (e.g. 1.22.0 becomes 1.22.0-20220201+gitf6e6a7025d) """ version_str = '0.0.0' version_parsed = parse_version(version_str) version_new = {} # Use refs (not branches) to get local branches plus remote branches refs = check_output(['git', 'show-ref']).splitlines() for ref in refs: release_branch_match = RELEASE_BRANCH_RE.match(ref.split()[1]) if release_branch_match: # Construct a candidate version from this branch name version_new['major'] = int(release_branch_match.group('vermaj')) version_new['minor'] = int(release_branch_match.group('vermin')) + 1 version_new['patch'] = 0 version_new['prerelease'] = prerelease_marker new_version_str = str(version_new['major']) + '.' + \ str(version_new['minor']) + '.' + \ str(version_new['patch']) + '-' + \ version_new['prerelease'] new_version_parsed = parse_version(new_version_str) if new_version_parsed > version_parsed: version_str = new_version_str version_parsed = new_version_parsed if DEBUG: print('Found new best version "' + version_str \ + '" based on branch "' \ + release_branch_match.group('brname') + '"') return version_str def get_branch_tags(active_branch_name): """ Returns the tag or tags (as a single string with newlines between tags) corresponding to the current branch, which must not be master. If the specified branch is a release branch then return all tags based on the major/minor X.Y release version. If the specified branch is neither master nor a release branch, then walk backwards in history until the first tag matching the glob '1.*' and return that tag. """ if active_branch_name == 'master': raise Exception('this method is not meant to be called while on "master"') tags = '' release_branch_match = RELEASE_BRANCH_RE.match(active_branch_name) if release_branch_match: # This is a release branch, so look for tags only on this branch tag_glob = release_branch_match.group('vermaj') + '.' \ + release_branch_match.group('vermin') + '.*' tags = check_output(['git', 'tag', '--list', tag_glob]) else: # Not a release branch, so look for the most recent tag in history commits = check_output(['git', 'log', '--pretty=format:%H', '--no-merges']) if len(commits) > 0: for commit in commits.splitlines(): tags = check_output(['git', 'tag', '--points-at', commit, '--list', '1.*']) if len(tags) > 0: # found a tag, we should be done break return tags def process_and_sort_tags(tags): """ Given a string (as returned from get_branch_tags), return a sorted list of zero or more tags (sorted based on the Version comparison) which meet the following criteria: - a final release tag (i.e., 1.x.y without any pre-release suffix) - a pre-release tag which is not superseded by a release tag (i.e., 1.x.y-preX iff 1.x.y does not already exist) """ processed_and_sorted_tags = [] if not tags or len(tags) == 0: return processed_and_sorted_tags raw_tags = tags.splitlines() # find all the final release tags for tag in raw_tags: release_tag_match = RELEASE_TAG_RE.match(tag) if release_tag_match and not release_tag_match.group('verpre'): processed_and_sorted_tags.append(tag) # collect together final release tags and pre-release tags for # versions that have not yet had a final release for tag in raw_tags: tag_parts = tag.split('-') if len(tag_parts) >= 2 and tag_parts[0] not in processed_and_sorted_tags: processed_and_sorted_tags.append(tag) processed_and_sorted_tags.sort(key=Version) return processed_and_sorted_tags def main(): """ The algorithm is roughly: - Is the --next-minor flag passed? If "yes", then return the next minor release with a pre-release marker. - Is the current HEAD associated with a tag that looks like a release version? - If "yes" then use that as the version - If "no" then is the current branch master? - If "yes" the current branch is master, then return the next minor release with a pre-release marker. - If "no" the current branch is not master, then determine the most recent tag in history; strip any pre-release marker, increment the patch version, and append a new pre-release marker """ version_str = '0.0.0' version_parsed = parse_version(version_str) head_commit_short = check_output(['git', 'rev-parse', '--revs-only', '--short=10', 'HEAD^{commit}']).strip() prerelease_marker = datetime.date.today().strftime('%Y%m%d') \ + '+git' + head_commit_short if NEXT_MINOR: if DEBUG: print('Calculating next minor release') return get_next_minor(prerelease_marker) head_tag_ver = check_head_tag() if head_tag_ver: return head_tag_ver active_branch_name = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip() if DEBUG: print('Calculating release version for branch: ' + active_branch_name) if active_branch_name == 'master': return get_next_minor(prerelease_marker) branch_tags = get_branch_tags(active_branch_name) tags = process_and_sort_tags(branch_tags) tag = tags[-1] if len(tags) > 0 else '' # at this point the RE match is redundant, but convenient for accessing # the components of the version string release_tag_match = RELEASE_TAG_RE.match(tag) if release_tag_match: version_new = {} version_new['major'] = int(release_tag_match.group('vermaj')) version_new['minor'] = int(release_tag_match.group('vermin')) version_new['patch'] = int(release_tag_match.group('verpatch')) + 1 version_new['prerelease'] = prerelease_marker new_version_str = str(version_new['major']) + '.' + \ str(version_new['minor']) + '.' + \ str(version_new['patch']) + '-' + \ version_new['prerelease'] new_version_parsed = parse_version(new_version_str) if new_version_parsed > version_parsed: version_str = new_version_str version_parsed = new_version_parsed if DEBUG: print('Found new best version "' + version_str \ + '" from tag "' + release_tag_match.group('ver') + '"') return version_str RELEASE_VER = main() if DEBUG: print('Final calculated release version:') print(RELEASE_VER) libmongocrypt-1.19.0/etc/calc_release_version_selftest.sh000077500000000000000000000041461521103432300236630ustar00rootroot00000000000000#!/bin/bash # calc_release_version_selftest.sh is used to test output of calc_release_version.py. # run with: # cd etc # ./calc_release_version_selftest.sh set -o errexit set -o pipefail function assert_eq () { a="$1" b="$2" if [[ "$a" != "$b" ]]; then echo "Assertion failed: $a != $b" # Print caller caller exit 1 fi } SAVED_REF=$(git rev-parse HEAD) function cleanup () { [[ -e calc_release_version_test.py ]] && rm calc_release_version_test.py git checkout $SAVED_REF --quiet } trap cleanup EXIT : ${PYTHON_INTERP:=python} if [[ -z $(command -v "${PYTHON_INTERP}") ]]; then echo "Python interpreter '${PYTHON_INTERP}' is not valid." echo "Set the PYTHON_INTERP environment variable to a valid interpreter." exit 1 fi # copy calc_release_version.py to a separate file not tracked by git so it does not change on `git checkout` cp calc_release_version.py calc_release_version_test.py echo "Test a tagged commit ... begin" { git checkout 1.8.1 --quiet got=$("${PYTHON_INTERP}" calc_release_version_test.py) assert_eq "$got" "1.8.1" git checkout - --quiet } echo "Test a tagged commit ... end" DATE=$(date +%Y%m%d) echo "Test an untagged commit ... begin" { # b7f8a1f1502d28a5ef440e642fddda8da8f873a1 is commit before 1.8.1 git checkout b7f8a1f1502d28a5ef440e642fddda8da8f873a1 --quiet got=$("${PYTHON_INTERP}" calc_release_version_test.py) assert_eq "$got" "1.8.1-$DATE+gitb7f8a1f150" git checkout - --quiet } echo "Test an untagged commit ... end" echo "Test next minor version ... begin" { CURRENT_SHORTREF=$(git rev-parse --revs-only --short=10 HEAD) got=$("${PYTHON_INTERP}" calc_release_version_test.py --next-minor) # XXX NOTE XXX NOTE XXX # If you find yourself looking at this line because the assertion below # failed, then it is probably because a new major/minor release was made. # Update the expected output to represent the correct next version. # XXX NOTE XXX NOTE XXX assert_eq "$got" "1.14.0-$DATE+git$CURRENT_SHORTREF" } echo "Test next minor version ... end" echo "All tests passed" libmongocrypt-1.19.0/etc/cyclonedx.sbom.json000066400000000000000000000063141521103432300210650ustar00rootroot00000000000000{ "components": [ { "bom-ref": "pkg:github/mongodb/mongo-c-driver@v2.3.0#src/libbson", "copyright": "Copyright 2009-present MongoDB, Inc.", "externalReferences": [ { "type": "distribution", "url": "https://github.com/mongodb/mongo-c-driver/archive/v2.3.0.tar.gz" }, { "type": "website", "url": "https://github.com/mongodb/mongo-c-driver/tree/v2.3.0" } ], "group": "mongodb", "licenses": [ { "license": { "id": "Apache-2.0" } } ], "name": "mongo-c-driver", "purl": "pkg:github/mongodb/mongo-c-driver@v2.3.0#src/libbson", "type": "library", "version": "v2.3.0" }, { "bom-ref": "pkg:generic/IntelRDFPMathLib@20U2?download_url=https://www.netlib.org/misc/intel/IntelRDFPMathLib20U2.tar.gz", "copyright": "Copyright (c) 2018, Intel Corp.", "externalReferences": [ { "type": "distribution", "url": "https://www.netlib.org/misc/intel/IntelRDFPMathLib20U2.tar.gz" } ], "licenses": [ { "license": { "id": "BSD-3-Clause" } } ], "name": "IntelRDFPMathLib", "purl": "pkg:generic/IntelRDFPMathLib@20U2?download_url=https://www.netlib.org/misc/intel/IntelRDFPMathLib20U2.tar.gz", "type": "library", "version": "20U2" } ], "dependencies": [ { "ref": "pkg:generic/IntelRDFPMathLib@20U2?download_url=https://www.netlib.org/misc/intel/IntelRDFPMathLib20U2.tar.gz" }, { "ref": "pkg:github/mongodb/mongo-c-driver@v2.3.0#src/libbson" } ], "metadata": { "timestamp": "2026-05-01T14:49:06.464175+00:00", "tools": [ { "externalReferences": [ { "type": "build-system", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" }, { "type": "distribution", "url": "https://pypi.org/project/cyclonedx-python-lib/" }, { "type": "documentation", "url": "https://cyclonedx-python-library.readthedocs.io/" }, { "type": "issue-tracker", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" }, { "type": "license", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" }, { "type": "release-notes", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" }, { "type": "vcs", "url": "https://github.com/CycloneDX/cyclonedx-python-lib" }, { "type": "website", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" } ], "name": "cyclonedx-python-lib", "vendor": "CycloneDX", "version": "6.4.4" } ] }, "serialNumber": "urn:uuid:5c89265b-cc3d-4649-878b-17b482325b56", "version": 1, "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.5", "vulnerabilities": [] } libmongocrypt-1.19.0/etc/debian/000077500000000000000000000000001521103432300164615ustar00rootroot00000000000000libmongocrypt-1.19.0/etc/debian/control000066400000000000000000000021411521103432300200620ustar00rootroot00000000000000Source: libmongocrypt Priority: optional Maintainer: Mongo C Driver Team Uploaders: Kevin Albertson , Roberto C. Sanchez Standards-Version: 4.5.0 Section: libs Homepage: https://github.com/mongodb/libmongocrypt Package: libmongocrypt-dev Section: libdevel Architecture: any Multi-Arch: same Depends: libmongocrypt0 (= ${binary:Version}), ${misc:Depends} Description: client-side field level encryption library - dev files libmongocrypt facilitates the client-side encryption and decryption, at the field level, of data stored in MongoDB. . This package contains the libmongocrypt and libkms_message development headers and libraries. Package: libmongocrypt0 Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: client-side field level encryption library - runtime files libmongocrypt facilitates the client-side encryption and decryption, at the field level, of data stored in MongoDB. . This package contains the libmongocrypt and libkms_message runtime libraries. libmongocrypt-1.19.0/etc/debian/rules000077500000000000000000000031601521103432300175410ustar00rootroot00000000000000#!/usr/bin/make -f # See debhelper(7) (uncomment to enable) # output every command that modifies files on the build system. export DH_VERBOSE = 1 # see FEATURE AREAS in dpkg-buildflags(1) #export DEB_BUILD_MAINT_OPTIONS = hardening=+all # see ENVIRONMENT in dpkg-buildflags(1) # package maintainers to append CFLAGS #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic # package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed %: dh $@ override_dh_auto_configure: # nothing to configure override_dh_auto_build: # nothing to build override_dh_auto_install: mkdir -p $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) mkdir -p $(CURDIR)/debian/tmp/usr/include cp -dv $(CURDIR)/lib/*.so.* $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) cp -dv $(CURDIR)/lib/*.so $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) cp -v $(CURDIR)/lib/*.a $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) cp -rv $(CURDIR)/lib/cmake $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) cp -rv $(CURDIR)/lib/pkgconfig $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) sed -i -e 's|{prefix}/lib|{prefix}/lib/$(DEB_HOST_MULTIARCH)|g' \ -e 's|^prefix=.*|prefix=/usr|' \ $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/*.pc sed -i -r -e 's/get_filename_component\(_IMPORT_PREFIX "\$$\{CMAKE_CURRENT_LIST_FILE\}" PATH\)/&\nget_filename_component\(_IMPORT_PREFIX "\$$\{_IMPORT_PREFIX\}" PATH\)/' \ -e "s+lib/lib(mongocrypt|kms_message)+lib/$(DEB_HOST_MULTIARCH)/lib\1+" \ $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/cmake/*/*.cmake cp -rv $(CURDIR)/include/* $(CURDIR)/debian/tmp/usr/include libmongocrypt-1.19.0/etc/debian/source/000077500000000000000000000000001521103432300177615ustar00rootroot00000000000000libmongocrypt-1.19.0/etc/debian/source/format000066400000000000000000000000041521103432300211660ustar00rootroot000000000000001.0 libmongocrypt-1.19.0/etc/download-artifacts.py000066400000000000000000000040551521103432300214020ustar00rootroot00000000000000# Download libmongocrypt artifacts to upload to a GitHub release. # # /// script # dependencies = [ # "requests", # ] # /// import argparse import subprocess import requests from pathlib import Path from urllib.parse import urlsplit def download_file(url: str): url_parts = urlsplit(url) path = Path(url_parts.path) filename = path.name destination_dir = Path("_build/artifacts") destination_dir.mkdir(exist_ok=True, parents=True) destination = destination_dir / filename print(f"Downloading {url} to {destination}") with requests.get(url, stream=True) as resp: with destination.open("wb") as file: for chunk in resp.iter_content(chunk_size=8192): file.write(chunk) def main(): parser = argparse.ArgumentParser(description="Download release artifacts from an Evergreen version") parser.add_argument("version_id", help="Evergreen version ID. (e.g. https://evergreen.corp.mongodb.com/version/)") args = parser.parse_args() version_id = args.version_id oauth_token = subprocess.check_output(["evergreen", "client", "get-oauth-token"], text=True).strip() api_server_host = "https://evergreen.corp.mongodb.com/rest/v2" headers = {'Authorization': f'Bearer {oauth_token}'} resp: requests.Response = requests.get( f"{api_server_host}/versions/{version_id}/builds", params={"include_task_info": "true"}, headers=headers) if resp.status_code != 200: print(f"Failed to get builds for version {version_id}: {resp.status_code} {resp.text}") return builds = resp.json() for build in builds: for task_info in build["task_cache"]: if task_info["display_name"] == "upload-release": task_id = task_info["id"] resp = requests.get( f"{api_server_host}/tasks/{task_id}", headers=headers) task = resp.json() for artifact in task["artifacts"]: download_file(artifact["url"]) if __name__ == "__main__": main() libmongocrypt-1.19.0/etc/fle2_aead_generate_tests.py000066400000000000000000000020351521103432300225070ustar00rootroot00000000000000# fle2_aead_generate_tests.py is used to generate the test file: ./test/data/roundtrip/fle2aead-generated.json import json import os import itertools import fle2_crypto # Generate test cases by taking the cross-product of AD (associated data) and plaintext (M) lengths. AD_lens = [5, 10, 20] M_lens = [1, 16, 64, 100] out = [] for (AD_len, M_len) in itertools.product(AD_lens, M_lens): M = os.urandom(M_len) AD = os.urandom(AD_len) IV = os.urandom(16) Ke = os.urandom(32) Km = os.urandom(32) C = fle2_crypto.fle2aead_encrypt(M=M, Ke=Ke, IV=IV, Km=Km, AD=AD) # Create the 96 byte data encryption key. The last 32 are unused. key = Ke + Km + (b"\x00" * 32) out.append({ 'name': "generated test. AD length={}, M length={}".format(AD_len, M_len), 'origin': "etc/fle2_aead_generate_tests.py", 'algo': 'AES-256-CTR/SHA-256', 'iv': IV.hex(), 'aad': AD.hex(), 'key': key.hex(), 'plaintext': M.hex(), 'ciphertext': C.hex() }) print(json.JSONEncoder(indent=3).encode(out)) libmongocrypt-1.19.0/etc/fle2_crypto.py000066400000000000000000000063441521103432300200500ustar00rootroot00000000000000from cryptography.hazmat.primitives import hashes, hmac from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes import struct ENCRYPTION_KEY_LENGTH = 32 MAC_KEY_LENGTH = 32 HMAC_SHA256_TAG_LENGTH = 32 IV_LENGTH = 16 DEK_LENGTH = 96 BLOCK_LENGTH = 16 def _hmacsha256 (Km, input): assert (len(Km) == MAC_KEY_LENGTH) hm = hmac.HMAC(Km, hashes.SHA256()) hm.update (input) return hm.finalize() class DEK (): """ Class representing a Data Encryption Key (DEK) """ def __init__ (self, bytesIn): assert (len(bytesIn) == DEK_LENGTH) self.Ke = bytesIn[0:32] self.Km = bytesIn[32:64] self.TokenKey = bytesIn[64:96] def _fle2_encrypt (IV, Ke, M, mode, Km = None, AD = None): """ Generalized encrypt vector create. S = AES-{mode}.Enc(Ke, IV, M) if Km is not None: T = HMAC/SHA-256(Km, AD || IV || S) C = IV || S || T """ assert len(Ke) == ENCRYPTION_KEY_LENGTH assert len(IV) == IV_LENGTH assert (mode == 'CTR') or (mode == 'CBC') modeObj = modes.CTR(IV) if mode == 'CTR' else modes.CBC(IV) # S = AES-{mode}.Enc(Ke, IV, M) cipher = Cipher(algorithms.AES(Ke), modeObj) encryptor = cipher.encryptor() if mode == 'CBC': # PKCS#7 padding_len = BLOCK_LENGTH - (len(M) % BLOCK_LENGTH) M = M + (padding_len.to_bytes(1, 'big') * padding_len) S = encryptor.update(M) + encryptor.finalize() if Km is not None: assert AD is not None assert len(Km) == MAC_KEY_LENGTH # T = HMAC-SHA256(Km, AD || IV || S) T = _hmacsha256 (Km, AD + IV + S) else: assert AD is None T = b'' # C = IV + S + T C = IV + S + T return C def fle2_encrypt (M, Ke, IV): """ AES-256-CTR/NONE """ return _fle2_encrypt (IV, Ke, M, 'CTR') def fle2aead_encrypt(M, Ke, IV, Km, AD): """ AES-256-CTR/SHA-256 """ return _fle2_encrypt (IV, Ke, M, 'CTR', Km, AD) def fle2v2_aead_encrypt(M, Ke, IV, Km, AD): """ AES-256-CBC/SHA-256 """ return _fle2_encrypt (IV, Ke, M, 'CBC', Km, AD) def _fle2_decrypt (C, Ke, mode, Km = None, AD = None): assert (len(Ke) == ENCRYPTION_KEY_LENGTH) Tlen = 0 if Km is None else HMAC_SHA256_TAG_LENGTH; assert (len(C) > (IV_LENGTH + Tlen)) # C = IV || S || T IV = C[0:IV_LENGTH] S = C[IV_LENGTH:-Tlen] if Km is not None: T = C[-Tlen:] assert T == _hmacsha256 (Km, AD + IV + S) assert (mode == 'CTR') or (mode == 'CBC') modeObj = modes.CTR(IV) if mode == 'CTR' else modes.CBC(IV) # M = AES-{mode}.Dec(Ke, IV, S) cipher = Cipher(algorithms.AES(Ke), modeObj) encryptor = cipher.decryptor() if mode == 'CBC': # PKCS#7 padding_len = ord(S[-1:]) S = S[-padding_len:] M = encryptor.update(S) + encryptor.finalize() return M def fle2_decrypt (C, Ke): """AES-256-CTR/NONE""" return _fle2_decrypt (C, Ke, 'CTR') def fle2aead_decrypt(C, Km, AD, Ke): """AES-256-CTR/SHA-256""" return _fle2_decrypt (C, Ke, 'CTR', Km, AD) def fle2v2_aead_decrypt(C, Km, AD, Ke): """AES-256-CBC/SHA-256""" return _fle2_decrypt (C, Ke, 'CBC', Km, AD) def ServerDataEncryptionLevel1Token (rootKey): return _hmacsha256 (rootKey, struct.pack("/dev/null 1>&2; then if test -f /usr/bin/dnf; then if echo "$@" | grep -q '[.]spec'; then # We use the build-dep command to let dnf do the heavy lifting from the spec file dnf build-dep -y --allowerasing "$@" else # 'dnf' will "do the right thing" # --allow-erasing: see https://github.com/jeroen/curl/issues/350. dnf install -y --allowerasing "$@" fi elif test -f /usr/bin/yum; then yum install -y -- "$@" # 'yum' happily ignores missing packages. Use 'rpm -q' to check that # everything we requested actually got installed. if ! rpm -q -- "$@"; then echo "__install: Failing because one or more packages requested are not available" exit 1 fi else echo "No package manager here?" 1>&2 exit 1 fi elif test -f /etc/SuSE-brand \ || (test -f /etc/os-release && grep "opensuse" /etc/os-release); then zypper --non-interactive install "$@" elif test -f /etc/arch-release; then pacman -Sy --noconfirm -- "$@" elif test -f /etc/alpine-release; then apk add -- "$@" else echo "We don't know how to manage packages on this system" 1>&2 exit 1 fi libmongocrypt-1.19.0/etc/libbson-remove-GCC-diagnostic-pragma.patch000066400000000000000000000103451521103432300251670ustar00rootroot00000000000000diff --git a/src/common/src/common-bson-dsl-private.h b/src/common/src/common-bson-dsl-private.h index 748ba54046..67a6a4f194 100644 --- a/src/common/src/common-bson-dsl-private.h +++ b/src/common/src/common-bson-dsl-private.h @@ -31,6 +31,13 @@ enum { BSON_IF_WINDOWS(__declspec(selectany)) \ BSON_IF_POSIX(__attribute__((weak))) +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +// Do not define `GCC diagnostic` pragma for GCC < 4.6. +#define _bsonDSL_disableWarnings() ((void)0) +#define _bsonDSL_restoreWarnings() ((void)0) +#else +// Not using GCC < 4.6 #ifdef __GNUC__ // GCC has a bug handling pragma statements that disable warnings within complex // nested macro expansions. If we're GCC, just disable -Wshadow outright: @@ -50,7 +57,7 @@ BSON_IF_GNU_LIKE(_Pragma("GCC diagnostic ignored \"-Wshadow\"")) mlib_diagnostic_pop(); \ } else \ ((void)0) - +#endif /** * @brief Parse the given BSON document. * diff --git a/src/common/src/common-macros-private.h b/src/common/src/common-macros-private.h index 068b09470f..b5c94c5c9d 100644 --- a/src/common/src/common-macros-private.h +++ b/src/common/src/common-macros-private.h @@ -97,7 +97,13 @@ #endif // Disable the -Wcast-qual warning -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +// Do not define `GCC diagnostic` pragma for GCC < 4.6. +#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN +#define MC_DISABLE_CAST_QUAL_WARNING_END +#elif defined(__GNUC__) +// Not using GCC < 4.6 #define MC_DISABLE_CAST_QUAL_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") #define MC_DISABLE_CAST_QUAL_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP #elif defined(__clang__) diff --git a/src/common/src/mlib/config.h b/src/common/src/mlib/config.h index a95f917efb..d1494797cd 100644 --- a/src/common/src/mlib/config.h +++ b/src/common/src/mlib/config.h @@ -204,7 +204,13 @@ * @brief Expands to `noexcept` when compiled as C++, otherwise expands to * nothing */ +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +// Don't define this expansion for the older gcc/g++ +#define mlib_noexcept +#else #define mlib_noexcept MLIB_IF_CXX(noexcept) +#endif #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) #define mlib_is_little_endian() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) @@ -282,15 +288,29 @@ #define MLIB_FUNC MLIB_IF_GNU_LIKE(__func__) MLIB_IF_MSVC(__FUNCTION__) +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +// Do not define `GCC diagnostic` pragma for GCC < 4.6. +#define mlib_diagnostic_push() +#else +// Not using GCC < 4.6 #define mlib_diagnostic_push() \ MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic push);) \ MLIB_IF_MSVC(mlib_pragma(warning(push));) \ mlib_static_assert(1, "") +#endif +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +// Do not define `GCC diagnostic` pragma for GCC < 4.6. +#define mlib_diagnostic_pop() +#else +// Not using GCC < 4.6 #define mlib_diagnostic_pop() \ MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic pop);) \ MLIB_IF_MSVC(mlib_pragma(warning(pop));) \ mlib_static_assert(1, "") +#endif #define mlib_gcc_warning_disable(Warning) \ MLIB_IF_GCC(mlib_pragma(GCC diagnostic ignored Warning);) \ @@ -344,9 +364,15 @@ /** * @brief Emit a _Pragma that will disable warnings about the use of deprecated entities. */ +#if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) +// Using GCC < 4.6 +#define mlib_disable_deprecation_warnings() +#else +// Not using GCC < 4.6 #define mlib_disable_deprecation_warnings() \ mlib_gnu_warning_disable("-Wdeprecated-declarations"); \ mlib_msvc_warning(disable : 4996) +#endif /** * @brief Function-like macro that expands to `1` if we are certain that we are libmongocrypt-1.19.0/etc/list-compile-files.py000066400000000000000000000010171521103432300213110ustar00rootroot00000000000000description = """Prints the list of files in a compilation database. Run with python ./list-compile-files.py """ from fnmatch import fnmatch import sys import json import os if len(sys.argv) != 2: print(description) sys.exit(1) compile_db = json.load(open(sys.argv[1] + "compile_commands.json", "r")) for entry in compile_db: fpath = entry['file'] fname = os.path.basename(fpath) if fnmatch(fname, 'mc-*') or fnmatch(fname, 'mongocrypt-*'): print(fpath) print("") libmongocrypt-1.19.0/etc/mongo-common-test-harness.patch000066400000000000000000000011471521103432300233060ustar00rootroot00000000000000diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e95cd8d8a4..3f3db6ce2a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -15,7 +15,7 @@ add_library(mongo::mlib ALIAS mongo-mlib) target_include_directories(mongo-mlib INTERFACE $) set_property(TARGET mongo-mlib PROPERTY EXPORT_NAME mongo::mlib) -if(CMAKE_CXX_COMPILER) +if(CMAKE_CXX_COMPILER AND BUILD_TESTING) add_executable(mlib-ckdint-test src/mlib/ckdint.test.cpp) set_target_properties(mlib-ckdint-test PROPERTIES COMPILE_FEATURES cxx_std_11 libmongocrypt-1.19.0/etc/mongo-inteldfp-MONGOCRYPT-571.patch000066400000000000000000000020021521103432300232050ustar00rootroot00000000000000diff --git a/src/third-party/IntelRDFPMathLib20U2/LIBRARY/float128/dpml_exception.c b/src/third-party/IntelRDFPMathLib20U2/LIBRARY/float128/dpml_exception.c index d061a4c..916b41d 100755 --- a/src/third-party/IntelRDFPMathLib20U2/LIBRARY/float128/dpml_exception.c +++ b/src/third-party/IntelRDFPMathLib20U2/LIBRARY/float128/dpml_exception.c @@ -132,6 +132,7 @@ !defined(wnt) # include +# include # define DPML_SIGNAL(p) raise(SIGFPE) #else diff --git a/src/third-party/IntelRDFPMathLib20U2/LIBRARY/src/bid_internal.h b/src/third-party/IntelRDFPMathLib20U2/LIBRARY/src/bid_internal.h index cd08ea7..1892637 100755 --- a/src/third-party/IntelRDFPMathLib20U2/LIBRARY/src/bid_internal.h +++ b/src/third-party/IntelRDFPMathLib20U2/LIBRARY/src/bid_internal.h @@ -30,6 +30,8 @@ #ifndef __BIDECIMAL_H #define __BIDECIMAL_H +#include + #define _CRT_SECURE_NO_DEPRECATE #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning( disable: 4996 ) libmongocrypt-1.19.0/etc/mongo-inteldfp-alpine-arm-fix.patch000066400000000000000000000013231521103432300240100ustar00rootroot00000000000000diff --git a/cmake-build/_deps/intel_dfp-src/LIBRARY/src/bid_functions.h b/cmake-build/_deps/intel_dfp-src/LIBRARY/src/bid_functions.h index 7042eed..e55f59e 100755 --- a/cmake-build/_deps/intel_dfp-src/LIBRARY/src/bid_functions.h +++ b/cmake-build/_deps/intel_dfp-src/LIBRARY/src/bid_functions.h @@ -41,12 +41,6 @@ #endif #include -// Fix system header issue on Sun solaris and define required type by ourselves -#if !defined(_WCHAR_T) && !defined(_WCHAR_T_DEFINED) && !defined(__QNX__) && !defined(__cplusplus) -typedef int wchar_t; -#endif - - #ifdef IN_LIBGCC2 // When we are built as the part of the gcc runtime library, libgcc, // we will use gcc types defined in bid_gcc_intrinsics.h. libmongocrypt-1.19.0/etc/mongo-inteldfp-libmongocrypt-pr-625.patch000066400000000000000000000010211521103432300250130ustar00rootroot00000000000000diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h index ef8a5b7..98d86c1 100644 --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h @@ -39,7 +39,7 @@ # define merced #endif -#if defined(ct) || defined(efi2) +#if defined(ct) || defined(efi2) || defined(__x86_64__) # undef _M_AMD64 # define _M_AMD64 #endif libmongocrypt-1.19.0/etc/mongo-inteldfp-s390x.patch000066400000000000000000000113141521103432300220660ustar00rootroot00000000000000diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h index 3eb9fbb17a80..355d70e813bb 100755 --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/architecture.h @@ -570,8 +570,65 @@ # endif +#elif (defined(__s390x__)) +# undef vax +# undef mips +# undef hp_pa +# undef cray +# undef alpha +# undef ix86 +# undef merced +# undef amd64 +# undef sparc + +# define s390 10 +# define ARCHITECTURE s390 + +# define LOCAL_DATA 1 +# undef STATIC_ROUNDING_MODES +# define DYNAMIC_ROUNDING_MODES 1 +# define DENORMS_EMULATED 1 +# define SEPARATE_FLOAT_REGS 1 +# undef MULTIPLE_ISSUE +# undef UNSIGNED_TO_FLOAT +# define UNSIGNED_MULTIPLY 1 +# define ENDIANESS big_endian +# define SCALE_METHOD by_int +# define CVT_TO_HI_LO_METHOD by_flt + +# define BITS_PER_CHAR 8 +# define BITS_PER_SHORT 16 +# define BITS_PER_INT 32 +# define BITS_PER_LONG 64 + +# define BITS_PER_ADDRESS 64 + +# define BITS_PER_FLOAT 32 +# define BITS_PER_DOUBLE 64 +# define BITS_PER_LONG_DOUBLE 128 +# define LONG_DOUBLE_128_TYPE _Quad + +# define __INT_64 long long + +# define INT_8 signed char +# define INT_16 signed short +# define INT_32 signed int +# define INT_64 signed __INT_64 +# undef INT_128 +# define U_INT_8 unsigned char +# define U_INT_16 unsigned short +# define U_INT_32 unsigned int +# define U_INT_64 unsigned __INT_64 +# undef U_INT_128 + /* Setup for 64 bits */ +# define WORD INT_64 +# define U_WORD U_INT_64 +# define BITS_PER_WORD 64 +# define HALF_WORD INT_32 +# define U_HALF_WORD U_INT_32 +# define BITS_PER_HALF_WORD 32 #elif (defined(_M_AMD64)) diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_private.h b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_private.h index 2d429afafc17..d57406c9014c 100755 --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_private.h +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_private.h @@ -242,6 +242,10 @@ versions until we need them. ] */ # include "ix86_macros.h" +#elif (ARCHITECTURE == s390 ) + +# include "ix86_macros.h" + #else # error Unknown ARCHITECTURE. diff --git a/src/third_party/IntelRDFPMathLib20U1/TESTS/readtest.c b/src/third_party/IntelRDFPMathLib20U1/TESTS/readtest.c index afb77da454c2..24e91685de52 100755 --- a/src/third_party/IntelRDFPMathLib20U1/TESTS/readtest.c +++ b/src/third_party/IntelRDFPMathLib20U1/TESTS/readtest.c @@ -449,7 +449,7 @@ BID_UINT32 a32, b32, c32, q32, r32; BID_UINT64 a64, b64, c64, q64, r64; BID_UINT128 a, b, c, q, r; -char AI8; +signed char AI8; unsigned char AUI8; short AI16, BI16; unsigned short AUI16, BUI16; @@ -466,7 +466,7 @@ unsigned short u1_16, u2_16; short i1_16, i2_16; unsigned char u1_8, u2_8; -char i1_8, i2_8; +signed char i1_8, i2_8; unsigned int expected_status; diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.c b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.c --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.c +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux_ops.c @@ -666,3 +666,3 @@ } - return fp_class | ((WORD) 1 << (BITS_PER_WORD - 1)); + return fp_class | ((U_WORD) 1 << (BITS_PER_WORD - 1)); } diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux.h b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux.h --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux.h +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/float128/dpml_ux.h @@ -129,3 +129,3 @@ #define UX_UNDERFLOW_EXPONENT (- UX_OVERFLOW_EXPONENT) -#define UX_ZERO_EXPONENT (- (UX_EXPONENT_TYPE) 1 << (F_EXP_WIDTH + 2)) +#define UX_ZERO_EXPONENT (- ((UX_EXPONENT_TYPE) 1 << (F_EXP_WIDTH + 2))) #define UX_INFINITY_EXPONENT (-(UX_ZERO_EXPONENT + 1)) diff --git a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_functions.h b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_functions.h --- a/src/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_functions.h +++ b/src/third_party/IntelRDFPMathLib20U1/LIBRARY/src/bid_functions.h @@ -45,3 +45,3 @@ // Fix system header issue on Sun solaris and define required type by ourselves -#if !defined(_WCHAR_T) && !defined(_WCHAR_T_DEFINED) && !defined(__QNX__) +#if !defined(_WCHAR_T) && !defined(_WCHAR_T_DEFINED) && !defined(__QNX__) && !defined(__cplusplus) typedef int wchar_t; libmongocrypt-1.19.0/etc/packager.py000077500000000000000000000766551521103432300174140ustar00rootroot00000000000000#!/usr/bin/env python3 """Packager module. This program makes Debian and RPM repositories for libmongocrypt, by downloading our tarballs and forming them into Linux packages. It must be run on a Debianoid, since Debian provides tools to make RPMs, but RPM-based systems don't provide debian packaging crud. This program was adapted from the program of the same name in the MongoDB server repository: https://github.com/mongodb/mongo/blob/master/buildscripts/packager.py Notes ----- * Almost anything that you want to be able to influence about how a package construction must be embedded in some file that the packaging tool uses for input (e.g., debian/rules, debian/control, debian/changelog; or the RPM specfile), and the precise details are arbitrary and silly. So this program generates all the relevant inputs to the packaging tools. * Once a .deb or .rpm package is made, there's a separate layer of tools that makes a "repository" for use by the apt/yum layers of package tools. The layouts of these repositories are arbitrary and silly, too. * Before you run the program on a new host, these are the prerequisites: apt-get install dpkg-dev rpm debhelper fakeroot ia32-libs createrepo git-core echo "Now put the dist gnupg signing keys in ~root/.gnupg" Differences from the server version of this script -------------------------------------------------- * The version numbering scheme is a bit different, so the regexes in this script are modified to match the versioning of libmongocrypt. Naturally, instances of the package name 'mongodb' have been replaced with 'libmongocrypt', and since libmonogcrypt does not use the 'org' and 'enterprise' suffixes, those have been left out as well. * The server version of this script has been updated to generate the changelog based on Git history, while this version of the script continues to use the boilerplate changelog. * Other specific divergences, especially where care should be taken in order to not clobber when syncing changes from the server version of the script, are noted with explanatory comments as needed. These comments are prefixed 'MC:' to make them easily distinguished from other comments coming from the original source. """ import argparse from datetime import datetime import errno from glob import glob import os import re import shutil import subprocess import sys import tempfile import time # The MongoDB names for the architectures we support. ARCH_CHOICES = ["x86_64", "arm64", "aarch64", "s390x", "ppc64le"] # Made up names for the flavors of distribution we package for. DISTROS = ["suse", "debian", "redhat", "ubuntu", "amazon", "amazon2", "amazon2023"] class Spec(object): """Spec class.""" def __init__(self, ver, gitspec=None, rel=None): """Initialize Spec.""" self.ver = ver self.gitspec = gitspec self.rel = rel # Commit-triggerd version numbers can be in the form: 3.0.7-pre-, or 3.0.7-5-g3b67ac # Patch builds version numbers are in the form: 3.5.5-64-g03945fa-patch-58debcdb3ff1223c9d00005b # def is_nightly(self): """Return True if nightly.""" return bool(re.search("-$", self.version())) or bool( re.search(r"\d-\d+-g[0-9a-f]+$", self.version())) def is_patch(self): """Return True if patch.""" return bool(re.search(r"\d-\d+-g[0-9a-f]+-patch-[0-9a-f]+$", self.version())) def is_rc(self): """Return True if rc.""" return bool(re.search(r"-rc\d+(\+[0-9]{8}git[0-9a-f]+)?$", self.version())) # MC: libmongocrypt also has beta releases def is_beta(self): """Return True if beta.""" return bool(re.search(r"-beta\d+(\+[0-9]{8}git[0-9a-f]+)?$", self.version())) def is_pre_release(self): """Return True if pre-release.""" return self.is_rc() or self.is_beta() or self.is_nightly() def version(self): """Return version.""" return self.ver def patch_id(self): """Return patch id.""" if self.is_patch(): return re.sub(r'.*-([0-9a-f]+$)', r'\1', self.version()) return "none" def metadata_gitspec(self): """Git revision to use for spec+control+init+manpage files. The default is the release tag for the version being packaged. """ if self.gitspec: return self.gitspec return 'r' + self.version() # MC: Leave out the version_better_than() and suffix() funtions, which are # not used by libmongocrypt's packaging workflow def prelease(self): """Return pre-release verison suffix.""" # NOTE: This is only called for RPM packages, and only after # pversion() below has been called. If you want to change this format # and want DEB packages to match, make sure to update pversion() # below # # "N" is either passed in on the command line, or "1" if self.rel: corenum = self.rel else: corenum = 1 # Version suffix for RPM packages: # 1) RC's - "0.N.rcX" # 2) Nightly (snapshot) - "0.N.latest" # 3) Patch builds - "0.N.patch." # 4) Standard release - "N" if self.is_rc(): return "0.%s.%s" % (corenum, re.sub('.*-', '', self.version())) elif self.is_beta(): return "0.%s.beta.%s" % (corenum, re.sub('.*-beta', '', self.version())) elif self.is_nightly(): return "0.%s.latest" % (corenum) elif self.is_patch(): return "0.%s.patch.%s" % (corenum, self.patch_id()) return str(corenum) def pversion(self, distro): """Return the pversion.""" # Note: Debian packages have funny rules about dashes in # version numbers, and RPM simply forbids dashes. pversion # will be the package's version number (but we need to know # our upstream version too). # For RPM packages this just returns X.Y.X because of the # aforementioned rules, and prelease (above) adds a suffix later, # so detect this case early if re.search("(suse|redhat|fedora|centos|amazon)", distro.name()): return re.sub("-.*", "", self.version()) # For DEB packages, this code sets the full version. If you change # this format and want RPM packages to match make sure you change # prelease above as well if re.search("^(debian|ubuntu)", distro.name()): if self.is_nightly(): ver = re.sub("-.*", "-latest", self.ver) elif self.is_patch(): ver = re.sub("-.*", "", self.ver) + "-patch-" + self.patch_id() else: ver = self.ver return re.sub("-", "~", ver) raise Exception("BUG: unsupported platform?") def branch(self): """Return the major and minor portions of the specified version. For example, if the version is "2.5.5" the branch would be "2.5" """ return ".".join(self.ver.split(".")[0:2]) class Distro(object): """Distro class.""" def __init__(self, string): """Initialize Distro.""" self.dname = string def name(self): """Return name.""" return self.dname @staticmethod def pkgbase(): """Return pkgbase.""" return "libmongocrypt" def archname(self, arch): """Return the packaging system's architecture name. Power and x86 have different names for apt/yum (ppc64le/ppc64el and x86_64/amd64). """ if re.search("^(debian|ubuntu)", self.dname): if arch == "ppc64le": return "ppc64el" elif arch == "s390x": return "s390x" elif arch == "arm64": return "arm64" elif arch.endswith("86"): return "i386" return "amd64" elif re.search("^(suse|centos|redhat|fedora|amazon)", self.dname): if arch == "ppc64le": return "ppc64le" elif arch == "s390x": return "s390x" elif arch.endswith("86"): return "i686" elif arch == "arm64": return "arm64" elif arch == "aarch64": return "aarch64" return "x86_64" else: raise Exception("BUG: unsupported platform?") def repodir(self, arch, build_os, spec): # noqa: D406,D407,D412,D413 """Return the directory where we'll place the package files for (distro, distro_version). This is in that distro's preferred repository layout (as distinct from where that distro's packaging building tools place the package files). Examples: repo/apt/ubuntu/dists/precise/libmongocrypt/2.5/universe/binary-amd64 repo/apt/ubuntu/dists/precise/libmongocrypt/2.5/universe/binary-i386 repo/apt/ubuntu/dists/trusty/libmongocrypt/2.5/universe/binary-amd64 repo/apt/ubuntu/dists/trusty/libmongocrypt/2.5/universe/binary-i386 repo/apt/debian/dists/wheezy/libmongocrypt/2.5/main/binary-amd64 repo/apt/debian/dists/wheezy/libmongocrypt/2.5/main/binary-i386 repo/yum/redhat/6/libmongocrypt/2.5/x86_64 yum/redhat/6/libmongocrypt/2.5/i386 repo/zypper/suse/11/libmongocrypt/2.5/x86_64 zypper/suse/11/libmongocrypt/2.5/i386 """ repo_directory = "" if spec.is_pre_release(): repo_directory = "testing" else: repo_directory = spec.branch() if re.search("^(debian|ubuntu)", self.dname): return "repo/apt/%s/dists/%s/libmongocrypt/%s/%s/binary-%s/" % ( self.dname, self.repo_os_version(build_os), repo_directory, self.repo_component(), self.archname(arch)) elif re.search("(redhat|fedora|centos|amazon)", self.dname): return "repo/yum/%s/%s/libmongocrypt/%s/%s/RPMS/" % ( self.dname, self.repo_os_version(build_os), repo_directory, self.archname(arch)) elif re.search("(suse)", self.dname): return "repo/zypper/%s/%s/libmongocrypt/%s/%s/RPMS/" % ( self.dname, self.repo_os_version(build_os), repo_directory, self.archname(arch)) else: raise Exception("BUG: unsupported platform?") def repo_component(self): """Return the name of the section/component/pool we are publishing into. Example, "universe" for Ubuntu, "main" for debian. """ # MC: libmongocrypt uses the 'universe' section for Ubuntu, rather than # 'multiverse' like the server if self.dname == 'ubuntu': return "universe" elif self.dname == 'debian': return "main" else: raise Exception("unsupported distro: %s" % self.dname) def repo_os_version(self, build_os): """Return an OS version suitable for package repo directory naming. Example, 5, 6 or 7 for redhat/centos, "precise," "wheezy," etc. for Ubuntu/Debian, 11 for suse, "2013.03" for amazon. """ if self.dname == 'suse': return re.sub(r'^suse(\d+)$', r'\1', build_os) if self.dname == 'redhat': return re.sub(r'^rhel(\d).*$', r'\1', build_os) if self.dname == 'amazon': return "2013.03" elif self.dname == 'amazon2': return "2017.12" elif self.dname == 'amazon2023': return "2023.0" elif self.dname == 'ubuntu': if build_os == 'ubuntu1204': return "precise" elif build_os == 'ubuntu1404': return "trusty" elif build_os == 'ubuntu1604': return "xenial" elif build_os == 'ubuntu1804': return "bionic" elif build_os == 'ubuntu2004': return "focal" elif build_os == 'ubuntu2204': return "jammy" elif build_os == 'ubuntu2404': return "noble" else: raise Exception("unsupported build_os: %s" % build_os) elif self.dname == 'debian': if build_os == 'debian81': return 'jessie' elif build_os == 'debian92': return 'stretch' elif build_os == 'debian10': return 'buster' elif build_os == 'debian11': return 'bullseye' elif build_os == 'debian12': return 'bookworm' elif build_os == 'debian13': return 'trixie' else: raise Exception("unsupported build_os: %s" % build_os) else: raise Exception("unsupported distro: %s" % self.dname) def make_pkg(self, build_os, arch, spec, srcdir): """Return the package.""" if re.search("^(debian|ubuntu)", self.dname): return make_deb(self, build_os, arch, spec, srcdir) elif re.search("^(suse|centos|redhat|fedora|amazon)", self.dname): return make_rpm(self, build_os, arch, spec, srcdir) else: raise Exception("BUG: unsupported platform?") def build_os(self, arch): """Return the build os label in the binary package to download. Example, "rhel55" for redhat, "ubuntu1204" for ubuntu, "debian81" for debian, "suse11" for suse, etc. """ # Community builds only support amd64 if arch not in ['x86_64', 'ppc64le', 's390x', 'arm64', 'aarch64']: raise Exception("BUG: unsupported architecture (%s)" % arch) if re.search("(suse)", self.dname): return ["suse11", "suse12", "suse15"] elif re.search("(redhat|fedora|centos)", self.dname): return [ "rhel91", "rhel83", "rhel82", "rhel81", "rhel80", "rhel70", "rhel71", "rhel72", "rhel62", "rhel55", "rhel67" ] elif self.dname in ['amazon', 'amazon2', 'amazon2023']: return [self.dname] elif self.dname == 'ubuntu': return [ "ubuntu1204", "ubuntu1404", "ubuntu1604", "ubuntu1804", "ubuntu2004", "ubuntu2204", "ubuntu2404", ] elif self.dname == 'debian': return ["debian81", "debian92", "debian10", "debian11", "debian12", "debian13"] else: raise Exception("BUG: unsupported platform?") def release_dist(self, build_os): """Return the release distribution to use in the rpm. "el5" for rhel 5.x, "el6" for rhel 6.x, return anything else unchanged. """ if self.dname == 'amazon': return 'amzn1' elif self.dname == 'amazon2': return 'amzn2' elif self.dname == 'amazon2023': return 'amzn2023' return re.sub(r'^rh(el\d).*$', r'\1', build_os) def get_args(distros, arch_choices): """Return the program arguments.""" distro_choices = [] for distro in distros: for arch in arch_choices: distro_choices.extend(distro.build_os(arch)) parser = argparse.ArgumentParser(description='Build libmongocrypt Packages') parser.add_argument("-v", "--library-version", help="Library version to build (e.g. 1.0.0-beta2)", required=True) parser.add_argument("-m", "--metadata-gitspec", help="Gitspec to use for package metadata files", required=False) parser.add_argument("-r", "--release-number", help="RPM release number base", type=int, required=False) parser.add_argument("-d", "--distros", help="Distros to build for", choices=distro_choices, required=False, default=[], action='append') parser.add_argument("-p", "--prefix", help="Directory to build into", required=False) parser.add_argument("-a", "--arches", help="Architecture to build", choices=arch_choices, default=[], required=False, action='append') parser.add_argument("-t", "--tarball", help="Local tarball to package", required=True, type=lambda x: is_valid_file(parser, x)) args = parser.parse_args() if len(args.distros) * len(args.arches) > 1 and args.tarball: parser.error("Can only specify local tarball with one distro/arch combination") return args def main(): """Execute Main program.""" distros = [Distro(distro) for distro in DISTROS] args = get_args(distros, ARCH_CHOICES) spec = Spec(args.library_version, args.metadata_gitspec, args.release_number) oldcwd = os.getcwd() srcdir = oldcwd + "/../" # Where to do all of our work. Use a randomly-created directory if one # is not passed in. prefix = args.prefix if prefix is None: prefix = tempfile.mkdtemp() print("Working in directory %s" % prefix) os.chdir(prefix) try: # Build a package for each distro/spec/arch tuple, and # accumulate the repository-layout directories. for (distro, arch) in crossproduct(distros, args.arches): for build_os in distro.build_os(arch): if build_os in args.distros or not args.distros: filename = tarfile(build_os, arch, spec) ensure_dir(filename) shutil.copyfile(args.tarball, filename) repo = make_package(distro, build_os, arch, spec, srcdir) make_repo(repo, distro, build_os) finally: os.chdir(oldcwd) def crossproduct(*seqs): """Provide a generator for iterating all the tuples consisting of elements of seqs.""" num_seqs = len(seqs) if num_seqs == 0: pass elif num_seqs == 1: for idx in seqs[0]: yield [idx] else: for lst in crossproduct(*seqs[:-1]): for idx in seqs[-1]: lst2 = list(lst) lst2.append(idx) yield lst2 def sysassert(argv): """Run argv and assert that it exited with status 0.""" print("In %s, running %s" % (os.getcwd(), " ".join(argv))) sys.stdout.flush() sys.stderr.flush() assert subprocess.Popen(argv).wait() == 0 def backtick(argv): """Run argv and return its output string.""" print("In %s, running %s" % (os.getcwd(), " ".join(argv))) sys.stdout.flush() sys.stderr.flush() return subprocess.Popen(argv, stdout=subprocess.PIPE).communicate()[0] def tarfile(build_os, arch, spec): """Return the location where we store the downloaded tarball for this package.""" return "dl/libmongocrypt-%s-%s-%s.tar.gz" % (spec.version(), build_os, arch) def setupdir(distro, build_os, arch, spec): """Return the setup directory name.""" # The setupdir will be a directory containing all inputs to the # distro's packaging tools (e.g., package metadata files, init # scripts, etc), along with the already-built binaries). In case # the following format string is unclear, an example setupdir # would be dst/x86_64/debian-sysvinit/wheezy/libmongocrypt/ # or dst/x86_64/redhat/rhel55/libmongocrypt/ return "dst/%s/%s/%s/%s-%s/" % (arch, distro.name(), build_os, distro.pkgbase(), spec.pversion(distro)) def unpack_binaries_into(build_os, arch, spec, where): """Unpack the tarfile for (build_os, arch, spec) into directory where.""" rootdir = os.getcwd() ensure_dir(where) # Note: POSIX tar doesn't require support for gtar's "-C" option, # and Python's tarfile module prior to Python 2.7 doesn't have the # features to make this detail easy. So we'll just do the dumb # thing and chdir into where and run tar there. os.chdir(where) try: sysassert(["tar", "xvzf", rootdir + "/" + tarfile(build_os, arch, spec)]) release_dir = glob('libmongocrypt-*')[0] for releasefile in "lib", "lib64", "include", "LICENSE", "README.md": # MC: wrap print() and os.rename() in a conditional since as a library # we have to consider that sometimes artifacts are in lib and other # times they are in lib64 if os.path.exists("%s/%s" % (release_dir, releasefile)): print("moving file: %s/%s" % (release_dir, releasefile)) os.rename("%s/%s" % (release_dir, releasefile), releasefile) os.rmdir(release_dir) except Exception: exc = sys.exc_info()[1] os.chdir(rootdir) raise exc os.chdir(rootdir) def make_package(distro, build_os, arch, spec, srcdir): """Construct the package for (arch, distro, spec). Get the packaging files from srcdir and any user-specified suffix from suffixes. """ sdir = setupdir(distro, build_os, arch, spec) ensure_dir(sdir) # Note that the RPM packages get their man pages from the debian # directory, so the debian directory is needed in all cases (and # innocuous in the debianoids' sdirs). for pkgdir in ["etc/debian", "etc/rpm"]: print("Copying packaging files from %s to %s" % ("%s/%s" % (srcdir, pkgdir), sdir)) # FIXME: sh-dash-cee is bad. See if tarfile can do this. sysassert([ "sh", "-c", # MC: we use --strip-components=1 since the 'debian/' and 'rpm/' # dirs are under 'etc/' in the libmongocrypt repo "(cd \"%s\" && tar cf - %s ) | (cd \"%s\" && tar --strip-components=1 -xvf -)" % (srcdir, pkgdir, sdir) ]) # Splat the binaries under sdir. The "build" stages of the # packaging infrastructure will move the files to wherever they # need to go. unpack_binaries_into(build_os, arch, spec, sdir) return distro.make_pkg(build_os, arch, spec, srcdir) def make_repo(repodir, distro, build_os): """Make the repo.""" if re.search("(debian|ubuntu)", repodir): make_deb_repo(repodir, distro, build_os) elif re.search("(suse|centos|redhat|fedora|amazon)", repodir): make_rpm_repo(repodir) else: raise Exception("BUG: unsupported platform?") def make_deb(distro, build_os, arch, spec, srcdir): """Make the Debian script.""" # I can't remember the details anymore, but the initscript/upstart # job files' names must match the package name in some way; and # see also the --name flag to dh_installinit in the generated # debian/rules file. # MC: We leave out all the stuff that the server does related to init scripts sdir = setupdir(distro, build_os, arch, spec) # Rewrite the control and rules files write_debian_changelog(sdir + "debian/changelog", spec, srcdir, distro) distro_arch = distro.archname(arch) sysassert([ "cp", "-v", srcdir + "etc/debian/control", sdir + "debian/control" ]) sysassert([ "cp", "-v", srcdir + "etc/debian/rules", sdir + "debian/rules" ]) # MC: Skip some server-specific things, like variations of the controls and # rules files, as well as maintainer scripts (i.e., postinst) # Do the packaging. oldcwd = os.getcwd() try: os.chdir(sdir) sysassert(["dpkg-buildpackage", "-uc", "-us", "-a" + distro_arch]) finally: os.chdir(oldcwd) repo_dir = distro.repodir(arch, build_os, spec) ensure_dir(repo_dir) # FIXME: see if shutil.copyfile or something can do this without # much pain. sysassert(["sh", "-c", "cp -v \"%s/../\"*.deb \"%s\"" % (sdir, repo_dir)]) return repo_dir def make_deb_repo(repo, distro, build_os): """Make the Debian repo.""" # Note: the Debian repository Packages files must be generated # very carefully in order to be usable. oldpwd = os.getcwd() os.chdir(repo + "../../../../../../") try: dirs = { os.path.dirname(deb)[2:] for deb in backtick(["find", ".", "-name", "*.deb"]).decode('utf-8').split() } for directory in dirs: st = backtick(["dpkg-scanpackages", directory, "/dev/null"]) with open(directory + "/Packages", "wb") as fh: fh.write(st) bt = backtick(["gzip", "-9c", directory + "/Packages"]) with open(directory + "/Packages.gz", "wb") as fh: fh.write(bt) finally: os.chdir(oldpwd) # Notes: the Release{,.gpg} files must live in a special place, # and must be created after all the Packages.gz files have been # done. s1 = """Origin: libmongocrypt Label: libmongocrypt Suite: %s Codename: %s/libmongocrypt Architectures: amd64 arm64 ppc64el s390x Components: %s Description: libmongocrypt packages """ % (distro.repo_os_version(build_os), distro.repo_os_version(build_os), distro.repo_component()) if os.path.exists(repo + "../../Release"): os.unlink(repo + "../../Release") if os.path.exists(repo + "../../Release.gpg"): os.unlink(repo + "../../Release.gpg") oldpwd = os.getcwd() os.chdir(repo + "../../") s2 = backtick(["apt-ftparchive", "release", "."]) try: with open("Release", 'wb') as fh: fh.write(s1.encode('utf-8')) fh.write(s2) finally: os.chdir(oldpwd) def move_repos_into_place(src, dst): """Move the repos into place.""" # Find all the stuff in src/*, move it to a freshly-created # directory beside dst, then play some games with symlinks so that # dst is a name the new stuff and dst+".old" names the previous # one. This feels like a lot of hooey for something so trivial. # First, make a crispy fresh new directory to put the stuff in. i = 0 while True: date_suffix = time.strftime("%Y-%m-%d") dname = dst + ".%s.%d" % (date_suffix, i) try: os.mkdir(dname) break except OSError: exc = sys.exc_info()[1] if exc.errno == errno.EEXIST: pass else: raise exc i = i + 1 # Put the stuff in our new directory. for src_file in os.listdir(src): sysassert(["cp", "-rv", src + "/" + src_file, dname]) # Make a symlink to the new directory; the symlink will be renamed # to dst shortly. i = 0 while True: tmpnam = dst + ".TMP.%d" % i try: os.symlink(dname, tmpnam) break except OSError: # as exc: # Python >2.5 exc = sys.exc_info()[1] if exc.errno == errno.EEXIST: pass else: raise exc i = i + 1 # Make a symlink to the old directory; this symlink will be # renamed shortly, too. oldnam = None if os.path.exists(dst): i = 0 while True: oldnam = dst + ".old.%d" % i try: os.symlink(os.readlink(dst), oldnam) break except OSError: # as exc: # Python >2.5 exc = sys.exc_info()[1] if exc.errno == errno.EEXIST: pass else: raise exc os.rename(tmpnam, dst) if oldnam: os.rename(oldnam, dst + ".old") def write_debian_changelog(path, spec, srcdir, distro): """Write the debian changelog.""" oldcwd = os.getcwd() os.chdir(srcdir) # MC: Rather than using Git to generate the changelog (like the server), we # just use a simple boilerplate changelog preamble = "libmongocrypt (%s-0) unstable; urgency=medium\n\n" % spec.pversion(distro) preamble += " * Built from Evergreen.\n\n" preamble += " -- Roberto C. Sanchez " preamble += "%s\n\n" % datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S +0000") try: sb = preamble + backtick([ "sh", "-c", "cat etc/debian/changelog" ]).decode('utf-8') finally: os.chdir(oldcwd) # MC: No need to munge the version and rewrite the changelog with open(path, 'w') as fh: fh.write(sb) def make_rpm(distro, build_os, arch, spec, srcdir): """Create the RPM specfile.""" sdir = setupdir(distro, build_os, arch, spec) specfile = srcdir + "etc/rpm/libmongocrypt.spec" # No need to mess with init scripts and such like the server topdir = ensure_dir('%s/rpmbuild/%s/' % (os.getcwd(), build_os)) for subdir in ["BUILD", "RPMS", "SOURCES", "SPECS", "SRPMS"]: ensure_dir("%s/%s/" % (topdir, subdir)) distro_arch = distro.archname(arch) # Places the RPM Spec file where it's expected for the rpmbuild execution later. shutil.copy(specfile, topdir + "SPECS") oldcwd = os.getcwd() os.chdir(sdir + "/../") try: sysassert([ "tar", "-cpzf", topdir + "SOURCES/libmongocrypt-%s.tar.gz" % spec.pversion(distro), os.path.basename(os.path.dirname(sdir)) ]) finally: os.chdir(oldcwd) # Do the build. # MC: Dump the rpm config. bt = backtick(["rpm", "--showrc"]).decode('utf-8') print(bt) # # MC: Add some macro definitions to the rpmbuild invocation: # # _arch - it seems like this one ought to be defined by RPM, but it appears # to not be and without it the build fails to find some artifacts # # _smp_build_ncpus - it seems that on some platforms xargs is invoked in # such a way that if _smp_build_ncpus is not set (which it apparently is not # on every platform) then the xargs invocation fails # # debug_package - tells rpmbuild that there is no package to build with # debugging information/symbols; we don't generate them and since we are # packaging a library rpmbuild expects to find them # flags = [ "-D", "_smp_build_ncpus 1", "-D", "debug_package %{nil}", "-D", "_arch {}".format(arch), "-D", "_topdir {}".format(topdir), "-D", "dist .{}".format(distro.release_dist(build_os)), "-D", "dynamic_version {}".format(spec.pversion(distro)), "-D", "dynamic_release {}".format(spec.prelease()), ] # Versions of RPM after 4.4 ignore our BuildRoot tag so we need to # specify it on the command line args to rpmbuild if ((distro.name() == "suse" and distro.repo_os_version(build_os) == "15") or (distro.name() == "redhat" and distro.repo_os_version(build_os) == "8")): flags.extend([ "--buildroot", os.path.join(topdir, "BUILDROOT"), ]) sysassert(["rpmbuild", "-ba", "--target", distro_arch] + flags + ["%s/SPECS/libmongocrypt.spec" % topdir]) repo_dir = distro.repodir(arch, build_os, spec) ensure_dir(repo_dir) # FIXME: see if some combination of shutil.copy and glob # can do this without shelling out. sysassert(["sh", "-c", "cp -v \"%s/RPMS/%s/\"*.rpm \"%s\"" % (topdir, distro_arch, repo_dir)]) return repo_dir def make_rpm_repo(repo): """Make the RPM repo.""" oldpwd = os.getcwd() os.chdir(repo + "../") try: sysassert(["createrepo", "."]) finally: os.chdir(oldpwd) def ensure_dir(filename): """Ensure that the dirname directory of filename exists, and return filename.""" dirpart = os.path.dirname(filename) try: os.makedirs(dirpart) except OSError: # as exc: # Python >2.5 exc = sys.exc_info()[1] if exc.errno == errno.EEXIST: pass else: raise exc return filename def is_valid_file(parser, filename): """Check if file exists, and return the filename.""" if not os.path.exists(filename): parser.error("The file %s does not exist!" % filename) return None return filename if __name__ == "__main__": main() libmongocrypt-1.19.0/etc/print-marking.py000066400000000000000000000020621521103432300203730ustar00rootroot00000000000000instructions = """ Print the contents of a marking from mongocryptd. Usage: python print-marking.py Example: python ./etc/print-marking.py ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA """ import sys import base64 import bson from bson import BSON from bson import json_util from bson.codec_options import CodecOptions if len(sys.argv) != 2: print(instructions) sys.exit(1) marking = base64.b64decode(sys.argv[1]) codec_options = CodecOptions(uuid_representation=bson.binary.STANDARD) json_options = json_util.JSONOptions(json_mode=json_util.JSONMode.CANONICAL, uuid_representation=bson.binary.STANDARD) if marking[0] != 0: print("Invalid first byte: {}".format(marking[0])) marking_bson = BSON(marking[1:]).decode(codec_options=codec_options) print(json_util.dumps(marking_bson, indent=4, json_options=json_options)) if "a" in marking_bson: if marking_bson["a"] == 1: print ("Algorithm is deterministic") elif marking_bson["a"] == 2: print ("Algorithm is random")libmongocrypt-1.19.0/etc/purls.txt000066400000000000000000000015141521103432300171460ustar00rootroot00000000000000# These package URLs (purls) point to the versions (tags) of external dependencies # that are committed to the project. Refer: https://github.com/package-url/purl-spec # This file is fed to silkbomb to generate the cyclonedx.sbom.json file. Edit this file # instead of modifying the SBOM JSON directly. After modifying this file, be sure to # re-generate the SBOM JSON file with: `./.evergreen/earthly.sh +sbom-generate`. If # adding a new dependency, ensure the resulting SBOM JSON includes the `licenses` and # `copyright` property. This information can be manually added. # libbson is obtained via `cmake/FetchMongoC.cmake`. pkg:github/mongodb/mongo-c-driver@v2.3.0?#src/libbson # IntelDFP is obtained via `cmake/IntelDFP.cmake` pkg:generic/IntelRDFPMathLib@20U2?download_url=https://www.netlib.org/misc/intel/IntelRDFPMathLib20U2.tar.gz libmongocrypt-1.19.0/etc/repo_config.yaml000066400000000000000000000147571521103432300204330ustar00rootroot00000000000000services: notary_url: "http://notary-service.build.10gen.cc:5000" templates: deb: org: | Origin: libmongocrypt Label: libmongocrypt Suite: {{ .CodeName }} Codename: {{ .CodeName }}/libmongocrypt Architectures: {{ .Architectures }} Components: {{ .Component }} Description: libmongocrypt packages index_page: | {{ .Title }} {{ range $fn := .Files }} {{ end }}

{{ .Title }}


Parent Directory
{{ $fn }}

{{ .RepoName }}
repos: - name: rhel62 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/6/libmongocrypt - yum/redhat/6Server/libmongocrypt - name: rhel67 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/6/libmongocrypt - yum/redhat/6Server/libmongocrypt - name: rhel70 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/7/libmongocrypt - yum/redhat/7Server/libmongocrypt - name: rhel71 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/7/libmongocrypt - yum/redhat/7Server/libmongocrypt - name: rhel72 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/7/libmongocrypt - yum/redhat/7Server/libmongocrypt - name: rhel80 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/8/libmongocrypt - yum/redhat/8Server/libmongocrypt - name: rhel81 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/8/libmongocrypt - yum/redhat/8Server/libmongocrypt - name: rhel82 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/8/libmongocrypt - yum/redhat/8Server/libmongocrypt - name: rhel83 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/8/libmongocrypt - yum/redhat/8Server/libmongocrypt - name: rhel91 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/redhat/9/libmongocrypt - yum/redhat/9Server/libmongocrypt - name: amazon type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/amazon/2013.03/libmongocrypt - name: amazon2 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/amazon/2/libmongocrypt - name: amazon2023 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - yum/amazon/2023/libmongocrypt - name: suse12 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - zypper/suse/12/libmongocrypt - name: suse15 type: rpm edition: org bucket: libmongocrypt region: us-east-1 repos: - zypper/suse/15/libmongocrypt - name: debian81 type: deb code_name: "jessie" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - i386 repos: - apt/debian/dists/jessie/libmongocrypt - name: debian92 type: deb code_name: "stretch" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - arm64 repos: - apt/debian/dists/stretch/libmongocrypt - name: debian10 type: deb code_name: "buster" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - arm64 repos: - apt/debian/dists/buster/libmongocrypt - name: debian11 type: deb code_name: "bullseye" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - arm64 repos: - apt/debian/dists/bullseye/libmongocrypt - name: debian12 type: deb code_name: "bookworm" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - arm64 repos: - apt/debian/dists/bookworm/libmongocrypt - name: debian13 type: deb code_name: "trixie" bucket: libmongocrypt region: us-east-1 edition: org component: main architectures: - amd64 - arm64 repos: - apt/debian/dists/trixie/libmongocrypt - name: ubuntu1404 type: deb code_name: "trusty" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - i386 repos: - apt/ubuntu/dists/trusty/libmongocrypt - name: ubuntu1604 type: deb code_name: "xenial" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - arm64 - i386 repos: - apt/ubuntu/dists/xenial/libmongocrypt - name: ubuntu1804 type: deb code_name: "bionic" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - i386 - arm64 repos: - apt/ubuntu/dists/bionic/libmongocrypt - name: ubuntu2004 type: deb code_name: "focal" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - i386 - arm64 repos: - apt/ubuntu/dists/focal/libmongocrypt - name: ubuntu2204 type: deb code_name: "jammy" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - i386 - arm64 repos: - apt/ubuntu/dists/jammy/libmongocrypt - name: ubuntu2404 type: deb code_name: "noble" edition: org bucket: libmongocrypt region: us-east-1 component: universe architectures: - amd64 - i386 - arm64 repos: - apt/ubuntu/dists/noble/libmongocrypt libmongocrypt-1.19.0/etc/requirements.txt000066400000000000000000000000151521103432300205170ustar00rootroot00000000000000boto3 pymongolibmongocrypt-1.19.0/etc/rewrite.py000066400000000000000000000005561521103432300173000ustar00rootroot00000000000000help = """ Script to rewrite \n to \r\n Usage: python3 rewrite.py file-in.txt file-out.txt file-in.txt will be rewritten to have \n replaced with \r\n. """ import sys if len(sys.argv) != 3: print (help) sys.exit(1) src = open (sys.argv[1], "r") dst = open (sys.argv[2], "w") for line in src: print (line) dst.write(line.strip() + "\r\n") dst.close()libmongocrypt-1.19.0/etc/rpm/000077500000000000000000000000001521103432300160355ustar00rootroot00000000000000libmongocrypt-1.19.0/etc/rpm/libmongocrypt.spec000066400000000000000000000031461521103432300216050ustar00rootroot00000000000000Name: libmongocrypt Prefix: /usr Version: %{dynamic_version} Release: %{dynamic_release}%{?dist} Summary: library to perform field-level encryption License: Apache License 2.0 URL: https://github.com/mongodb/libmongocrypt Group: Development/Libraries Requires: libmongocrypt-devel = %{version}, libmongocrypt-libs = %{version} Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root %description libmongocrypt facilitates the encryption and decryption, at the field level, of data stored in MongoDB. %package devel Summary: library to perform field-level encryption - dev files Group: Development/Libraries Requires: libmongocrypt-libs = %{version} %description devel libmongocrypt facilitates the encryption and decryption, at the field level, of data stored in MongoDB. . This package contains the libmongocrypt and libkms_message development headers and libraries. %package libs Summary: library to perform field-level encryption - runtime files Group: Development/Libraries %description libs This package contains the libmongocrypt and libkms_message runtime libraries. %prep %setup %build %install mkdir -p $RPM_BUILD_ROOT/usr cp -rv lib* $RPM_BUILD_ROOT/usr cp -rv include $RPM_BUILD_ROOT/usr find $RPM_BUILD_ROOT/usr -name '*.pc' -exec sed -i -e 's|^prefix=.*|prefix=/usr|' {} \; %clean rm -rf $RPM_BUILD_ROOT %files %files devel %{_includedir}/* %{_prefix}/lib*/*.a %{_prefix}/lib*/lib*.so %{_prefix}/lib*/pkgconfig/* %{_prefix}/lib*/cmake/* %files libs %{_prefix}/lib*/lib*.so.* %changelog * Tue Aug 06 2019 Roberto C. Sanchez - Initial release libmongocrypt-1.19.0/etc/rpm/tweak.awk000066400000000000000000000010141521103432300176500ustar00rootroot00000000000000# This script is used to tweak the upstream libmongocrypt.spec file such that # the rpm build will build from a copy of the source tree instead of by # unpacking an archive of the sources # Drop the gcc-13 patch. /Patch.*gcc-13/ { next } # Drop the source. We're bringing our own /Source0/ { next } # Replace the autosetup with one that copies the source tree into place /%autosetup/ { print "cp -rf %{_sourcedir}/. %{_builddir}/\n" \ "%autopatch 0 -p1" next } # Print every other line as-is { print } libmongocrypt-1.19.0/etc/ssdlc_compliance_report.md000066400000000000000000000026001521103432300224540ustar00rootroot00000000000000# libmongocrypt SSDLC Compliance Report ## Release Creator See [C/CXX Release Info](https://docs.google.com/spreadsheets/d/1yHfGmDnbA5-Qt8FX4tKWC5xk9AhzYZx1SKF4AD36ecY/edit?usp=sharing) (internal). ## Process Document Not available. ## Tool used to track third party vulnerabilities Silk and Snyk. ## Third-Party Dependency Information See `cyclonedx.augmented.sbom.json` attached to release. ## Static Analysis Findings See [SSDLC Static Analysis Reports](https://drive.google.com/drive/folders/17bjBnQ3mhEXvs6IK1rrTphJr0CUO2qZh?usp=sharing) (internal) for release-specific reports. ## Security Testing Report See [libmongocrypt Security Testing Summary](https://docs.google.com/document/d/1dc7uvBzu3okAIsA8LSW5sVQGkYIvwpBVdg5v4wb4c4s?usp=sharing) (internal). Available as needed from the libmongocrypt team. ## Security Assessment Report Not applicable to libmongocrypt. ## Signature Information Signatures for Windows binaries are attached to this release and may be verified with `gpg`. The public key for `libmongocrypt` is available on https://pgp.mongodb.com/. ## Known Vulnerabilities Any vulnerabilities that may be shown in the links referenced above have been reviewed and accepted by the appropriate approvers. For detailed information, see `third_party_vulnerabilities.md` attached to release. libmongocrypt-1.19.0/etc/third_party_vulnerabilities.md000066400000000000000000000047601521103432300234020ustar00rootroot00000000000000# 3rd Party Dependency Vulnerabilities This document tracks vulnerabilities in 3rd party dependencies that directly affect the standard release product of libmongocrypt. > [!IMPORTANT] > The "standard release product" is defined as the set of files which are _installed_ by a configuration, build, and install of libmongocrypt. This includes static/shared library files, header files, and packaging files for supported build configurations. Vulnerabilities for 3rd party dependencies that are bundled with the standard release product are reported in this document. > > Language bindings (in the `bindings` directory), test files, utility scripts, documentation generators, and other miscellaneous files and artifacts are NOT considered part of the standard release product, even if they are included in the release distribution tarball. Vulnerabilities for such 3rd party dependencies are NOT reported in this document. ## Template This section provides a template that may be used for actual vulnerability reports further below. ### CVE-YYYY-NNNNNN - **Date Detected:** YYYY-MM-DD - **Severity:** Low, Medium, High, or Critical - **Detector:** Silk or Snyk - **Description:** A short vulnerability description. - **Dependency:** Name and version of the 3rd party dependency. - **Upstream Status:** False Positive, Won't Fix, Fix Pending, or Fix Available. This is the fix status for the 3rd party dependency, not libmongocrypt. "Fix Available" should include the version and/or date when the fix was released, e.g. "Fix Available (1.2.3, 1970-01-01)". - **Fix Status:** False Positive, Won't Fix, Fix Pending, or Fix Committed. This is the fix status for the libmongocrypt. "False Positive" and "Won't Fix" must include rationale in notes below. - **For Release:** The libmongocrypt release version for which the "Fix Status" above was last updated. - **Notes:** Context or rationale for remediation, references to relevant issue trackers, etc. ## libbson ### CVE-2023-0437 - **Date Detected:** 2024-05-20 - **Severity:** Medium - **Detector:** Snyk - **Description:** Loop with Unreachable Exit Condition ('Infinite Loop') - **Dependency:** mongodb/mongo-c-driver@1.17.7 - **Upstream Status:** Fix Available (1.25.0, 2023-11-01). - **Fix Status:** Fix Committed. - **For Release:** 1.10.1 - **Notes:** Fixed in libbson 1.25.0 ([CDRIVER-4747](https://jira.mongodb.org/browse/CDRIVER-4747)). Fixed in libmongocrypt by upgrading libbson to 1.27.1 ([MONGOCRYPT-685](https://jira.mongodb.org/browse/MONGOCRYPT-685)). ## IntelDFP None. libmongocrypt-1.19.0/integrating.md000066400000000000000000000306671521103432300173350ustar00rootroot00000000000000# The guide to integrating libmongocrypt # libmongocrypt is a C library meant to assist drivers in supporting client side encryption. libmongocrypt acts as a state machine and the driver is responsible for I/O between mongod, mongocryptd, crypt_shared, and KMS. There are two major parts to integrating libmongocrypt into your driver: - Writing a language-specific binding to libmongocrypt - Using the binding in your driver to support client side encryption ## Design Rationale ### Simple interface The library interface is intended to be used with multiple languages. The API tries to be minimal. Most structs are opaque. Global initialization is lazy. Much of the API passes and returns BSON since all drivers can produce and parse BSON. ### No I/O libmongocrypt deliberately does not do I/O to avoid poor behavior with some language runtimes. Example: in Go a blocking C call may block an OS thread, rather than a goroutine. ## Part 1: Writing a Language-Specific Binding ## The binding is the glue between your driver\'s native language and libmongocrypt. The binding uses the native language\'s foreign function interface to C. For example, Java can accomplish this with [JNA](https://github.com/java-native-access/jna), CPython with [extensions](https://docs.python.org/3/extending/extending.html), Node.js with [add-ons](https://nodejs.org/api/addons.html), etc. The libmongocrypt library files (.so/.dll) are pre-built on its [Evergreen project](https://spruce.mongodb.com/project/libmongocrypt/waterfall). Click the variant\'s \"built-and-test-and-upload\" tasks to download the attached files. libmongocrypt describes all API that needs to be called from your driver in the main public header [mongocrypt.h](https://github.com/mongodb/libmongocrypt/blob/master/src/mongocrypt.h). There are many types and functions in mongocrypt.h to bind. Consider as a first step binding to only `mongocrypt_version`. Once you have that working, proceed to write bindings for the remaining API. Here are a few things to keep in mind: - \"ctx\" is short for context, and is a generic term indicating that the object stores state. - By C convention, functions are named like: `mongocrypt__`. For example `mongocrypt_ctx_id` can be thought of as a class method \"id\" on the class \"ctx\". - `mongocrypt_binary_t` is a non-owning view of data. Calling `mongocrypt_binary_destroy` frees the view, but does nothing to the underlying data. When a `mongocrypt_binary_t` is returned (e.g. `mongocrypt_ctx_mongo_op`), the lifetime of the data is tied to the type that returned it (so the data returned will be freed when the `mongocrypt_ctx_t`) is freed. Once you have full bindings for the API, it\'s time to do a sanity check. The crux of libmongocrypt\'s API is the state machine represented by `mongocrypt_ctx_t`. This state machine is exercised in the [example-state-machine](https://github.com/mongodb/libmongocrypt/blob/master/test/example-state-machine.c) executable included with libmongocrypt. It uses mock responses from mongod, mongocryptd, and KMS. Reimplement the state machine loop (`_run_state_machine`) in example-state-machine with your binding. Seek help in the slack channel \#dbx-encryption. ## Part 2: Integrate into Driver ## After you have a binding, integrate libmongocrypt in your driver to support client side encryption. See the [driver spec](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md) for a reference of the user-facing API. libmongocrypt is needed for: - Automatic encryption/decryption (enabled with `AutoEncryptionOpts`) - ClientEncryption (explicit encryption/decryption + key management) It is recommended to start by integrating libmongocrypt to support automatic encryption/decryption. Then reuse the implementation to implement the ClientEncryption. A MongoClient enabled with client side encryption MUST have one shared `mongocrypt_t` handle (important because keys + JSON Schemas are cached in this handle). Each ClientEncryption also has its own `mongocrypt_t`. Any encryption or decryption operation is done by creating a `mongocrypt_ctx_t` and initializing it for the appropriate operation. `mongocrypt_ctx_t` is a state machine, and each state requires the driver to perform some action. This may be performing I/O on one of the following: - the encrypted MongoClient to which the operation is occurring (for auto encrypt). - the key vault MongoClient (which may be the same as the encrypted MongoClient). - KMS (via a TLS socket). - the MongoClient to the local mongocryptd process. ### Initializing ### Call one of the following on a `mongocrypt_ctx_t`: - auto encrypt (`mongocrypt_ctx_encrypt_init`) - auto decrypt (`mongocrypt_ctx_decrypt_init`) - explicit encrypt (`mongocrypt_ctx_explicit_encrypt_init`) - explicit decrypt (`mongocrypt_ctx_explicit_decrypt_init`) - create data key (`mongocrypt_ctx_datakey_init`) - rewrap data key (`mongocrypt_ctx_rewrap_many_datakey_init`) ### State Machine ### Below is a list of the various states a mongocrypt ctx can be in. For each state, there is a description of what the driver is expected to do to advance the state machine. Not all states will be entered for all types of contexts. But one state machine runner can be used for all types of contexts. #### State: `MONGOCRYPT_CTX_ERROR` #### **Driver needs to...** Throw an exception based on the status from `mongocrypt_ctx_status`. **Applies to...** All contexts. #### State: `MONGOCRYPT_CTX_NEED_MONGO_COLLINFO` #### > [!IMPORTANT] > **Multi-collection commands**: prior to 1.13.0, drivers were expected to pass _at most one result_ from `listCollections`. In 1.13.0, drivers are expected to pass _all results_ from `listCollections` to support multi-collection commands (e.g. aggregate with `$lookup`). > > Drivers must call `mongocrypt_setopt_enable_multiple_collinfo` to indicate the new behavior is implemented and opt-in to support for multi-collection commands. This opt-in is to prevent the following bug scenario: > > A driver upgrades to 1.13.0, but does not update prior behavior which passes at most one result of a multi-collection command. > > A multi-collection command requests schemas for both `db.c1` and `db.c2`. > > The driver only passes the result for `db.c1` even though `db.c2` also has a result. > > Therefore, libmongocrypt incorrectly believes `db.c2` has no schema. **libmongocrypt needs**... A result from a listCollections cursor. **Driver needs to...** 1. Run listCollections on the encrypted MongoClient with the filter provided by `mongocrypt_ctx_mongo_op` 2. Pass all results (if any) with calls to `mongocrypt_ctx_mongo_feed` or proceed to the next step if nothing was returned. Results may be passed in any order. 3. Call `mongocrypt_ctx_mongo_done` **Applies to...** auto encrypt #### State: `MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB` #### See [note](#multi-collection-commands) about multi-collection commands. **libmongocrypt needs**... Results from a listCollections cursor from a specified database. **Driver needs to...** 1. Run listCollections on the encrypted MongoClient with the filter provided by `mongocrypt_ctx_mongo_op` on the database provided by `mongocrypt_ctx_mongo_db`. 2. Pass all results (if any) with calls to `mongocrypt_ctx_mongo_feed` or proceed to the next step if nothing was returned. Results may be passed in any order. 3. Call `mongocrypt_ctx_mongo_done` **Applies to...** A context initialized with `mongocrypt_ctx_encrypt_init` for automatic encryption. This state is only entered when `mongocrypt_setopt_use_need_mongo_collinfo_with_db_state` is called to opt-in. #### State: `MONGOCRYPT_CTX_NEED_MONGO_MARKINGS` #### **libmongocrypt needs**... A reply from mongocryptd indicating which values in a command need to be encrypted. **Driver needs to...** 1. Use db.runCommand to run the command provided by `mongocrypt_ctx_mongo_op` on the MongoClient connected to mongocryptd. 2. Feed the reply back with `mongocrypt_ctx_mongo_feed`. 3. Call `mongocrypt_ctx_mongo_done`. **Applies to...** auto encrypt #### State: `MONGOCRYPT_CTX_NEED_MONGO_KEYS` #### **libmongocrypt needs**... Documents from the key vault collection. **Driver needs to...** 1. Use MongoCollection.find on the MongoClient connected to the key vault client (which may be the same as the encrypted client). Use the filter provided by `mongocrypt_ctx_mongo_op`. 2. Feed all resulting documents back (if any) with repeated calls to `mongocrypt_ctx_mongo_feed`. 3. Call `mongocrypt_ctx_mongo_done`. **Applies to...** All contexts except for create data key. #### State: `MONGOCRYPT_CTX_NEED_KMS` #### **libmongocrypt needs**... The responses from one or more messages to KMS. Ensure `mongocrypt_setopt_retry_kms` is called on the `mongocrypt_t` to enable retry. **Driver needs to...** 1. For each context returned by `mongocrypt_ctx_next_kms_ctx`: a. Delay the message by the time in microseconds indicated by `mongocrypt_kms_ctx_usleep` if returned value is greater than 0. b. Create/reuse a TLS socket connected to the endpoint indicated by `mongocrypt_kms_ctx_endpoint`. The endpoint string is a host name with a port number separated by a colon. E.g. "kms.us-east-1.amazonaws.com:443". A port number will always be included. Drivers may assume the host name is not an IP address or IP literal. c. Write the message from `mongocrypt_kms_ctx_message` to the > socket. d. Feed the reply back with `mongocrypt_kms_ctx_feed` or `mongocrypt_kms_ctx_feed_with_retry`. Repeat until `mongocrypt_kms_ctx_bytes_needed` returns 0. If the `should_retry` outparam returns true, the request may be retried by feeding the new response into the same context. If any step encounters a network error, call `mongocrypt_kms_ctx_fail`. If `mongocrypt_kms_ctx_fail` returns true, retry the request by continuing to the next KMS context or by feeding the new response into the same context. If `mongocrypt_kms_ctx_fail` returns false, abort and report an error. Consider wrapping the error reported in `mongocrypt_kms_ctx_status` to include the last network error. 2. When done feeding all replies, call `mongocrypt_ctx_kms_done`. ##### Retry and Iteration Call `mongocrypt_setopt_retry_kms` to enable retry behavior. There are two options for retry: - Lazy retry: After processing KMS contexts, iterate again by calling `mongocrypt_ctx_next_kms_ctx`. KMS contexts needing a retry will be returned. - In-place retry: If a KMS context indicates retry, retry the KMS request and feed the new response to the same KMS context. Use `mongocrypt_kms_ctx_feed_with_retry` and check the return of `mongocrypt_kms_ctx_fail` to check if a retry is indicated. The driver MAY fan out KMS requests in parallel. It is not safe to iterate KMS contexts (i.e. call `mongocrypt_ctx_next_kms_ctx`) while operating on KMS contexts (e.g. calling `mongocrypt_kms_ctx_feed`). Drivers are recommended to do an in-place retry on KMS requests. **Applies to...** All contexts. #### State: `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` #### `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` was added in libmongocrypt 1.4.0 as part of [MONGOCRYPT-382](https://jira.mongodb.org/browse/MONGOCRYPT-382). `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` can only be entered if `mongocrypt_setopt_use_need_kms_credentials_state` is called. This prevents breaking drivers that do not handle the `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` state. If a KMS provider is configured with an empty document (e.g. `{ "aws": {} }`), the `MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS` is entered before KMS requests are made. **libmongocrypt needs**... Credentials for one or more KMS providers. **Driver needs to...** Fetch credentials for supported KMS providers. See the [Client Side Encryption specification](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#automatic-credentials) for details. Pass credentials to libmongocrypt using `mongocrypt_ctx_provide_kms_providers`. **Applies to...** All contexts. #### State: `MONGOCRYPT_CTX_READY` #### **Driver needs to...** Call `mongocrypt_ctx_finalize` to perform the encryption/decryption and get the final result. **Applies to...** All contexts except for create data key. #### State: `MONGOCRYPT_CTX_DONE` #### **Driver needs to...** Exit the state machine loop. **Applies to...** All contexts. libmongocrypt-1.19.0/kms-message/000077500000000000000000000000001521103432300167005ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/.gitattributes000066400000000000000000000000151521103432300215670ustar00rootroot00000000000000*.bin binary libmongocrypt-1.19.0/kms-message/CMakeLists.txt000066400000000000000000000236561521103432300214540ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.15...4.0) project (kms_message VERSION 0.0.1 LANGUAGES C ) set (CMAKE_C_STANDARD 99) include (CheckCCompilerFlag) set (CMAKE_C_VISIBILITY_PRESET hidden) set (KMS_MESSAGE_SOURCES src/kms_b64.c src/kms_message/kms_b64.h src/hexlify.c src/hexlify.h src/kms_azure_request.c src/kms_crypto.h src/kms_crypto_none.c src/kms_crypto_windows.c src/kms_crypto_apple.c src/kms_crypto_libcrypto.c src/kms_caller_identity_request.c src/kms_decrypt_request.c src/kms_encrypt_request.c src/kms_gcp_request.c src/kms_kmip_reader_writer.c src/kms_kmip_request.c src/kms_kmip_response.c src/kms_kmip_response_parser.c src/kms_kv_list.c src/kms_kv_list.h src/kms_message.c src/kms_port.c src/kms_message/kms_azure_request.h src/kms_message/kms_caller_identity_request.h src/kms_message/kms_decrypt_request.h src/kms_message/kms_encrypt_request.h src/kms_message/kms_gcp_request.h src/kms_message/kms_message.h src/kms_message/kms_request.h src/kms_message/kms_request_opt.h src/kms_message/kms_response.h src/kms_message/kms_response_parser.h src/kms_request.c src/kms_request_opt.c src/kms_request_opt_private.h src/kms_request_str.c src/kms_request_str.h src/kms_response.c src/kms_response_parser.c src/sort.c ) if (DISABLE_NATIVE_CRYPTO) # Nothing elseif (WIN32) set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_ENABLE_CRYPTO" "KMS_MESSAGE_ENABLE_CRYPTO_CNG") elseif (APPLE) set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_ENABLE_CRYPTO" "KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO") else() set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_ENABLE_CRYPTO" "KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO") endif() include (CheckSymbolExists) CHECK_SYMBOL_EXISTS (gmtime_r time.h KMS_MESSAGE_HAVE_GMTIME_R) if (KMS_MESSAGE_HAVE_GMTIME_R) set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_HAVE_GMTIME_R") endif () include (TestBigEndian) TEST_BIG_ENDIAN (KMS_BIG_ENDIAN) if (KMS_BIG_ENDIAN) message ("Detected byte order: big endian") set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_BIG_ENDIAN") else () message ("Detected byte order: little endian") set (KMS_MESSAGE_DEFINITIONS ${KMS_MESSAGE_DEFINITIONS} "KMS_MESSAGE_LITTLE_ENDIAN") endif () add_library ( kms_message SHARED ${KMS_MESSAGE_SOURCES} ) add_library ( kms_message_static STATIC ${KMS_MESSAGE_SOURCES} ) string(FIND "${CMAKE_C_FLAGS}" "-fPIC" FPIC_LOCATION) if (NOT WIN32 AND ENABLE_PIC AND "${FPIC_LOCATION}" EQUAL "-1") set_property (TARGET kms_message_static PROPERTY POSITION_INDEPENDENT_CODE TRUE) message ("Adding -fPIC to compilation of kms_message_static components") endif () add_library ( kms_message_obj OBJECT ${KMS_MESSAGE_SOURCES} ) target_compile_definitions (kms_message_obj PRIVATE ${KMS_MESSAGE_DEFINITIONS}) if (NOT DISABLE_NATIVE_CRYPTO) if (WIN32) target_link_libraries(kms_message "bcrypt" "crypt32") target_link_libraries(kms_message_static "bcrypt" "crypt32") elseif (APPLE) target_link_libraries (kms_message "-framework Security -framework CoreFoundation") target_link_libraries (kms_message_static "-framework Security -framework CoreFoundation") else() include (FindOpenSSL) target_link_libraries(kms_message "${OPENSSL_LIBRARIES}") target_include_directories(kms_message SYSTEM PRIVATE "${OPENSSL_INCLUDE_DIR}") target_link_libraries(kms_message_static "${OPENSSL_LIBRARIES}") target_include_directories(kms_message_static SYSTEM PRIVATE "${OPENSSL_INCLUDE_DIR}") endif() endif () if ( CMAKE_COMPILER_IS_GNUCC ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-unused-function -Wsign-compare") endif() if ( MSVC ) # W4273 - inconsistent dll linkage # W4996 - POSIX name for this item is deprecated set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /wd4273 /wd4996 /D_CRT_SECURE_NO_WARNINGS") endif() set_target_properties (kms_message PROPERTIES SOVERSION 0 VERSION "0.0.0" OUTPUT_NAME "kms_message" ) set_target_properties (kms_message_static PROPERTIES SOVERSION 0 VERSION "0.0.0" OUTPUT_NAME "kms_message-static" ) target_compile_definitions (kms_message PRIVATE KMS_MSG_COMPILATION) target_compile_definitions (kms_message PRIVATE ${KMS_MESSAGE_DEFINITIONS}) set_property (TARGET kms_message APPEND PROPERTY COMPATIBLE_INTERFACE_STRING kms_message_MAJOR_VERSION ) target_compile_definitions (kms_message_static PRIVATE KMS_MSG_COMPILATION) target_compile_definitions (kms_message_static PRIVATE ${KMS_MESSAGE_DEFINITIONS}) target_compile_definitions (kms_message_static PUBLIC KMS_MSG_STATIC) set_property (TARGET kms_message_static APPEND PROPERTY COMPATIBLE_INTERFACE_STRING kms_message_MAJOR_VERSION ) include (CMakePackageConfigHelpers) if (ENABLE_STATIC) set (TARGETS_TO_INSTALL kms_message kms_message_static) else () set (TARGETS_TO_INSTALL kms_message) endif () install ( TARGETS ${TARGETS_TO_INSTALL} EXPORT kms_message_targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install ( FILES src/kms_message/kms_azure_request.h src/kms_message/kms_b64.h src/kms_message/kms_caller_identity_request.h src/kms_message/kms_decrypt_request.h src/kms_message/kms_encrypt_request.h src/kms_message/kms_gcp_request.h src/kms_message/kms_message.h src/kms_message/kms_message_defines.h src/kms_message/kms_kmip_request.h src/kms_message/kms_kmip_response.h src/kms_message/kms_kmip_response_parser.h src/kms_message/kms_request.h src/kms_message/kms_request_opt.h src/kms_message/kms_response.h src/kms_message/kms_response_parser.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kms_message COMPONENT Devel ) include (CMakePackageConfigHelpers) write_basic_package_version_file ( "${CMAKE_CURRENT_BINARY_DIR}/kms_message/kms_message-config-version.cmake" VERSION 0.0.1 COMPATIBILITY AnyNewerVersion ) export (EXPORT kms_message_targets NAMESPACE mongo:: FILE "${CMAKE_CURRENT_BINARY_DIR}/kms_message/kms_message_targets.cmake" ) configure_file (cmake/kms_message-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/kms_message/kms_message-config.cmake" COPYONLY ) set (ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/kms_message) install (EXPORT kms_message_targets NAMESPACE mongo:: FILE kms_message_targets.cmake DESTINATION ${ConfigPackageLocation} ) install ( FILES cmake/kms_message-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/kms_message/kms_message-config-version.cmake" DESTINATION ${ConfigPackageLocation} COMPONENT Devel ) # pkg-config. set (PKG_CONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") set (PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") set (PKG_CONFIG_LIBS "-L\${libdir} -lkms_message") set (PKG_CONFIG_CFLAGS "-I\${includedir}") configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libkms_message.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/libkms_message.pc" ) install ( FILES "${CMAKE_CURRENT_BINARY_DIR}/libkms_message.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) # cannot run tests without crypto if (NOT DISABLE_NATIVE_CRYPTO) add_executable ( test_kms_request ${KMS_MESSAGE_SOURCES} test/test_kms_request.c test/test_kmip_reader_writer.c test/test_kms_util.c test/test_kms_kmip_request.c test/test_kms_kmip_response_parser.c test/test_kms_kmip_response.c ) target_include_directories(test_kms_request PRIVATE ${PROJECT_SOURCE_DIR}/src) target_compile_definitions(test_kms_request PRIVATE ${KMS_MESSAGE_DEFINITIONS}) if (WIN32) target_link_libraries(test_kms_request "bcrypt" "crypt32") elseif (APPLE) target_link_libraries (test_kms_request "-framework Security -framework CoreFoundation") else() include (FindOpenSSL) target_link_libraries(test_kms_request "${OPENSSL_LIBRARIES}") target_include_directories(test_kms_request SYSTEM PRIVATE "${OPENSSL_INCLUDE_DIR}") endif() add_test ( NAME test_kms_request COMMAND test_kms_request WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) endif () # build online_tests if OpenSSL is available (to create TLS connections). if(DISABLE_NATIVE_CRYPTO) message ("test_kms_azure_online target disabled: Not building with native crypto.") elseif(ENABLE_ONLINE_TESTS) add_executable(test_kms_azure_online test/test_kms_azure_online.c test/test_kms_util.c test/test_kms_online_util.c) target_include_directories(test_kms_azure_online PRIVATE ${PROJECT_SOURCE_DIR}/src) target_compile_definitions(test_kms_azure_online PRIVATE ${KMS_MESSAGE_DEFINITIONS}) target_link_libraries(test_kms_azure_online _mongocrypt::mongoc) target_link_libraries(test_kms_azure_online kms_message_static) add_test ( NAME test_kms_azure_online COMMAND test_kms_azure_online WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) add_executable(test_kms_gcp_online test/test_kms_gcp_online.c test/test_kms_util.c test/test_kms_online_util.c) target_include_directories(test_kms_gcp_online PRIVATE ${PROJECT_SOURCE_DIR}/src) target_compile_definitions(test_kms_gcp_online PRIVATE ${KMS_MESSAGE_DEFINITIONS}) target_link_libraries(test_kms_gcp_online _mongocrypt::mongoc) target_link_libraries(test_kms_gcp_online kms_message_static) add_test ( NAME test_kms_gcp_online COMMAND test_kms_gcp_online WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) set_tests_properties ( test_kms_gcp_online test_kms_azure_online PROPERTIES SKIP_REGULAR_EXPRESSION "@@ctest-skip@@" # Older CMake (<3.16) does not support skipping via regex, so we'll use the # less-expressive skip-retcode. SKIP_RETURN_CODE 2 ) endif () libmongocrypt-1.19.0/kms-message/COPYING000066400000000000000000000236761521103432300177510ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS libmongocrypt-1.19.0/kms-message/README.md000066400000000000000000000041171521103432300201620ustar00rootroot00000000000000# kms-message Library used to generate requests for: - Amazon Web Services Key Management Service (KMS) - Azure Key Vault This library is *not* a complete implementation of a KMS client, it only implements the request format. ## Testing kms-message - `test_kms_request` tests HTTP request generation and response parsing, but does not require internet or use any live servers. - `test_kms_azure_online` makes live requests, and has additional requirements (must have working credentials). ### Requirements - A complete installation of the C driver. (libbson is needed for parsing JSON, and libmongoc is used for creating TLS streams). See the [C Driver Manual](https://www.mongodb.com/docs/languages/c/c-driver/current/libmongoc/tutorials/obtaining-libraries/) for installation instructions. For macOS, `brew install mongo-c-driver` will suffice. - An Azure key vault, and a service principal with an access policy allowing encrypt / decrypt key operations. The following environment variables must be set: - AZURE_TENANT_ID - AZURE_CLIENT_ID - AZURE_CLIENT_SECRET - AZURE_KEY_URL (e.g. `https://key-vault-kevinalbs.vault.azure.net/keys/test-key/9e1159e6ee5b447ba17e850b779bf652`) ### Building Configure and build with cmake: ``` mkdir cmake-build cd cmake-build cmake .. cmake --build . --target all ``` If the C driver is installed in a non-default location, specify the location with `-DCMAKE_PREFIX_PATH=...`. To build tests with verbose (and insecure) tracing, define `TEST_TRACING_INSECURE` in compiler flags by specifying `-DCMAKE_C_FLAGS="-DTEST_TRACING_INSECURE"` on cmake configuration. Recommended: compile tests with address sanitizer (use a relatively new gcc / clang compiler) by specifying `-fsanitize=address` in the C flags. This can be done by specifygin `-DCMAKE_C_FLAGS="-fsanitize=address"` as an option to cmake. Enable leak detection with the environment variable `ASAN_OPTIONS='detect_leaks=1'. Example: ``` cd cmake-build cmake -DCMAKE_C_FLAGS="-fsanitize=address -DTEST_TRACING_INSECURE" export ASAN_OPTIONS='detect_leaks=1' ./cmake-build/kms-message/test_kms_azure_online ``` libmongocrypt-1.19.0/kms-message/THIRD_PARTY_NOTICES000066400000000000000000000042751521103432300216500ustar00rootroot00000000000000License notice for kms_b64.c ------------------------------------------------------------------------------- ISC License Copyright: 1996, 1998 Internet Software Consortium 1995 International Business Machines, Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Portions Copyright (c) 1995 by International Business Machines, Inc. International Business Machines, Inc. (hereinafter called IBM) grants permission under its copyrights to use, copy, modify, and distribute this Software with or without fee, provided that the above copyright notice and all paragraphs of this notice appear in all copies, and that the name of IBM not be used in connection with the marketing of any product incorporating the Software or modifications thereof, without specific, written prior permission. To the extent it has a right to do so, IBM grants an immunity from suit under its patents, if any, for the use, sale or manufacture of products to the extent that such products are used for performing Domain Name System dynamic updates in TCP/IP networks by means of the Software. No immunity is granted for any product per se or for any other function of any product. THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/000077500000000000000000000000001521103432300227055ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/LICENSE000077500000000000000000000261361521103432300237250ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/NOTICE000077500000000000000000000001531521103432300236130ustar00rootroot00000000000000AWS Signature Version 4 Test Suite Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicate/000077500000000000000000000000001521103432300274505ustar00rootroot00000000000000get-header-key-duplicate.authz000077500000000000000000000003051521103432300352120ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicateAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138eaget-header-key-duplicate.creq000077500000000000000000000002721521103432300350140ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicateGET / host:example.amazonaws.com my-header1:value2,value2,value1 x-amz-date:20150830T123600Z host;my-header1;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-header-key-duplicate.req000077500000000000000000000001731521103432300346510ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicateGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value2 My-Header1:value2 My-Header1:value1 X-Amz-Date:20150830T123600Zget-header-key-duplicate.sreq000077500000000000000000000005201521103432300350300ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicateGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value2 My-Header1:value2 My-Header1:value1 X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138eaget-header-key-duplicate.sts000077500000000000000000000002121521103432300346650ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-key-duplicateAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966clibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multiline/000077500000000000000000000000001521103432300300245ustar00rootroot00000000000000get-header-value-multiline.authz000077500000000000000000000003051521103432300361420ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multilineAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790get-header-value-multiline.creq000077500000000000000000000002721521103432300357440ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multilineGET / host:example.amazonaws.com my-header1:value1,value2,value3 x-amz-date:20150830T123600Z host;my-header1;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-header-value-multiline.req000077500000000000000000000001541521103432300356000ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multilineGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value1 value2 value3 X-Amz-Date:20150830T123600Zget-header-value-multiline.sreq000077500000000000000000000005011521103432300357570ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multilineGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value1 value2 value3 X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790get-header-value-multiline.sts000077500000000000000000000002121521103432300356150ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-multilineAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request b7b6cbfd8a0430b78891e986784da2630c8a135a8595cec25b26ea94f926ee55libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-order/000077500000000000000000000000001521103432300271355ustar00rootroot00000000000000get-header-value-order.authz000077500000000000000000000003051521103432300343640ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-orderAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01get-header-value-order.creq000077500000000000000000000003011521103432300341570ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-orderGET / host:example.amazonaws.com my-header1:value4,value1,value3,value2 x-amz-date:20150830T123600Z host;my-header1;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-header-value-order.req000077500000000000000000000002151521103432300340200ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-orderGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value4 My-Header1:value1 My-Header1:value3 My-Header1:value2 X-Amz-Date:20150830T123600Zget-header-value-order.sreq000077500000000000000000000005421521103432300342060ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-orderGET / HTTP/1.1 Host:example.amazonaws.com My-Header1:value4 My-Header1:value1 My-Header1:value3 My-Header1:value2 X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01get-header-value-order.sts000077500000000000000000000002121521103432300340370ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-orderAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trim/000077500000000000000000000000001521103432300267755ustar00rootroot00000000000000get-header-value-trim.authz000077500000000000000000000003201521103432300340610ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trimAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736get-header-value-trim.creq000077500000000000000000000003121521103432300336610ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trimGET / host:example.amazonaws.com my-header1:value1 my-header2:"a b c" x-amz-date:20150830T123600Z host;my-header1;my-header2;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-header-value-trim.req000077500000000000000000000001601521103432300335170ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trimGET / HTTP/1.1 Host:example.amazonaws.com My-Header1: value1 My-Header2: "a b c" X-Amz-Date:20150830T123600Zget-header-value-trim.sreq000077500000000000000000000005201521103432300337020ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trimGET / HTTP/1.1 Host:example.amazonaws.com My-Header1: value1 My-Header2: "a b c" X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736get-header-value-trim.sts000077500000000000000000000002121521103432300335370ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-header-value-trimAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/000077500000000000000000000000001521103432300256445ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz000077500000000000000000000002721521103432300316640ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24flibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq000077500000000000000000000003211521103432300314560ustar00rootroot00000000000000GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req000077500000000000000000000002071521103432300313160ustar00rootroot00000000000000GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq000077500000000000000000000005211521103432300315000ustar00rootroot00000000000000GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24flibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts000077500000000000000000000002121521103432300313340ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/000077500000000000000000000000001521103432300243505ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/get-utf8.authz000077500000000000000000000002721521103432300270740ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/get-utf8.creq000077500000000000000000000002301521103432300266650ustar00rootroot00000000000000GET /%E1%88%B4 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/get-utf8.req000077500000000000000000000001101521103432300265170ustar00rootroot00000000000000GET /ሴ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq000077500000000000000000000004221521103432300267100ustar00rootroot00000000000000GET /ሴ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-utf8/get-utf8.sts000077500000000000000000000002121521103432300265440ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-key/000077500000000000000000000000001521103432300301755ustar00rootroot00000000000000get-vanilla-empty-query-key.authz000077500000000000000000000002721521103432300364670ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-keyAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aebget-vanilla-empty-query-key.creq000077500000000000000000000002341521103432300362640ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-keyGET / Param1=value1 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-empty-query-key.req000077500000000000000000000001231521103432300361160ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-keyGET /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-empty-query-key.sreq000077500000000000000000000004351521103432300363070ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-keyGET /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aebget-vanilla-empty-query-key.sts000077500000000000000000000002121521103432300361370ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-empty-query-keyAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ablibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/000077500000000000000000000000001521103432300310635ustar00rootroot00000000000000get-vanilla-query-order-key-case.authz000077500000000000000000000002721521103432300402430ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-caseAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500get-vanilla-query-order-key-case.creq000077500000000000000000000002521521103432300400400ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-caseGET / Param1=value1&Param2=value2 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-query-order-key-case.req000077500000000000000000000001411521103432300376720ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-caseGET /?Param2=value2&Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-query-order-key-case.sreq000077500000000000000000000004531521103432300400630ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-caseGET /?Param2=value2&Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500get-vanilla-query-order-key-case.sts000077500000000000000000000002121521103432300377130ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key-caseAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-key/000077500000000000000000000000001521103432300301525ustar00rootroot00000000000000get-vanilla-query-order-key.authz000077500000000000000000000002721521103432300364210ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-keyAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1get-vanilla-query-order-key.creq000077500000000000000000000002521521103432300362160ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-keyGET / Param1=Value1&Param1=value2 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-query-order-key.req000077500000000000000000000001411521103432300360500ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-keyGET /?Param1=value2&Param1=Value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-query-order-key.sreq000077500000000000000000000004531521103432300362410ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-keyGET /?Param1=value2&Param1=Value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1get-vanilla-query-order-key.sts000077500000000000000000000002121521103432300360710ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-keyAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-value/000077500000000000000000000000001521103432300304765ustar00rootroot00000000000000get-vanilla-query-order-value.authz000077500000000000000000000002721521103432300372710ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-valueAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694get-vanilla-query-order-value.creq000077500000000000000000000002521521103432300370660ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-valueGET / Param1=value1&Param1=value2 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-query-order-value.req000077500000000000000000000001411521103432300367200ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-valueGET /?Param1=value2&Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-query-order-value.sreq000077500000000000000000000004531521103432300371110ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-valueGET /?Param1=value2&Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694get-vanilla-query-order-value.sts000077500000000000000000000002121521103432300367410ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-order-valueAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreserved/000077500000000000000000000000001521103432300304335ustar00rootroot00000000000000get-vanilla-query-unreserved.authz000077500000000000000000000002721521103432300371630ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreservedAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197get-vanilla-query-unreserved.creq000077500000000000000000000004241521103432300367610ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreservedGET / -._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-query-unreserved.req000077500000000000000000000003131521103432300366130ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreservedGET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-query-unreserved.sreq000077500000000000000000000006251521103432300370040ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreservedGET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197get-vanilla-query-unreserved.sts000077500000000000000000000002121521103432300366330ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query-unreservedAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/000077500000000000000000000000001521103432300262535ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz000077500000000000000000000002721521103432300327020ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq000077500000000000000000000002171521103432300325000ustar00rootroot00000000000000GET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req000077500000000000000000000001051521103432300323310ustar00rootroot00000000000000GET / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq000077500000000000000000000004171521103432300325220ustar00rootroot00000000000000GET / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts000077500000000000000000000002121521103432300323520ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-query/000077500000000000000000000000001521103432300271375ustar00rootroot00000000000000get-vanilla-utf8-query.authz000077500000000000000000000002721521103432300343730ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-queryAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04get-vanilla-utf8-query.creq000077500000000000000000000002341521103432300341700ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-queryGET / %E1%88%B4=bar host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-vanilla-utf8-query.req000077500000000000000000000001151521103432300340230ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-queryGET /?ሴ=bar HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-vanilla-utf8-query.sreq000077500000000000000000000004271521103432300342140ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-queryGET /?ሴ=bar HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04get-vanilla-utf8-query.sts000077500000000000000000000002121521103432300340430ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla-utf8-queryAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/000077500000000000000000000000001521103432300251105ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz000077500000000000000000000002721521103432300303740ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq000077500000000000000000000002171521103432300301720ustar00rootroot00000000000000GET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req000077500000000000000000000001051521103432300300230ustar00rootroot00000000000000GET / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq000077500000000000000000000004171521103432300302140ustar00rootroot00000000000000GET / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts000077500000000000000000000002121521103432300300440ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/000077500000000000000000000000001521103432300256375ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relative/000077500000000000000000000000001521103432300320405ustar00rootroot00000000000000get-relative-relative.authz000077500000000000000000000002721521103432300372430ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relativeAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31get-relative-relative.creq000077500000000000000000000002171521103432300370410ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relativeGET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-relative-relative.req000077500000000000000000000001341521103432300366740ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relativeGET /example1/example2/../.. HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-relative-relative.sreq000077500000000000000000000004461521103432300370650ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relativeGET /example1/example2/../.. HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31get-relative-relative.sts000077500000000000000000000002121521103432300367130ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative-relativeAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative/000077500000000000000000000000001521103432300302275ustar00rootroot00000000000000get-relative.authz000077500000000000000000000002721521103432300336210ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relativeAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq000077500000000000000000000002171521103432300334760ustar00rootroot00000000000000GET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req000077500000000000000000000001171521103432300333320ustar00rootroot00000000000000GET /example/.. HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq000077500000000000000000000004311521103432300335140ustar00rootroot00000000000000GET /example/.. HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts000077500000000000000000000002121521103432300333500ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/000077500000000000000000000000001521103432300314225ustar00rootroot00000000000000get-slash-dot-slash.authz000077500000000000000000000002721521103432300362070ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slashAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31get-slash-dot-slash.creq000077500000000000000000000002171521103432300360050ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slashGET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-slash-dot-slash.req000077500000000000000000000001071521103432300356400ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slashGET /./ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-slash-dot-slash.sreq000077500000000000000000000004211521103432300360220ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slashGET /./ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31get-slash-dot-slash.sts000077500000000000000000000002121521103432300356570ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slashAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/000077500000000000000000000000001521103432300323305ustar00rootroot00000000000000get-slash-pointless-dot.authz000077500000000000000000000002721521103432300400230ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dotAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5get-slash-pointless-dot.creq000077500000000000000000000002261521103432300376210ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dotGET /example host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855get-slash-pointless-dot.req000077500000000000000000000001161521103432300374540ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dotGET /./example HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zget-slash-pointless-dot.sreq000077500000000000000000000004301521103432300376360ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dotGET /./example HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5get-slash-pointless-dot.sts000077500000000000000000000002121521103432300374730ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dotAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/000077500000000000000000000000001521103432300275265ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz000077500000000000000000000002721521103432300324760ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq000077500000000000000000000002171521103432300322740ustar00rootroot00000000000000GET / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req000077500000000000000000000001061521103432300321260ustar00rootroot00000000000000GET // HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq000077500000000000000000000004201521103432300323100ustar00rootroot00000000000000GET // HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts000077500000000000000000000002121521103432300321460ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/000077500000000000000000000000001521103432300300565ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz000077500000000000000000000002721521103432300333560ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq000077500000000000000000000002271521103432300331550ustar00rootroot00000000000000GET /example/ host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req000077500000000000000000000001171521103432300330100ustar00rootroot00000000000000GET //example// HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq000077500000000000000000000004311521103432300331720ustar00rootroot00000000000000GET //example// HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts000077500000000000000000000002121521103432300330260ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/000077500000000000000000000000001521103432300275075ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz000077500000000000000000000002721521103432300324400ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq000077500000000000000000000002371521103432300322400ustar00rootroot00000000000000GET /example%20space/ host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req000077500000000000000000000001231521103432300320670ustar00rootroot00000000000000GET /example space/ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq000077500000000000000000000004351521103432300322600ustar00rootroot00000000000000GET /example space/ HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts000077500000000000000000000002121521103432300321100ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/normalize-path/normalize-path.txt000077500000000000000000000010071521103432300313330ustar00rootroot00000000000000A note about signing requests to Amazon S3: In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-requestlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-case/000077500000000000000000000000001521103432300266175ustar00rootroot00000000000000post-header-key-case.authz000077500000000000000000000002721521103432300335330ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-caseAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6bpost-header-key-case.creq000077500000000000000000000002201521103432300333230ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-casePOST / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req000077500000000000000000000001061521103432300332420ustar00rootroot00000000000000POST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zpost-header-key-case.sreq000077500000000000000000000004201521103432300333450ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-casePOST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6blibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts000077500000000000000000000002121521103432300332620ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sort/000077500000000000000000000000001521103432300266735ustar00rootroot00000000000000post-header-key-sort.authz000077500000000000000000000003051521103432300336600ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sortAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852cpost-header-key-sort.creq000077500000000000000000000002551521103432300334630ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sortPOST / host:example.amazonaws.com my-header1:value1 x-amz-date:20150830T123600Z host;my-header1;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req000077500000000000000000000001301521103432300333670ustar00rootroot00000000000000POST / HTTP/1.1 Host:example.amazonaws.com My-Header1:value1 X-Amz-Date:20150830T123600Zpost-header-key-sort.sreq000077500000000000000000000004551521103432300335050ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sortPOST / HTTP/1.1 Host:example.amazonaws.com My-Header1:value1 X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852clibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts000077500000000000000000000002121521103432300334120ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-case/000077500000000000000000000000001521103432300271435ustar00rootroot00000000000000post-header-value-case.authz000077500000000000000000000003051521103432300344000ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-caseAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7dpost-header-value-case.creq000077500000000000000000000002551521103432300342030ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-casePOST / host:example.amazonaws.com my-header1:VALUE1 x-amz-date:20150830T123600Z host;my-header1;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855post-header-value-case.req000077500000000000000000000001301521103432300340300ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-casePOST / HTTP/1.1 Host:example.amazonaws.com My-Header1:VALUE1 X-Amz-Date:20150830T123600Zpost-header-value-case.sreq000077500000000000000000000004551521103432300342250ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-casePOST / HTTP/1.1 Host:example.amazonaws.com My-Header1:VALUE1 X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7dpost-header-value-case.sts000077500000000000000000000002121521103432300340530ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-header-value-caseAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/000077500000000000000000000000001521103432300256175ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/000077500000000000000000000000001521103432300317405ustar00rootroot00000000000000post-sts-header-after.authz000077500000000000000000000002721521103432300370630ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-afterAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6bpost-sts-header-after.creq000077500000000000000000000002201521103432300366530ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-afterPOST / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855post-sts-header-after.req000077500000000000000000000001061521103432300365130ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-afterPOST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zpost-sts-header-after.sreq000077500000000000000000000011661521103432300367050ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-afterPOST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6bpost-sts-header-after.sts000077500000000000000000000002121521103432300365330ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-afterAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/000077500000000000000000000000001521103432300321015ustar00rootroot00000000000000post-sts-header-before.authz000077500000000000000000000003171521103432300373650ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-beforeAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05eadpost-sts-header-before.creq000077500000000000000000000010131521103432300371560ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-beforePOST / host:example.amazonaws.com x-amz-date:20150830T123600Z x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== host;x-amz-date;x-amz-security-token e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855post-sts-header-before.req000077500000000000000000000006541521103432300370250ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-beforePOST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==post-sts-header-before.sreq000077500000000000000000000012131521103432300372000ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-beforePOST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05eadpost-sts-header-before.sts000077500000000000000000000002121521103432300370350ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/post-sts-header-beforeAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-sts-token/readme.txt000077500000000000000000000026361521103432300276270ustar00rootroot00000000000000A note about using temporary security credentials: You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials). When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service. The test suite has 2 examples: post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request. post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature. The test suite uses this example value for X-Amz-Security-Token: AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-value/000077500000000000000000000000001521103432300307275ustar00rootroot00000000000000post-vanilla-empty-query-value.authz000077500000000000000000000002721521103432300377530ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-valueAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11post-vanilla-empty-query-value.creq000077500000000000000000000002351521103432300375510ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-valuePOST / Param1=value1 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855post-vanilla-empty-query-value.req000077500000000000000000000001241521103432300374030ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-valuePOST /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zpost-vanilla-empty-query-value.sreq000077500000000000000000000004361521103432300375740ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-valuePOST /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11post-vanilla-empty-query-value.sts000077500000000000000000000002121521103432300374230ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-empty-query-valueAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbdlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/000077500000000000000000000000001521103432300264615ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz000077500000000000000000000002721521103432300333160ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq000077500000000000000000000002351521103432300331140ustar00rootroot00000000000000POST / Param1=value1 host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req000077500000000000000000000001241521103432300327460ustar00rootroot00000000000000POST /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq000077500000000000000000000004361521103432300331370ustar00rootroot00000000000000POST /?Param1=value1 HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts000077500000000000000000000002121521103432300327660ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbdlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/000077500000000000000000000000001521103432300253165ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz000077500000000000000000000002721521103432300310100ustar00rootroot00000000000000AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6blibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq000077500000000000000000000002201521103432300306000ustar00rootroot00000000000000POST / host:example.amazonaws.com x-amz-date:20150830T123600Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req000077500000000000000000000001061521103432300304400ustar00rootroot00000000000000POST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Zlibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq000077500000000000000000000004201521103432300306220ustar00rootroot00000000000000POST / HTTP/1.1 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6blibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts000077500000000000000000000002121521103432300304600ustar00rootroot00000000000000AWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/000077500000000000000000000000001521103432300322055ustar00rootroot00000000000000post-x-www-form-urlencoded-parameters.authz000077500000000000000000000003071521103432300425060ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parametersAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fepost-x-www-form-urlencoded-parameters.creq000077500000000000000000000003321521103432300423030ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parametersPOST / content-type:application/x-www-form-urlencoded; charset=utf8 host:example.amazonaws.com x-amz-date:20150830T123600Z content-type;host;x-amz-date 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6epost-x-www-form-urlencoded-parameters.req000077500000000000000000000002221521103432300421360ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parametersPOST / HTTP/1.1 Content-Type:application/x-www-form-urlencoded; charset=utf8 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Param1=value1post-x-www-form-urlencoded-parameters.sreq000077500000000000000000000005511521103432300423260ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parametersPOST / HTTP/1.1 Content-Type:application/x-www-form-urlencoded; charset=utf8 Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe Param1=value1post-x-www-form-urlencoded-parameters.sts000077500000000000000000000002121521103432300421570ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parametersAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 2e1cf7ed91881a30569e46552437e4156c823447bf1781b921b5d486c568dd1clibmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencoded/000077500000000000000000000000001521103432300300445ustar00rootroot00000000000000post-x-www-form-urlencoded.authz000077500000000000000000000003071521103432300362040ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencodedAWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473apost-x-www-form-urlencoded.creq000077500000000000000000000003141521103432300360010ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencodedPOST / content-type:application/x-www-form-urlencoded host:example.amazonaws.com x-amz-date:20150830T123600Z content-type;host;x-amz-date 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6epost-x-www-form-urlencoded.req000077500000000000000000000002041521103432300356340ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencodedPOST / HTTP/1.1 Content-Type:application/x-www-form-urlencoded Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Param1=value1post-x-www-form-urlencoded.sreq000077500000000000000000000005331521103432300360240ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencodedPOST / HTTP/1.1 Content-Type:application/x-www-form-urlencoded Host:example.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a Param1=value1post-x-www-form-urlencoded.sts000077500000000000000000000002121521103432300356550ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/aws-sig-v4-test-suite/post-x-www-form-urlencodedAWS4-HMAC-SHA256 20150830T123600Z 20150830/us-east-1/service/aws4_request 42a5e5bb34198acb3e84da4f085bb7927f2bc277ca766e6d19c73c2154021281libmongocrypt-1.19.0/kms-message/cmake/000077500000000000000000000000001521103432300177605ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/cmake/kms_message-config.cmake000066400000000000000000000000771521103432300245270ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/kms_message_targets.cmake") libmongocrypt-1.19.0/kms-message/cmake/libkms_message.pc.in000066400000000000000000000004141521103432300236750ustar00rootroot00000000000000Name: ${PROJECT_NAME} Description: ${PROJECT_DESCRIPTION} Version: ${PROJECT_VERSION} Requires: ${PKG_CONFIG_REQUIRES} prefix=${CMAKE_INSTALL_PREFIX} includedir=${PKG_CONFIG_INCLUDEDIR} libdir=${PKG_CONFIG_LIBDIR} Libs: ${PKG_CONFIG_LIBS} Cflags: ${PKG_CONFIG_CFLAGS} libmongocrypt-1.19.0/kms-message/src/000077500000000000000000000000001521103432300174675ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/src/hexlify.c000066400000000000000000000032211521103432300213010ustar00rootroot00000000000000/* * Copyright 2018-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 "hexlify.h" // #include "kms_message_private.h" #include #include #include #include #include char * hexlify (const uint8_t *buf, size_t len) { char *hex_chars = malloc (len * 2 + 1); KMS_ASSERT (hex_chars); char *p = hex_chars; size_t i; for (i = 0; i < len; i++) { KMS_ASSERT (2 == snprintf (p, 3, "%02x", buf[i])); p += 2; } *p = '\0'; return hex_chars; } /* Returns -1 on error. */ int unhexlify (const char *in, size_t len) { int i; int byte; int64_t total = 0; int64_t multiplier = 1; for (i = (int) len - 1; i >= 0; i--) { char c = *(in + i); if (c >= '0' && c <= '9') { byte = c - 48; } else if (c >= 'a' && c <= 'f') { byte = c - 97 + 10; } else if (c >= 'A' && c <= 'F') { byte = c - 65 + 10; } else { return -1; } total += (int64_t) byte * multiplier; if (total > INT_MAX) { return -1; } multiplier *= 16; } return (int) total; } libmongocrypt-1.19.0/kms-message/src/hexlify.h000066400000000000000000000013431521103432300213110ustar00rootroot00000000000000/* * Copyright 2018-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 #include char * hexlify (const uint8_t *buf, size_t len); int unhexlify (const char *in, size_t len); libmongocrypt-1.19.0/kms-message/src/kms_azure_request.c000066400000000000000000000155031521103432300234070ustar00rootroot00000000000000/* * Copyright 2020-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 "kms_message/kms_azure_request.h" #include "kms_message/kms_b64.h" #include "kms_message_private.h" #include "kms_request_opt_private.h" #include "kms_request_str.h" /* * Request has the following form: * * POST /{tenant ID}/oauth2/v2.0/token HTTP/1.1 * Host: {host of identify platform URL} * Content-Type: application/x-www-form-urlencoded * * client_id={client ID} * &scope=https%3A%2F%2Fvault.azure.net%2F.default * &client_secret={client secret} * &grant_type=client_credentials */ kms_request_t * kms_azure_request_oauth_new (const char *host, const char *scope, const char *tenant_id, const char *client_id, const char *client_secret, const kms_request_opt_t *opt) { char *path_and_query = NULL; char *payload = NULL; kms_request_t *req; kms_request_str_t *str; str = kms_request_str_new (); kms_request_str_appendf (str, "/%s/oauth2/v2.0/token", tenant_id); path_and_query = kms_request_str_detach (str); str = kms_request_str_new (); kms_request_str_appendf ( str, "client_id=%s&scope=%s&client_secret=%s&grant_type=client_credentials", client_id, scope, client_secret); payload = kms_request_str_detach (str); req = kms_request_new ("POST", path_and_query, opt); if (opt->provider != KMS_REQUEST_PROVIDER_AZURE) { KMS_ERROR (req, "Expected KMS request with provider type: Azure"); goto done; } if (kms_request_get_error (req)) { goto done; } if (!kms_request_add_header_field ( req, "Content-Type", "application/x-www-form-urlencoded")) { goto done; } if (!kms_request_add_header_field (req, "Host", host)) { goto done; } if (!kms_request_add_header_field (req, "Accept", "application/json")) { goto done; } if (!kms_request_append_payload (req, payload, strlen (payload))) { goto done; } done: kms_request_free_string (path_and_query); kms_request_free_string (payload); return req; } static kms_request_t * _wrap_unwrap_common (const char *wrap_unwrap, const char *host, const char *access_token, const char *key_name, const char *key_version, const uint8_t *value, size_t value_len, const kms_request_opt_t *opt) { char *path_and_query = NULL; char *payload = NULL; char *bearer_token_value = NULL; char *value_base64url = NULL; kms_request_t *req; kms_request_str_t *str; str = kms_request_str_new (); /* {vaultBaseUrl}/keys/{key-name}/{key-version}/wrapkey?api-version=7.1 */ kms_request_str_appendf (str, "/keys/%s/%s/%s?api-version=7.1", key_name, key_version ? key_version : "", wrap_unwrap); path_and_query = kms_request_str_detach (str); req = kms_request_new ("POST", path_and_query, opt); if (opt->provider != KMS_REQUEST_PROVIDER_AZURE) { KMS_ERROR (req, "Expected KMS request with provider type: Azure"); goto done; } if (kms_request_get_error (req)) { goto done; } value_base64url = kms_message_raw_to_b64url (value, value_len); if (!value_base64url) { KMS_ERROR (req, "Could not bases64url-encode plaintext"); goto done; } str = kms_request_str_new (); kms_request_str_appendf ( str, "{\"alg\": \"RSA-OAEP-256\", \"value\": \"%s\"}", value_base64url); payload = kms_request_str_detach (str); str = kms_request_str_new (); kms_request_str_appendf (str, "Bearer %s", access_token); bearer_token_value = kms_request_str_detach (str); if (!kms_request_add_header_field ( req, "Authorization", bearer_token_value)) { goto done; } if (!kms_request_add_header_field ( req, "Content-Type", "application/json")) { goto done; } if (!kms_request_add_header_field (req, "Host", host)) { goto done; } if (!kms_request_add_header_field (req, "Accept", "application/json")) { goto done; } if (!kms_request_append_payload (req, payload, strlen (payload))) { goto done; } done: kms_request_free_string (path_and_query); kms_request_free_string (payload); kms_request_free_string (bearer_token_value); kms_request_free_string (value_base64url); return req; } /* * Request has the following form: * * POST /keys/{key-name}/{key-version}/wrapkey?api-version=7.1 * Host: {host of key vault endpoint} * Authentication: Bearer {token} * Content-Type: application/json * * { * "alg": "RSA-OAEP-256" * "value": "base64url encoded data" * } */ kms_request_t * kms_azure_request_wrapkey_new (const char *host, const char *access_token, const char *key_name, const char *key_version, const uint8_t *plaintext, size_t plaintext_len, const kms_request_opt_t *opt) { return _wrap_unwrap_common ("wrapkey", host, access_token, key_name, key_version, plaintext, plaintext_len, opt); } kms_request_t * kms_azure_request_unwrapkey_new (const char *host, const char *access_token, const char *key_name, const char *key_version, const uint8_t *ciphertext, size_t ciphertext_len, const kms_request_opt_t *opt) { return _wrap_unwrap_common ("unwrapkey", host, access_token, key_name, key_version, ciphertext, ciphertext_len, opt); } libmongocrypt-1.19.0/kms-message/src/kms_b64.c000066400000000000000000000475221521103432300211120ustar00rootroot00000000000000/* * Copyright (c) 1996, 1998 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include "kms_message/kms_b64.h" #include "kms_message/kms_message.h" #include "kms_message_private.h" static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) * The following encoding technique is taken from RFC 1521 by Borenstein * and Freed. It is reproduced here in a slightly edited form for * convenience. * * A 65-character subset of US-ASCII is used, enabling 6 bits to be * represented per printable character. (The extra 65th character, "=", * is used to signify a special processing function.) * * The encoding process represents 24-bit groups of input bits as output * strings of 4 encoded characters. Proceeding from left to right, a * 24-bit input group is formed by concatenating 3 8-bit input groups. * These 24 bits are then treated as 4 concatenated 6-bit groups, each * of which is translated into a single digit in the base64 alphabet. * * Each 6-bit group is used as an index into an array of 64 printable * characters. The character referenced by the index is placed in the * output string. * * Table 1: The Base64 Alphabet * * Value Encoding Value Encoding Value Encoding Value Encoding * 0 A 17 R 34 i 51 z * 1 B 18 S 35 j 52 0 * 2 C 19 T 36 k 53 1 * 3 D 20 U 37 l 54 2 * 4 E 21 V 38 m 55 3 * 5 F 22 W 39 n 56 4 * 6 G 23 X 40 o 57 5 * 7 H 24 Y 41 p 58 6 * 8 I 25 Z 42 q 59 7 * 9 J 26 a 43 r 60 8 * 10 K 27 b 44 s 61 9 * 11 L 28 c 45 t 62 + * 12 M 29 d 46 u 63 / * 13 N 30 e 47 v * 14 O 31 f 48 w (pad) = * 15 P 32 g 49 x * 16 Q 33 h 50 y * * Special processing is performed if fewer than 24 bits are available * at the end of the data being encoded. A full encoding quantum is * always completed at the end of a quantity. When fewer than 24 input * bits are available in an input group, zero bits are added (on the * right) to form an integral number of 6-bit groups. Padding at the * end of the data is performed using the '=' character. * * Since all base64 input is an integral number of octets, only the * following cases can arise: * * (1) the final quantum of encoding input is an integral * multiple of 24 bits; here, the final unit of encoded * output will be an integral multiple of 4 characters * with no "=" padding, * (2) the final quantum of encoding input is exactly 8 bits; * here, the final unit of encoded output will be two * characters followed by two "=" padding characters, or * (3) the final quantum of encoding input is exactly 16 bits; * here, the final unit of encoded output will be three * characters followed by one "=" padding character. */ int kms_message_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; size_t i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = (uint8_t) (((input[0] & 0x03) << 4) + (input[1] >> 4)); output[2] = (uint8_t) (((input[1] & 0x0f) << 2) + (input[2] >> 6)); output[3] = input[2] & 0x3f; KMS_ASSERT (output[0] < 64); KMS_ASSERT (output[1] < 64); KMS_ASSERT (output[2] < 64); KMS_ASSERT (output[3] < 64); if (datalength + 4 > targsize) { return -1; } target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) { input[i] = *src++; } output[0] = input[0] >> 2; output[1] = (uint8_t) (((input[0] & 0x03) << 4) + (input[1] >> 4)); output[2] = (uint8_t) (((input[1] & 0x0f) << 2) + (input[2] >> 6)); KMS_ASSERT (output[0] < 64); KMS_ASSERT (output[1] < 64); KMS_ASSERT (output[2] < 64); if (datalength + 4 > targsize) { return -1; } target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength == 1) { target[datalength++] = Pad64; } else { target[datalength++] = Base64[output[2]]; } target[datalength++] = Pad64; } if (datalength >= targsize) { return -1; } target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (int) datalength; } /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ static uint8_t b64rmap[256]; static const uint8_t b64rmap_special = 0xf0; static const uint8_t b64rmap_end = 0xfd; static const uint8_t b64rmap_space = 0xfe; static const uint8_t b64rmap_invalid = 0xff; void kms_message_b64_initialize_rmap (void) { uint16_t i; unsigned char ch; /* Null: end of string, stop parsing */ b64rmap[0] = b64rmap_end; for (i = 1; i < 256; ++i) { ch = (unsigned char) i; /* Whitespaces */ if (isspace (ch)) b64rmap[i] = b64rmap_space; /* Padding: stop parsing */ else if (ch == Pad64) b64rmap[i] = b64rmap_end; /* Non-base64 char */ else b64rmap[i] = b64rmap_invalid; } /* Fill reverse mapping for base64 chars */ for (i = 0; Base64[i] != '\0'; ++i) b64rmap[(uint8_t) Base64[i]] = (uint8_t) i; } static int b64_pton_do (char const *src, uint8_t *target, size_t targsize) { int tarindex, state, ch; uint8_t ofs; state = 0; tarindex = 0; while (1) { ch = *src++; ofs = b64rmap[ch]; if (ofs >= b64rmap_special) { /* Ignore whitespaces */ if (ofs == b64rmap_space) continue; /* End of base64 characters */ if (ofs == b64rmap_end) break; /* A non-base64 character. */ return (-1); } switch (state) { case 0: if ((size_t) tarindex >= targsize) return (-1); target[tarindex] = (uint8_t) (ofs << 2); state = 1; break; case 1: if ((size_t) tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 4; target[tarindex + 1] = (uint8_t) ((ofs & 0x0f) << 4); tarindex++; state = 2; break; case 2: if ((size_t) tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 2; target[tarindex + 1] = (uint8_t) ((ofs & 0x03) << 6); tarindex++; state = 3; break; case 3: if ((size_t) tarindex >= targsize) return (-1); target[tarindex] |= ofs; tarindex++; state = 0; break; default: abort (); } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for ((void) NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for ((void) NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) return (-1); /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target[tarindex] != 0) return (-1); default: break; } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } static int b64_pton_len (char const *src) { int tarindex, state, ch; uint8_t ofs; state = 0; tarindex = 0; while (1) { ch = *src++; ofs = b64rmap[ch]; if (ofs >= b64rmap_special) { /* Ignore whitespaces */ if (ofs == b64rmap_space) continue; /* End of base64 characters */ if (ofs == b64rmap_end) break; /* A non-base64 character. */ return (-1); } switch (state) { case 0: state = 1; break; case 1: tarindex++; state = 2; break; case 2: tarindex++; state = 3; break; case 3: tarindex++; state = 0; break; default: abort (); } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for ((void) NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for ((void) NULL; ch != '\0'; ch = *src++) if (b64rmap[ch] != b64rmap_space) return (-1); default: break; } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } int kms_message_b64_pton (char const *src, uint8_t *target, size_t targsize) { if (target) return b64_pton_do (src, target, targsize); else return b64_pton_len (src); } int kms_message_b64_to_b64url (const char *src, size_t srclength, char *target, size_t targsize) { size_t i; for (i = 0; i < srclength; i++) { if (i >= targsize) { return -1; } target[i] = src[i]; if (target[i] == '+') { target[i] = '-'; } else if (target[i] == '/') { target[i] = '_'; } } /* NULL terminate if room. */ if (i < targsize) { target[i] = '\0'; } return (int) i; } int kms_message_b64url_to_b64 (const char *src, size_t srclength, char *target, size_t targsize) { size_t i; size_t boundary; for (i = 0; i < srclength; i++) { if (i >= targsize) { return -1; } target[i] = src[i]; if (target[i] == '-') { target[i] = '+'; } else if (target[i] == '_') { target[i] = '/'; } } /* Pad to four byte boundary. */ boundary = 4 * ((i + 3) / 4); for (; i < boundary; i++) { if (i >= targsize) { return -1; } target[i] = '='; } /* NULL terminate if room. */ if (i < targsize) { target[i] = '\0'; } return (int) i; } char * kms_message_raw_to_b64 (const uint8_t *raw, size_t raw_len) { char *b64; size_t b64_len; b64_len = (raw_len / 3 + 1) * 4 + 1; b64 = malloc (b64_len); KMS_ASSERT (b64); memset (b64, 0, b64_len); if (-1 == kms_message_b64_ntop (raw, raw_len, b64, b64_len)) { free (b64); return NULL; } return b64; } uint8_t * kms_message_b64_to_raw (const char *b64, size_t *out) { uint8_t *raw; int ret; size_t b64len; b64len = strlen (b64); raw = (uint8_t *) malloc (b64len + 1); KMS_ASSERT (raw); memset (raw, 0, b64len + 1); ret = kms_message_b64_pton (b64, raw, b64len); if (ret > 0) { *out = (size_t) ret; return raw; } free (raw); return NULL; } char * kms_message_raw_to_b64url (const uint8_t *raw, size_t raw_len) { char *b64; size_t b64len; b64 = kms_message_raw_to_b64 (raw, raw_len); if (!b64) { return NULL; } b64len = strlen (b64); if (-1 == kms_message_b64_to_b64url (b64, b64len, b64, b64len)) { free (b64); return NULL; } return b64; } uint8_t * kms_message_b64url_to_raw (const char *b64url, size_t *out) { char *b64; size_t capacity; uint8_t *raw; size_t b64urllen; b64urllen = strlen(b64url); /* Add four for padding '=' characters. */ capacity = b64urllen + 4; b64 = malloc (capacity); KMS_ASSERT (b64); memset (b64, 0, capacity); if (-1 == kms_message_b64url_to_b64 (b64url, b64urllen, b64, capacity)) { free (b64); return NULL; } raw = kms_message_b64_to_raw (b64, out); free (b64); return raw; } libmongocrypt-1.19.0/kms-message/src/kms_caller_identity_request.c000066400000000000000000000027501521103432300254340ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"){} * * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_message/kms_b64.h" #include "kms_request_str.h" kms_request_t * kms_caller_identity_request_new (const kms_request_opt_t *opt) { kms_request_t *request; kms_request_str_t *payload = NULL; request = kms_request_new ("POST", "/", opt); if (kms_request_get_error (request)) { goto done; } if (!(kms_request_add_header_field ( request, "Content-Type", "application/x-www-form-urlencoded"))) { goto done; } payload = kms_request_str_new (); kms_request_str_appendf (payload, "Action=GetCallerIdentity&Version=2011-06-15"); if (!kms_request_append_payload (request, payload->str, payload->len)) { KMS_ERROR (request, "Could not append payload"); goto done; } done: kms_request_str_destroy (payload); return request; } libmongocrypt-1.19.0/kms-message/src/kms_crypto.h000066400000000000000000000044411521103432300220350ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_KMS_CRYPTO_H #define KMS_MESSAGE_KMS_CRYPTO_H #include #include typedef struct { bool (*sha256) (void *ctx, const char *input, size_t len, unsigned char *hash_out); bool (*sha256_hmac) (void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out); bool (*sign_rsaes_pkcs1_v1_5) (void *sign_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out); void *ctx; void *sign_ctx; } _kms_crypto_t; int kms_crypto_init (void); void kms_crypto_cleanup (void); bool kms_sha256 (void *ctx, const char *input, size_t len, unsigned char *hash_out); bool kms_sha256_hmac (void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out); #define KMS_SIGN_RSAES_PKCS1_V1_5_OUTLEN 256 /* signature_out must be a preallocated buffer of 256 bytes (or greater). */ bool kms_sign_rsaes_pkcs1_v1_5 (void *sign_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out); #endif /* KMS_MESSAGE_KMS_CRYPTO_H */ libmongocrypt-1.19.0/kms-message/src/kms_crypto_apple.c000066400000000000000000000111351521103432300232070ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_crypto.h" #include "kms_message_private.h" /* KMS_ASSERT */ #ifdef KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO #include #include #include #include #include #include int kms_crypto_init (void) { return 0; } void kms_crypto_cleanup (void) { } bool kms_sha256 (void *unused_ctx, const char *input, size_t len, unsigned char *hash_out) { CC_SHA256_CTX ctx; CC_SHA256_Init (&ctx); KMS_ASSERT (len <= (size_t) UINT32_MAX); CC_SHA256_Update (&ctx, input, (uint32_t) len); CC_SHA256_Final (hash_out, &ctx); return true; } bool kms_sha256_hmac (void *unused_ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) { CCHmac (kCCHmacAlgSHA256, key_input, key_len, input, len, hash_out); return true; } static void safe_CFRelease (CFTypeRef ptr) { if (ptr) { CFRelease (ptr); } } bool kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out) { CFDataRef key_data_ref = NULL; CFDataRef pass_ref = NULL; SecItemImportExportKeyParameters import_params; OSStatus status; /* TODO: I think the expected format should be kSecFormatWrappedPKCS8, but * GCP keys appear to only load for kSecFormatBSAFE. */ SecExternalFormat format = kSecFormatUnknown; SecExternalItemType type = kSecItemTypePrivateKey; CFArrayRef out_ref = NULL; SecKeyRef key_ref = NULL; CFDataRef data_to_sign_ref = NULL; CFErrorRef error_ref; CFDataRef signature_ref = NULL; bool ret = false; key_data_ref = CFDataCreate (NULL /* default allocator */, (const uint8_t *) private_key, (CFIndex) private_key_len); if (!key_data_ref) { goto cleanup; } memset (&import_params, 0, sizeof (SecItemImportExportKeyParameters)); import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; /* Give an empty password. SecItemImport returns an error expecting a * password. */ pass_ref = CFDataCreate (NULL, NULL, 0); if (!pass_ref) { goto cleanup; } import_params.passphrase = (CFTypeRef) pass_ref; status = SecItemImport (key_data_ref, NULL /* extension. */, &format, &type, 0, &import_params, NULL /* keychain */, &out_ref); if (status != errSecSuccess) { goto cleanup; } if (1 != CFArrayGetCount (out_ref)) { goto cleanup; } key_ref = (SecKeyRef) CFArrayGetValueAtIndex (out_ref, 0); KMS_ASSERT (input_len <= (size_t) LONG_MAX); data_to_sign_ref = CFDataCreate (NULL, (const uint8_t *) input, (long) input_len); if (!data_to_sign_ref) { goto cleanup; } error_ref = NULL; signature_ref = SecKeyCreateSignature (key_ref, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256, data_to_sign_ref, &error_ref); if (!signature_ref) { goto cleanup; } if (CFDataGetLength(signature_ref) != KMS_SIGN_RSAES_PKCS1_V1_5_OUTLEN) { goto cleanup; } memcpy (signature_out, CFDataGetBytePtr (signature_ref), (size_t) CFDataGetLength (signature_ref)); ret = true; cleanup: safe_CFRelease (key_data_ref); safe_CFRelease (pass_ref); safe_CFRelease (out_ref); safe_CFRelease (data_to_sign_ref); safe_CFRelease (signature_ref); return ret; } #endif /* KMS_MESSAGE_ENABLE_CRYPTO_COMMON_CRYPTO */ libmongocrypt-1.19.0/kms-message/src/kms_crypto_libcrypto.c000066400000000000000000000065661521103432300241310ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_crypto.h" #include "kms_message_private.h" #ifdef KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO #include #include #include #include /* INT_MAX, LONG_MAX */ #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) static EVP_MD_CTX * EVP_MD_CTX_new (void) { return calloc (sizeof (EVP_MD_CTX), 1); } static void EVP_MD_CTX_free (EVP_MD_CTX *ctx) { EVP_MD_CTX_cleanup (ctx); free (ctx); } #endif int kms_crypto_init (void) { return 0; } void kms_crypto_cleanup (void) { } bool kms_sha256 (void *unused_ctx, const char *input, size_t len, unsigned char *hash_out) { EVP_MD_CTX *digest_ctxp = EVP_MD_CTX_new (); KMS_ASSERT (digest_ctxp); bool rval = false; if (1 != EVP_DigestInit_ex (digest_ctxp, EVP_sha256 (), NULL)) { goto cleanup; } if (1 != EVP_DigestUpdate (digest_ctxp, input, len)) { goto cleanup; } rval = (1 == EVP_DigestFinal_ex (digest_ctxp, hash_out, NULL)); cleanup: EVP_MD_CTX_free (digest_ctxp); return rval; } bool kms_sha256_hmac (void *unused_ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) { KMS_ASSERT (key_len <= INT_MAX); return HMAC (EVP_sha256 (), key_input, (int) key_len, (unsigned char *) input, len, hash_out, NULL) != NULL; } bool kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out) { EVP_MD_CTX *ctx; EVP_PKEY *pkey = NULL; bool ret = false; size_t signature_out_len = 256; ctx = EVP_MD_CTX_new (); KMS_ASSERT (ctx); KMS_ASSERT (private_key_len <= LONG_MAX); pkey = d2i_PrivateKey (EVP_PKEY_RSA, NULL, (const unsigned char **) &private_key, (long) private_key_len); if (!pkey) { goto cleanup; } ret = EVP_DigestSignInit (ctx, NULL, EVP_sha256 (), NULL /* engine */, pkey); if (ret != 1) { goto cleanup; } ret = EVP_DigestSignUpdate (ctx, input, input_len); if (ret != 1) { goto cleanup; } ret = EVP_DigestSignFinal (ctx, signature_out, &signature_out_len); if (ret != 1) { goto cleanup; } ret = true; cleanup: EVP_MD_CTX_free (ctx); EVP_PKEY_free (pkey); return ret; } #endif /* KMS_MESSAGE_ENABLE_CRYPTO_LIBCRYPTO */ libmongocrypt-1.19.0/kms-message/src/kms_crypto_none.c000066400000000000000000000032131521103432300230430ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_crypto.h" #ifndef KMS_MESSAGE_ENABLE_CRYPTO int kms_crypto_init (void) { return 0; } void kms_crypto_cleanup (void) { } bool kms_sha256 (void *unused_ctx, const char *input, size_t len, unsigned char *hash_out) { /* only gets called if hooks were mistakenly not set */ return false; } bool kms_sha256_hmac (void *unused_ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) { /* only gets called if hooks were mistakenly not set */ return false; } bool kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out) { /* only gets called if hooks were mistakenly not set */ return false; } #endif /* KMS_MESSAGE_ENABLE_CRYPTO */ libmongocrypt-1.19.0/kms-message/src/kms_crypto_windows.c000066400000000000000000000167061521103432300236110ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_crypto.h" #ifdef KMS_MESSAGE_ENABLE_CRYPTO_CNG #include "kms_message_private.h" // tell windows.h not to include a bunch of headers we don't need: #define WIN32_LEAN_AND_MEAN // Tell windows.h not to define any NT status codes, so that we can // get the definitions from ntstatus.h, which has a more complete list. #define WIN32_NO_STATUS #include #undef WIN32_NO_STATUS // Obtain a definition for the ntstatus type. #include // Add back in the status definitions so that macro expansions for // things like STILL_ACTIVE and WAIT_OBJECT_O can be resolved (they // expand to STATUS_ codes). #include #include #include static BCRYPT_ALG_HANDLE _algoSHA256 = 0; static BCRYPT_ALG_HANDLE _algoSHA256Hmac = 0; static BCRYPT_ALG_HANDLE _algoRSA = 0; #define SHA_256_HASH_LEN 32 int kms_crypto_init (void) { if (BCryptOpenAlgorithmProvider ( &_algoSHA256, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0) != STATUS_SUCCESS) { return 1; } if (BCryptOpenAlgorithmProvider (&_algoSHA256Hmac, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG) != STATUS_SUCCESS) { return 2; } if (BCryptOpenAlgorithmProvider ( &_algoRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0) != STATUS_SUCCESS) { return 3; } return 0; } void kms_crypto_cleanup (void) { (void) BCryptCloseAlgorithmProvider (_algoSHA256, 0); (void) BCryptCloseAlgorithmProvider (_algoSHA256Hmac, 0); (void) BCryptCloseAlgorithmProvider (_algoRSA, 0); } bool kms_sha256 (void *unused_ctx, const char *input, size_t len, unsigned char *hash_out) { BCRYPT_HASH_HANDLE hHash; NTSTATUS status = BCryptCreateHash (_algoSHA256, &hHash, NULL, 0, NULL, 0, 0); if (status != STATUS_SUCCESS) { return 0; } status = BCryptHashData (hHash, (PUCHAR) (input), (ULONG) len, 0); if (status != STATUS_SUCCESS) { goto cleanup; } // Hardcode output length status = BCryptFinishHash (hHash, hash_out, 256 / 8, 0); if (status != STATUS_SUCCESS) { goto cleanup; } cleanup: (void) BCryptDestroyHash (hHash); return status == STATUS_SUCCESS ? 1 : 0; } bool kms_sha256_hmac (void *unused_ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) { BCRYPT_HASH_HANDLE hHash; NTSTATUS status = BCryptCreateHash ( _algoSHA256Hmac, &hHash, NULL, 0, (PUCHAR) key_input, (ULONG) key_len, 0); if (status != STATUS_SUCCESS) { return 0; } status = BCryptHashData (hHash, (PUCHAR) input, (ULONG) len, 0); if (status != STATUS_SUCCESS) { goto cleanup; } // Hardcode output length status = BCryptFinishHash (hHash, hash_out, 256 / 8, 0); if (status != STATUS_SUCCESS) { goto cleanup; } cleanup: (void) BCryptDestroyHash (hHash); return status == STATUS_SUCCESS ? 1 : 0; } bool kms_sign_rsaes_pkcs1_v1_5 (void *unused_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out) { bool success = false; bool ret = false; LPBYTE blob_private = NULL; DWORD blob_private_len = 0; LPBYTE raw_private = NULL; DWORD raw_private_len = 0; NTSTATUS status; BCRYPT_KEY_HANDLE hKey = NULL; BCRYPT_PKCS1_PADDING_INFO padding_PKCS1; unsigned char *hash_value = NULL; DWORD hash_length = 256; success = CryptDecodeObjectEx (X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, (BYTE*) private_key, (DWORD) private_key_len, 0, NULL, NULL, &blob_private_len); if (!success) { goto cleanup; } blob_private = (LPBYTE) calloc (1, blob_private_len); KMS_ASSERT (blob_private); success = CryptDecodeObjectEx (X509_ASN_ENCODING, PKCS_PRIVATE_KEY_INFO, (BYTE*) private_key, (DWORD) private_key_len, 0, NULL, blob_private, &blob_private_len); if (!success) { goto cleanup; } CRYPT_PRIVATE_KEY_INFO *privateKeyInfo = (CRYPT_PRIVATE_KEY_INFO *) blob_private; success = CryptDecodeObjectEx (X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, privateKeyInfo->PrivateKey.pbData, (DWORD) privateKeyInfo->PrivateKey.cbData, 0, NULL, NULL, &raw_private_len); if (!success) { goto cleanup; } raw_private = (LPBYTE) calloc (1, raw_private_len); KMS_ASSERT (raw_private); success = CryptDecodeObjectEx (X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, privateKeyInfo->PrivateKey.pbData, (DWORD) privateKeyInfo->PrivateKey.cbData, 0, NULL, raw_private, &raw_private_len); if (!success) { goto cleanup; } status = BCryptImportKeyPair ( _algoRSA, NULL, LEGACY_RSAPRIVATE_BLOB, &hKey, raw_private, raw_private_len, 0); if (!NT_SUCCESS (status)) { goto cleanup; } hash_value = calloc (1, SHA_256_HASH_LEN); KMS_ASSERT (hash_value); if(!kms_sha256 (NULL, input, input_len, hash_value)) { goto cleanup; } padding_PKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM; status = BCryptSignHash (hKey, &padding_PKCS1, hash_value, SHA_256_HASH_LEN, signature_out, hash_length, &hash_length, BCRYPT_PAD_PKCS1); if (!NT_SUCCESS (status)) { goto cleanup; } ret = true; cleanup: BCryptDestroyKey(hKey); free (blob_private); free (raw_private); free (hash_value); return ret; } #endif /* KMS_MESSAGE_ENABLE_CRYPTO_CNG */ libmongocrypt-1.19.0/kms-message/src/kms_decrypt_request.c000066400000000000000000000041001521103432300237220ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_message/kms_b64.h" #include "kms_request_str.h" kms_request_t * kms_decrypt_request_new (const uint8_t *ciphertext_blob, size_t len, const kms_request_opt_t *opt) { kms_request_t *request; size_t b64_len; char *b64 = NULL; kms_request_str_t *payload = NULL; request = kms_request_new ("POST", "/", opt); if (kms_request_get_error (request)) { goto done; } if (!(kms_request_add_header_field ( request, "Content-Type", "application/x-amz-json-1.1") && kms_request_add_header_field ( request, "X-Amz-Target", "TrentService.Decrypt"))) { goto done; } b64_len = (len / 3 + 1) * 4 + 1; if (!(b64 = malloc (b64_len))) { KMS_ERROR (request, "Could not allocate %d bytes for base64-encoding payload", (int) b64_len); goto done; } if (kms_message_b64_ntop (ciphertext_blob, len, b64, b64_len) == -1) { KMS_ERROR (request, "Could not base64-encode ciphertext blob"); goto done; } payload = kms_request_str_new (); kms_request_str_appendf (payload, "{\"CiphertextBlob\": \"%s\"}", b64); if (!kms_request_append_payload (request, payload->str, payload->len)) { KMS_ERROR (request, "Could not append payload"); goto done; } done: free (b64); kms_request_str_destroy (payload); return request; } libmongocrypt-1.19.0/kms-message/src/kms_encrypt_request.c000066400000000000000000000042721521103432300237460ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_message/kms_b64.h" #include "kms_request_str.h" kms_request_t * kms_encrypt_request_new (const uint8_t *plaintext, size_t plaintext_length, const char *key_id, const kms_request_opt_t *opt) { kms_request_t *request; size_t b64_len; char *b64 = NULL; kms_request_str_t *payload = NULL; request = kms_request_new ("POST", "/", opt); if (kms_request_get_error (request)) { goto done; } if (!(kms_request_add_header_field ( request, "Content-Type", "application/x-amz-json-1.1") && kms_request_add_header_field ( request, "X-Amz-Target", "TrentService.Encrypt"))) { goto done; } b64_len = (plaintext_length / 3 + 1) * 4 + 1; if (!(b64 = malloc (b64_len))) { KMS_ERROR (request, "Could not allocate %d bytes for base64-encoding payload", (int) b64_len); goto done; } if (kms_message_b64_ntop ( (const uint8_t *) plaintext, plaintext_length, b64, b64_len) == -1) { KMS_ERROR (request, "Could not base64-encode plaintext"); goto done; } payload = kms_request_str_new (); kms_request_str_appendf ( payload, "{\"Plaintext\": \"%s\", \"KeyId\": \"%s\"}", b64, key_id); if (!kms_request_append_payload (request, payload->str, payload->len)) { KMS_ERROR (request, "Could not append payload"); goto done; } done: free (b64); kms_request_str_destroy (payload); return request; } libmongocrypt-1.19.0/kms-message/src/kms_endian_private.h000066400000000000000000000121341521103432300235030ustar00rootroot00000000000000/* * 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. */ /* This file is copied and modified from libbson's bson-endian.h. */ #ifndef KMS_ENDIAN_PRIVATE_H #define KMS_ENDIAN_PRIVATE_H #include #include "kms_message/kms_message_defines.h" /* Define a fallback for __has_builtin for compatibility with non-clang compilers. */ #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if defined(__clang__) && __has_builtin(__builtin_bswap16) && \ __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64) #define KMS_UINT16_SWAP_LE_BE(v) __builtin_bswap16 (v) #define KMS_UINT32_SWAP_LE_BE(v) __builtin_bswap32 (v) #define KMS_UINT64_SWAP_LE_BE(v) __builtin_bswap64 (v) #elif defined(__GNUC__) && (__GNUC__ >= 4) #if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 3) #define KMS_UINT32_SWAP_LE_BE(v) __builtin_bswap32 ((uint32_t) v) #define KMS_UINT64_SWAP_LE_BE(v) __builtin_bswap64 ((uint64_t) v) #endif #if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 8) #define KMS_UINT16_SWAP_LE_BE(v) __builtin_bswap16 ((uint32_t) v) #endif #endif #ifndef KMS_UINT16_SWAP_LE_BE #define KMS_UINT16_SWAP_LE_BE(v) __kms_uint16_swap_slow ((uint16_t) v) #endif #ifndef KMS_UINT32_SWAP_LE_BE #define KMS_UINT32_SWAP_LE_BE(v) __kms_uint32_swap_slow ((uint32_t) v) #endif #ifndef KMS_UINT64_SWAP_LE_BE #define KMS_UINT64_SWAP_LE_BE(v) __kms_uint64_swap_slow ((uint64_t) v) #endif #if defined(KMS_MESSAGE_LITTLE_ENDIAN) #define KMS_UINT16_FROM_LE(v) ((uint16_t) v) #define KMS_UINT16_TO_LE(v) ((uint16_t) v) #define KMS_UINT16_FROM_BE(v) KMS_UINT16_SWAP_LE_BE (v) #define KMS_UINT16_TO_BE(v) KMS_UINT16_SWAP_LE_BE (v) #define KMS_UINT32_FROM_LE(v) ((uint32_t) v) #define KMS_UINT32_TO_LE(v) ((uint32_t) v) #define KMS_UINT32_FROM_BE(v) KMS_UINT32_SWAP_LE_BE (v) #define KMS_UINT32_TO_BE(v) KMS_UINT32_SWAP_LE_BE (v) #define KMS_UINT64_FROM_LE(v) ((uint64_t) v) #define KMS_UINT64_TO_LE(v) ((uint64_t) v) #define KMS_UINT64_FROM_BE(v) KMS_UINT64_SWAP_LE_BE (v) #define KMS_UINT64_TO_BE(v) KMS_UINT64_SWAP_LE_BE (v) #elif defined(KMS_MESSAGE_BIG_ENDIAN) #define KMS_UINT16_FROM_LE(v) KMS_UINT16_SWAP_LE_BE (v) #define KMS_UINT16_TO_LE(v) KMS_UINT16_SWAP_LE_BE (v) #define KMS_UINT16_FROM_BE(v) ((uint16_t) v) #define KMS_UINT16_TO_BE(v) ((uint16_t) v) #define KMS_UINT32_FROM_LE(v) KMS_UINT32_SWAP_LE_BE (v) #define KMS_UINT32_TO_LE(v) KMS_UINT32_SWAP_LE_BE (v) #define KMS_UINT32_FROM_BE(v) ((uint32_t) v) #define KMS_UINT32_TO_BE(v) ((uint32_t) v) #define KMS_UINT64_FROM_LE(v) KMS_UINT64_SWAP_LE_BE (v) #define KMS_UINT64_TO_LE(v) KMS_UINT64_SWAP_LE_BE (v) #define KMS_UINT64_FROM_BE(v) ((uint64_t) v) #define KMS_UINT64_TO_BE(v) ((uint64_t) v) #else #error "The endianness of target architecture is unknown." #endif /* *-------------------------------------------------------------------------- * * __kms_uint16_swap_slow -- * * Fallback endianness conversion for 16-bit integers. * * Returns: * The endian swapped version. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static KMS_MSG_INLINE uint16_t __kms_uint16_swap_slow (uint16_t v) /* IN */ { return (uint16_t) (((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8)); } /* *-------------------------------------------------------------------------- * * __kms_uint32_swap_slow -- * * Fallback endianness conversion for 32-bit integers. * * Returns: * The endian swapped version. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static KMS_MSG_INLINE uint32_t __kms_uint32_swap_slow (uint32_t v) /* IN */ { return ((v & 0x000000FFU) << 24) | ((v & 0x0000FF00U) << 8) | ((v & 0x00FF0000U) >> 8) | ((v & 0xFF000000U) >> 24); } /* *-------------------------------------------------------------------------- * * __kms_uint64_swap_slow -- * * Fallback endianness conversion for 64-bit integers. * * Returns: * The endian swapped version. * * Side effects: * None. * *-------------------------------------------------------------------------- */ static KMS_MSG_INLINE uint64_t __kms_uint64_swap_slow (uint64_t v) /* IN */ { return ((v & 0x00000000000000FFULL) << 56) | ((v & 0x000000000000FF00ULL) << 40) | ((v & 0x0000000000FF0000ULL) << 24) | ((v & 0x00000000FF000000ULL) << 8) | ((v & 0x000000FF00000000ULL) >> 8) | ((v & 0x0000FF0000000000ULL) >> 24) | ((v & 0x00FF000000000000ULL) >> 40) | ((v & 0xFF00000000000000ULL) >> 56); } #endif /* KMS_ENDIAN_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_gcp_request.c000066400000000000000000000236121521103432300230320ustar00rootroot00000000000000/* * Copyright 2020-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 "kms_message/kms_gcp_request.h" #include "kms_message/kms_b64.h" #include "kms_message_private.h" #include "kms_request_opt_private.h" /* Set a default expiration of 5 minutes for JSON Web Tokens (GCP allows up to * one hour) */ #define JWT_EXPIRATION_SECS 5 * 60 kms_request_t * kms_gcp_request_oauth_new (const char *host, const char *email, const char *audience, const char *scope, const char *private_key_data, size_t private_key_len, const kms_request_opt_t *opt) { kms_request_t *req = NULL; kms_request_str_t *str = NULL; time_t issued_at; /* base64 encoding of {"alg":"RS256","typ":"JWT"} */ const char *jwt_header_b64url = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; char *jwt_claims_b64url = NULL; char *jwt_header_and_claims_b64url = NULL; uint8_t *jwt_signature = NULL; char *jwt_signature_b64url = NULL; char *jwt_assertion_b64url = NULL; char *payload = NULL; req = kms_request_new ("POST", "/token", opt); if (opt->provider != KMS_REQUEST_PROVIDER_GCP) { KMS_ERROR (req, "Expected KMS request with provider type: GCP"); goto done; } if (kms_request_get_error (req)) { goto done; } /* Produce the signed JWT .. */ issued_at = time (NULL); str = kms_request_str_new (); kms_request_str_appendf (str, "{\"iss\": \"%s\", \"aud\": \"%s\", \"scope\": " "\"%s\", \"iat\": %lu, \"exp\": %lu}", email, audience, scope, (unsigned long) issued_at, (unsigned long) issued_at + JWT_EXPIRATION_SECS); jwt_claims_b64url = kms_message_raw_to_b64url ((const uint8_t *) str->str, str->len); kms_request_str_destroy (str); if (!jwt_claims_b64url) { KMS_ERROR (req, "Failed to base64url encode JWT claims"); goto done; } str = kms_request_str_new (); kms_request_str_appendf (str, "%s.%s", jwt_header_b64url, jwt_claims_b64url); jwt_header_and_claims_b64url = kms_request_str_detach (str); /* Produce the signature of . */ req->crypto.sign_rsaes_pkcs1_v1_5 = kms_sign_rsaes_pkcs1_v1_5; if (opt->crypto.sign_rsaes_pkcs1_v1_5) { req->crypto.sign_rsaes_pkcs1_v1_5 = opt->crypto.sign_rsaes_pkcs1_v1_5; req->crypto.sign_ctx = opt->crypto.sign_ctx; } jwt_signature = calloc (1, KMS_SIGN_RSAES_PKCS1_V1_5_OUTLEN); KMS_ASSERT (jwt_signature); if (!req->crypto.sign_rsaes_pkcs1_v1_5 ( req->crypto.sign_ctx, private_key_data, private_key_len, jwt_header_and_claims_b64url, strlen (jwt_header_and_claims_b64url), jwt_signature)) { KMS_ERROR (req, "Failed to create GCP oauth request signature"); goto done; } jwt_signature_b64url = kms_message_raw_to_b64url (jwt_signature, KMS_SIGN_RSAES_PKCS1_V1_5_OUTLEN); if (!jwt_signature_b64url) { KMS_ERROR (req, "Failed to base64url encode JWT signature"); goto done; } str = kms_request_str_new (); kms_request_str_appendf (str, "%s.%s.%s", jwt_header_b64url, jwt_claims_b64url, jwt_signature_b64url); jwt_assertion_b64url = kms_request_str_detach (str); str = kms_request_str_new_from_chars ("grant_type=urn%3Aietf%3Aparams%3Aoauth%" "3Agrant-type%3Ajwt-bearer&assertion=", -1); kms_request_str_append_chars (str, jwt_assertion_b64url, -1); payload = kms_request_str_detach (str); if (!kms_request_add_header_field ( req, "Content-Type", "application/x-www-form-urlencoded")) { goto done; } if (!kms_request_add_header_field (req, "Host", host)) { goto done; } if (!kms_request_add_header_field (req, "Accept", "application/json")) { goto done; } if (!kms_request_append_payload (req, payload, strlen (payload))) { goto done; } done: free (jwt_signature); free (jwt_signature_b64url); free (jwt_claims_b64url); free (jwt_header_and_claims_b64url); free (jwt_assertion_b64url); free (payload); return req; } static kms_request_t * _encrypt_decrypt_common (const char *encrypt_decrypt, const char *host, const char *access_token, const char *project_id, const char *location, const char *key_ring_name, const char *key_name, const char *key_version, const uint8_t *value, size_t value_len, const kms_request_opt_t *opt) { char *path_and_query = NULL; char *payload = NULL; char *bearer_token_value = NULL; char *value_base64 = NULL; kms_request_t *req; kms_request_str_t *str; str = kms_request_str_new (); /* /v1/projects/{project-id}/locations/{location}/keyRings/{key-ring-name}/cryptoKeys/{key-name} */ kms_request_str_appendf ( str, "/v1/projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", project_id, location, key_ring_name, key_name); if (key_version && strlen (key_version) > 0) { kms_request_str_appendf (str, "/cryptoKeyVersions/%s", key_version); } kms_request_str_appendf (str, ":%s", encrypt_decrypt); path_and_query = kms_request_str_detach (str); req = kms_request_new ("POST", path_and_query, opt); if (opt->provider != KMS_REQUEST_PROVIDER_GCP) { KMS_ERROR (req, "Expected KMS request with provider type: GCP"); goto done; } if (kms_request_get_error (req)) { goto done; } value_base64 = kms_message_raw_to_b64 (value, value_len); if (!value_base64) { KMS_ERROR (req, "Could not bases64-encode plaintext"); goto done; } str = kms_request_str_new (); if (0 == strcmp ("encrypt", encrypt_decrypt)) { kms_request_str_appendf (str, "{\"plaintext\": \"%s\"}", value_base64); } else { kms_request_str_appendf (str, "{\"ciphertext\": \"%s\"}", value_base64); } payload = kms_request_str_detach (str); str = kms_request_str_new (); kms_request_str_appendf (str, "Bearer %s", access_token); bearer_token_value = kms_request_str_detach (str); if (!kms_request_add_header_field ( req, "Authorization", bearer_token_value)) { goto done; } if (!kms_request_add_header_field ( req, "Content-Type", "application/json")) { goto done; } if (!kms_request_add_header_field (req, "Host", host)) { goto done; } if (!kms_request_add_header_field (req, "Accept", "application/json")) { goto done; } if (!kms_request_append_payload (req, payload, strlen (payload))) { goto done; } done: kms_request_free_string (path_and_query); kms_request_free_string (payload); kms_request_free_string (bearer_token_value); kms_request_free_string (value_base64); return req; } kms_request_t * kms_gcp_request_encrypt_new (const char *host, const char *access_token, const char *project_id, const char *location, const char *key_ring_name, const char *key_name, const char *key_version, const uint8_t *plaintext, size_t plaintext_len, const kms_request_opt_t *opt) { return _encrypt_decrypt_common ("encrypt", host, access_token, project_id, location, key_ring_name, key_name, key_version, plaintext, plaintext_len, opt); } kms_request_t * kms_gcp_request_decrypt_new (const char *host, const char *access_token, const char *project_id, const char *location, const char *key_ring_name, const char *key_name, const uint8_t *ciphertext, size_t ciphertext_len, const kms_request_opt_t *opt) { return _encrypt_decrypt_common ("decrypt", host, access_token, project_id, location, key_ring_name, key_name, NULL /* key_version */, ciphertext, ciphertext_len, opt); } libmongocrypt-1.19.0/kms-message/src/kms_kmip_item_type_private.h000066400000000000000000000033101521103432300252600ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_ITEM_TYPE_PRIVATE_H #define KMS_KMIP_ITEM_TYPE_PRIVATE_H #include "kms_message/kms_message_defines.h" #define KMS_XMACRO \ KMS_X (Structure, 0x01) \ KMS_X (Integer, 0x02) \ KMS_X (LongInteger, 0x03) \ KMS_X (BigInteger, 0x04) \ KMS_X (Enumeration, 0x05) \ KMS_X (Boolean, 0x06) \ KMS_X (TextString, 0x07) \ KMS_X (ByteString, 0x08) \ KMS_X (DateTime, 0x09) \ KMS_X_LAST (Interval, 0x0A) /* Generate an enum with each item_type value. */ #define KMS_X(ITEM_TYPE, VAL) KMIP_ITEM_TYPE_##ITEM_TYPE = VAL, #define KMS_X_LAST(ITEM_TYPE, VAL) KMIP_ITEM_TYPE_##ITEM_TYPE = VAL typedef enum { KMS_XMACRO } kmip_item_type_t; #undef KMS_X #undef KMS_X_LAST #define KMS_X(ITEM_TYPE, VAL) \ case KMIP_ITEM_TYPE_##ITEM_TYPE: \ return #ITEM_TYPE; #define KMS_X_LAST KMS_X static KMS_MSG_INLINE const char * kmip_item_type_to_string (kmip_item_type_t item_type) { switch (item_type) { default: return "Unknown KMIP item type"; KMS_XMACRO } } #undef KMS_X #undef KMS_X_LAST #undef KMS_XMACRO #endif /* KMS_KMIP_ITEM_TYPE_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kmip_reader_writer.c000066400000000000000000000320331521103432300243640ustar00rootroot00000000000000/* * 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 "kms_kmip_reader_writer_private.h" #include "kms_endian_private.h" #include "kms_request_str.h" #include #define MAX_KMIP_WRITER_POSITIONS 10 /* KMIP encodes signed integers with two's complement. * Parsing functions read Integer / LongInteger as int32_t / int64_t by * reinterpreting byte representation. * Ensure that platform represents integers in two's complement. * See: https://stackoverflow.com/a/64843863/774658 */ #if (-1 & 3) != 3 #error Error: Twos complement integer representation is required. #endif struct _kmip_writer_t { kms_request_str_t *buffer; size_t positions[MAX_KMIP_WRITER_POSITIONS]; size_t cur_pos; }; kmip_writer_t * kmip_writer_new (void) { kmip_writer_t *writer = calloc (1, sizeof (kmip_writer_t)); KMS_ASSERT (writer); writer->buffer = kms_request_str_new (); return writer; } void kmip_writer_destroy (kmip_writer_t *writer) { kms_request_str_destroy (writer->buffer); free (writer); } void kmip_writer_write_u8 (kmip_writer_t *writer, uint8_t value) { char *c = (char *) &value; kms_request_str_append_chars (writer->buffer, c, 1); } void kmip_writer_write_u16 (kmip_writer_t *writer, uint16_t value) { uint16_t v = KMS_UINT16_TO_BE (value); char *c = (char *) &v; kms_request_str_append_chars (writer->buffer, c, 2); } void kmip_writer_write_u32 (kmip_writer_t *writer, uint32_t value) { uint32_t v = KMS_UINT32_TO_BE (value); char *c = (char *) &v; kms_request_str_append_chars (writer->buffer, c, 4); } void kmip_writer_write_u64 (kmip_writer_t *writer, uint64_t value) { uint64_t v = KMS_UINT64_TO_BE (value); char *c = (char *) &v; kms_request_str_append_chars (writer->buffer, c, 8); } void kmip_writer_write_tag_enum (kmip_writer_t *writer, kmip_tag_type_t tag) { /* The 0x42 prefix is for tags built into the protocol. */ /* The 0x54 prefix is for extension tags. */ kmip_writer_write_u8 (writer, 0x42); kmip_writer_write_u16 (writer, (uint16_t) tag); } static size_t compute_padded_length (size_t len) { if (len % 8 == 0) { return len; } size_t padding = 8 - (len % 8); return len + padding; } void kmip_writer_write_string (kmip_writer_t *writer, kmip_tag_type_t tag, const char *str, size_t len) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_TextString); kmip_writer_write_u32 (writer, (uint32_t) len); size_t i; for (i = 0; i < len; i++) { kmip_writer_write_u8 (writer, (uint8_t) str[i]); } size_t padded_length = compute_padded_length (len); for (i = 0; i < padded_length - len; i++) { kmip_writer_write_u8 (writer, 0); } } void kmip_writer_write_bytes (kmip_writer_t *writer, kmip_tag_type_t tag, const char *str, size_t len) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_ByteString); kmip_writer_write_u32 (writer, (uint32_t) len); size_t i; for (i = 0; i < len; i++) { kmip_writer_write_u8 (writer, (uint8_t) str[i]); } size_t padded_length = compute_padded_length (len); for (i = 0; i < padded_length - len; i++) { kmip_writer_write_u8 (writer, 0); } } void kmip_writer_write_integer (kmip_writer_t *writer, kmip_tag_type_t tag, int32_t value) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_Integer); kmip_writer_write_u32 (writer, 4); KMS_ASSERT (value >= 0); kmip_writer_write_u32 (writer, (uint32_t) value); kmip_writer_write_u32 (writer, 0); } void kmip_writer_write_long_integer (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_LongInteger); kmip_writer_write_u32 (writer, 8); KMS_ASSERT (value >= 0); kmip_writer_write_u64 (writer, (uint64_t) value); } void kmip_writer_write_enumeration (kmip_writer_t *writer, kmip_tag_type_t tag, int32_t value) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_Enumeration); kmip_writer_write_u32 (writer, 4); KMS_ASSERT (value >= 0); kmip_writer_write_u32 (writer, (uint32_t) value); kmip_writer_write_u32 (writer, 0); } void kmip_writer_write_bool (kmip_writer_t *writer, kmip_tag_type_t tag, bool value) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_Boolean); kmip_writer_write_u32 (writer, 8); kmip_writer_write_u64(writer, (uint64_t) value); } void kmip_writer_write_datetime (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_DateTime); kmip_writer_write_u32 (writer, 8); KMS_ASSERT (value >= 0); kmip_writer_write_u64 (writer, (uint64_t) value); } void kmip_writer_begin_struct (kmip_writer_t *writer, kmip_tag_type_t tag) { kmip_writer_write_tag_enum (writer, tag); kmip_writer_write_u8 (writer, KMIP_ITEM_TYPE_Structure); size_t pos = writer->buffer->len; kmip_writer_write_u32 (writer, 0); KMS_ASSERT(writer->cur_pos < MAX_KMIP_WRITER_POSITIONS - 1); writer->cur_pos++; writer->positions[writer->cur_pos] = pos; } void kmip_writer_close_struct (kmip_writer_t *writer) { size_t current_pos = writer->buffer->len; KMS_ASSERT(writer->cur_pos > 0); size_t start_pos = writer->positions[writer->cur_pos]; writer->cur_pos--; /* offset by 4 */ uint32_t len = (uint32_t) (current_pos - start_pos - 4); uint32_t v = KMS_UINT32_TO_BE (len); char *c = (char *) &v; memcpy (writer->buffer->str + start_pos, c, 4); } const uint8_t * kmip_writer_get_buffer (kmip_writer_t *writer, size_t* len) { *len = writer->buffer->len; return (const uint8_t*) writer->buffer->str; } struct _kmip_reader_t { uint8_t *ptr; size_t pos; size_t len; }; kmip_reader_t * kmip_reader_new (uint8_t *ptr, size_t len) { kmip_reader_t *reader = calloc (1, sizeof (kmip_reader_t)); KMS_ASSERT (reader); reader->ptr = ptr; reader->len = len; return reader; } void kmip_reader_destroy (kmip_reader_t *reader) { free (reader); } bool kmip_reader_in_place (kmip_reader_t *reader, size_t pos, size_t len, kmip_reader_t *out_reader) { /* Everything should be padding to 8 byte boundaries. */ len = compute_padded_length (len); if ((pos + len) > reader->len) { return false; } memset (out_reader, 0, sizeof (kmip_reader_t)); out_reader->ptr = reader->ptr + reader->pos; out_reader->len = len; return true; } bool kmip_reader_has_data (kmip_reader_t *reader) { return reader->pos < reader->len; } #define CHECK_REMAINING_BUFFER_AND_RET(read_size) \ if ((reader->pos + (read_size)) > reader->len) { \ return false; \ } else \ ((void)0) bool kmip_reader_read_u8 (kmip_reader_t *reader, uint8_t *value) { CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint8_t)); *value = *(reader->ptr + reader->pos); reader->pos += sizeof (uint8_t); return true; } bool kmip_reader_read_u16 (kmip_reader_t *reader, uint16_t *value) { CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint16_t)); uint16_t temp; memcpy (&temp, reader->ptr + reader->pos, sizeof (uint16_t)); *value = KMS_UINT16_FROM_BE (temp); reader->pos += sizeof (uint16_t); return true; } bool kmip_reader_read_u32 (kmip_reader_t *reader, uint32_t *value) { CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint32_t)); uint32_t temp; memcpy (&temp, reader->ptr + reader->pos, sizeof (uint32_t)); *value = KMS_UINT32_FROM_BE (temp); reader->pos += sizeof (uint32_t); return true; } bool kmip_reader_read_u64 (kmip_reader_t *reader, uint64_t *value) { CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint64_t)); uint64_t temp; memcpy (&temp, reader->ptr + reader->pos, sizeof (uint64_t)); *value = KMS_UINT64_FROM_BE (temp); reader->pos += sizeof (uint64_t); return true; } bool kmip_reader_read_bytes (kmip_reader_t *reader, uint8_t **ptr, size_t length) { size_t advance_length = compute_padded_length (length); CHECK_REMAINING_BUFFER_AND_RET (advance_length); *ptr = reader->ptr + reader->pos; reader->pos += advance_length; return true; } #define CHECK_AND_RET(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) bool kmip_reader_read_tag (kmip_reader_t *reader, kmip_tag_type_t *tag) { uint8_t tag_first; CHECK_AND_RET (kmip_reader_read_u8 (reader, &tag_first)); if (tag_first != 0x42) { return false; } uint16_t tag_second; CHECK_AND_RET (kmip_reader_read_u16 (reader, &tag_second)); *tag = (kmip_tag_type_t) (0x420000 + tag_second); return true; } bool kmip_reader_read_length (kmip_reader_t *reader, uint32_t *length) { return kmip_reader_read_u32 (reader, length); } bool kmip_reader_read_type (kmip_reader_t *reader, kmip_item_type_t *type) { uint8_t u8; CHECK_AND_RET (kmip_reader_read_u8 (reader, &u8)); *type = (kmip_item_type_t) u8; return true; } bool kmip_reader_read_enumeration (kmip_reader_t *reader, uint32_t *enum_value) { CHECK_AND_RET (kmip_reader_read_u32 (reader, enum_value)); /* Skip 4 bytes because enums are padded. */ uint32_t ignored; return kmip_reader_read_u32 (reader, &ignored); } bool kmip_reader_read_bool (kmip_reader_t *reader, bool *value) { uint64_t u64; CHECK_AND_RET (kmip_reader_read_u64 (reader, &u64)); *value = (bool) u64; return true; } bool kmip_reader_read_integer (kmip_reader_t *reader, int32_t *value) { CHECK_AND_RET (kmip_reader_read_u32 (reader, (uint32_t*) value)); /* Skip 4 bytes because integers are padded. */ uint32_t ignored; return kmip_reader_read_u32 (reader, &ignored); } bool kmip_reader_read_long_integer (kmip_reader_t *reader, int64_t *value) { return kmip_reader_read_u64 (reader, (uint64_t*) value); } bool kmip_reader_read_string (kmip_reader_t *reader, uint8_t **ptr, size_t length) { return kmip_reader_read_bytes (reader, ptr, length); } bool kmip_reader_find (kmip_reader_t *reader, kmip_tag_type_t search_tag, kmip_item_type_t type, size_t *pos, size_t *length) { reader->pos = 0; while (kmip_reader_has_data (reader)) { kmip_tag_type_t read_tag; CHECK_AND_RET (kmip_reader_read_tag (reader, &read_tag)); kmip_item_type_t read_type; CHECK_AND_RET (kmip_reader_read_type (reader, &read_type)); uint32_t read_length; CHECK_AND_RET (kmip_reader_read_length (reader, &read_length)); if (read_tag == search_tag && read_type == type) { *pos = reader->pos; *length = read_length; CHECK_REMAINING_BUFFER_AND_RET (compute_padded_length(read_length)); return true; } size_t advance_length = read_length; advance_length = compute_padded_length (advance_length); CHECK_REMAINING_BUFFER_AND_RET (advance_length); /* Skip to the next type. */ reader->pos += advance_length; } return false; } bool kmip_reader_find_and_recurse (kmip_reader_t *reader, kmip_tag_type_t tag) { size_t pos; size_t length; if (!kmip_reader_find (reader, tag, KMIP_ITEM_TYPE_Structure, &pos, &length)) { return false; } reader->pos = 0; if (pos + compute_padded_length (length) > reader->len) { return false; } reader->ptr = reader->ptr + pos; reader->len = length; return true; } bool kmip_reader_find_and_read_enum (kmip_reader_t *reader, kmip_tag_type_t tag, uint32_t *value) { size_t pos; size_t length; if (!kmip_reader_find (reader, tag, KMIP_ITEM_TYPE_Enumeration, &pos, &length)) { return false; } kmip_reader_t temp_reader; if (!kmip_reader_in_place (reader, pos, length, &temp_reader)) { return false; } return kmip_reader_read_enumeration (&temp_reader, value); } bool kmip_reader_find_and_read_bytes (kmip_reader_t *reader, kmip_tag_type_t tag, uint8_t **out_ptr, size_t *out_len) { size_t pos; if (!kmip_reader_find (reader, tag, KMIP_ITEM_TYPE_ByteString, &pos, out_len)) { return false; } kmip_reader_t temp_reader; if (!kmip_reader_in_place (reader, pos, *out_len, &temp_reader)) { return false; } return kmip_reader_read_bytes (&temp_reader, out_ptr, *out_len); } libmongocrypt-1.19.0/kms-message/src/kms_kmip_reader_writer_private.h000066400000000000000000000104361521103432300261260ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_READER_WRITER_PRIVATE_H #define KMS_KMIP_READER_WRITER_PRIVATE_H #include "kms_kmip_item_type_private.h" #include "kms_kmip_tag_type_private.h" #include "kms_message_private.h" #include typedef struct _kmip_writer_t kmip_writer_t; kmip_writer_t * kmip_writer_new (void); void kmip_writer_destroy (kmip_writer_t *writer); void kmip_writer_write_u8 (kmip_writer_t *writer, uint8_t value); void kmip_writer_write_u16 (kmip_writer_t *writer, uint16_t value); void kmip_writer_write_u32 (kmip_writer_t *writer, uint32_t value); void kmip_writer_write_u64 (kmip_writer_t *writer, uint64_t value); void kmip_writer_write_tag_enum (kmip_writer_t *writer, kmip_tag_type_t tag); void kmip_writer_write_string (kmip_writer_t *writer, kmip_tag_type_t tag, const char *str, size_t len); void kmip_writer_write_bytes (kmip_writer_t *writer, kmip_tag_type_t tag, const char *str, size_t len); void kmip_writer_write_integer (kmip_writer_t *writer, kmip_tag_type_t tag, int32_t value); void kmip_writer_write_long_integer (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value); void kmip_writer_write_enumeration (kmip_writer_t *writer, kmip_tag_type_t tag, int32_t value); void kmip_writer_write_bool (kmip_writer_t *writer, kmip_tag_type_t tag, bool value); void kmip_writer_write_datetime (kmip_writer_t *writer, kmip_tag_type_t tag, int64_t value); void kmip_writer_begin_struct (kmip_writer_t *writer, kmip_tag_type_t tag); void kmip_writer_close_struct (kmip_writer_t *writer); const uint8_t * kmip_writer_get_buffer (kmip_writer_t *writer, size_t* len); typedef struct _kmip_reader_t kmip_reader_t; kmip_reader_t * kmip_reader_new (uint8_t *ptr, size_t len); void kmip_reader_destroy (kmip_reader_t *reader); bool kmip_reader_in_place (kmip_reader_t *reader, size_t pos, size_t len, kmip_reader_t *out_reader); bool kmip_reader_has_data (kmip_reader_t *reader); bool kmip_reader_read_u8 (kmip_reader_t *reader, uint8_t *value); bool kmip_reader_read_u16 (kmip_reader_t *reader, uint16_t *value); bool kmip_reader_read_u32 (kmip_reader_t *reader, uint32_t *value); bool kmip_reader_read_u64 (kmip_reader_t *reader, uint64_t *value); bool kmip_reader_read_tag (kmip_reader_t *reader, kmip_tag_type_t *tag); bool kmip_reader_read_length (kmip_reader_t *reader, uint32_t *length); bool kmip_reader_read_type (kmip_reader_t *reader, kmip_item_type_t *type); bool kmip_reader_read_enumeration (kmip_reader_t *reader, uint32_t *enum_value); bool kmip_reader_read_bool (kmip_reader_t *reader, bool *value); bool kmip_reader_read_integer (kmip_reader_t *reader, int32_t *value); bool kmip_reader_read_long_integer (kmip_reader_t *reader, int64_t *value); bool kmip_reader_read_bytes (kmip_reader_t *reader, uint8_t **ptr, size_t length); bool kmip_reader_read_string (kmip_reader_t *reader, uint8_t **ptr, size_t length); /* kmip_reader_find does not descend structures. * To find and descend into a structure use kmip_reader_find_and_recurse. */ bool kmip_reader_find (kmip_reader_t *reader, kmip_tag_type_t search_tag, kmip_item_type_t type, size_t *pos, size_t *length); bool kmip_reader_find_and_recurse (kmip_reader_t *reader, kmip_tag_type_t tag); bool kmip_reader_find_and_read_enum (kmip_reader_t *reader, kmip_tag_type_t tag, uint32_t *value); bool kmip_reader_find_and_read_bytes (kmip_reader_t *reader, kmip_tag_type_t tag, uint8_t **out_ptr, size_t *out_len); #endif /* KMS_KMIP_READER_WRITER_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kmip_request.c000066400000000000000000000474431521103432300232310ustar00rootroot00000000000000/* * 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 "kms_message/kms_kmip_request.h" #include "kms_kmip_tag_type_private.h" #include "kms_message_private.h" #include "kms_kmip_reader_writer_private.h" #include #include static void copy_writer_buffer (kms_request_t *req, kmip_writer_t *writer) { const uint8_t *buf; size_t buflen; buf = kmip_writer_get_buffer (writer, &buflen); req->kmip.data = malloc (buflen); KMS_ASSERT (req->kmip.data); memcpy (req->kmip.data, buf, buflen); req->kmip.len = (uint32_t) buflen; } kms_request_t * kms_kmip_request_register_secretdata_new (void *reserved, const uint8_t *data, size_t len) { /* Create a KMIP Register request with a 96 byte SecretData of this form: */ kmip_writer_t *writer; kms_request_t *req; req = calloc (1, sizeof (kms_request_t)); KMS_ASSERT (req); req->provider = KMS_REQUEST_PROVIDER_KMIP; if (len != KMS_KMIP_REQUEST_SECRETDATA_LENGTH) { KMS_ERROR (req, "expected SecretData length of %d, got %zu", KMS_KMIP_REQUEST_SECRETDATA_LENGTH, len); return req; } writer = kmip_writer_new (); kmip_writer_begin_struct (writer, KMIP_TAG_RequestMessage); kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader); kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 0); kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem); /* 0x03 == Register */ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x03); kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload); /* 0x07 == SecretData */ kmip_writer_write_enumeration (writer, KMIP_TAG_ObjectType, 0x07); kmip_writer_begin_struct (writer, KMIP_TAG_TemplateAttribute); // Add required Cryptographic Usage Mask attribute. { kmip_writer_begin_struct (writer, KMIP_TAG_Attribute); const char *cryptographicUsageMaskStr = "Cryptographic Usage Mask"; kmip_writer_write_string (writer, KMIP_TAG_AttributeName, cryptographicUsageMaskStr, strlen (cryptographicUsageMaskStr)); // Use 0 because the Secret Data object is not used in cryptographic // operations on the KMIP server. kmip_writer_write_integer (writer, KMIP_TAG_AttributeValue, 0); kmip_writer_close_struct (writer); } kmip_writer_close_struct (writer); /* KMIP_TAG_TemplateAttribute */ kmip_writer_begin_struct (writer, KMIP_TAG_SecretData); /* 0x02 = Seed */ kmip_writer_write_enumeration (writer, KMIP_TAG_SecretDataType, 0x02); kmip_writer_begin_struct (writer, KMIP_TAG_KeyBlock); /* 0x02 = Opaque */ kmip_writer_write_enumeration (writer, KMIP_TAG_KeyFormatType, 0x02); kmip_writer_begin_struct (writer, KMIP_TAG_KeyValue); kmip_writer_write_bytes ( writer, KMIP_TAG_KeyMaterial, (const char *) data, len); kmip_writer_close_struct (writer); /* KMIP_TAG_KeyValue */ kmip_writer_close_struct (writer); /* KMIP_TAG_KeyBlock */ kmip_writer_close_struct (writer); /* KMIP_TAG_SecretData */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */ copy_writer_buffer (req, writer); kmip_writer_destroy (writer); return req; } kms_request_t * kms_kmip_request_activate_new (void *reserved, const char *unique_identifer) { /* Create a KMIP Activate request of this form: */ kmip_writer_t *writer; kms_request_t *req; req = calloc (1, sizeof (kms_request_t)); KMS_ASSERT (req); req->provider = KMS_REQUEST_PROVIDER_KMIP; writer = kmip_writer_new (); kmip_writer_begin_struct (writer, KMIP_TAG_RequestMessage); kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader); kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 0); kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem); /* 0x12 == Activate */ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x12); kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload); kmip_writer_write_string (writer, KMIP_TAG_UniqueIdentifier, unique_identifer, strlen (unique_identifer)); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */ copy_writer_buffer (req, writer); kmip_writer_destroy (writer); return req; } kms_request_t * kms_kmip_request_get_new (void *reserved, const char *unique_identifer) { /* Create a KMIP Get request of this form: */ kmip_writer_t *writer; kms_request_t *req; req = calloc (1, sizeof (kms_request_t)); KMS_ASSERT (req); req->provider = KMS_REQUEST_PROVIDER_KMIP; writer = kmip_writer_new (); kmip_writer_begin_struct (writer, KMIP_TAG_RequestMessage); kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader); kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 0); kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem); /* 0x0A == Get */ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x0A); kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload); kmip_writer_write_string (writer, KMIP_TAG_UniqueIdentifier, unique_identifer, strlen (unique_identifer)); /* 0x01 = Raw */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */ /* Copy the KMIP writer buffer to a KMIP request. */ copy_writer_buffer (req, writer); kmip_writer_destroy (writer); return req; } kms_request_t * kms_kmip_request_create_new (void *reserved) { /* Create a KMIP Create request of this form: */ kmip_writer_t *writer; kms_request_t *req; req = calloc (1, sizeof (kms_request_t)); KMS_ASSERT (req); req->provider = KMS_REQUEST_PROVIDER_KMIP; writer = kmip_writer_new(); kmip_writer_begin_struct(writer, KMIP_TAG_RequestMessage); kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader); kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 2); kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem); /* 0x01 == Create */ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, 0x01); kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload); /* 0x02 == symmetric key */ kmip_writer_write_enumeration(writer, KMIP_TAG_ObjectType, 0x02); { kmip_writer_begin_struct (writer, KMIP_TAG_TemplateAttribute); kmip_writer_begin_struct (writer, KMIP_TAG_Attribute); const char *cryptographicAlgorithmStr = "Cryptographic Algorithm"; kmip_writer_write_string (writer, KMIP_TAG_AttributeName, cryptographicAlgorithmStr, strlen (cryptographicAlgorithmStr)); kmip_writer_write_enumeration (writer, KMIP_TAG_AttributeValue, 3 /* AES */); kmip_writer_close_struct (writer); kmip_writer_begin_struct (writer, KMIP_TAG_Attribute); const char *cryptographicLengthStr = "Cryptographic Length"; kmip_writer_write_string (writer, KMIP_TAG_AttributeName, cryptographicLengthStr, strlen (cryptographicLengthStr)); kmip_writer_write_integer (writer, KMIP_TAG_AttributeValue, 256); kmip_writer_close_struct (writer); kmip_writer_begin_struct (writer, KMIP_TAG_Attribute); const char *cryptographicUsageMaskStr = "Cryptographic Usage Mask"; kmip_writer_write_string (writer, KMIP_TAG_AttributeName, cryptographicUsageMaskStr, strlen (cryptographicUsageMaskStr)); kmip_writer_write_integer (writer, KMIP_TAG_AttributeValue, 4 | 8 /* Encrypt | Decrypt */); kmip_writer_close_struct (writer); kmip_writer_close_struct (writer); /* KMIP_TAG_TemplateAttribute */ } kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */ /* Copy the KMIP writer buffer to a KMIP request. */ copy_writer_buffer (req, writer); kmip_writer_destroy (writer); return req; } static kms_request_t * kmip_encrypt_decrypt (const char* unique_identifer, const uint8_t *data, size_t len, const uint8_t *iv_data, size_t iv_len, bool encrypt) { kmip_writer_t *writer; kms_request_t *req; req = calloc (1, sizeof (kms_request_t)); KMS_ASSERT (req); req->provider = KMS_REQUEST_PROVIDER_KMIP; writer = kmip_writer_new(); kmip_writer_begin_struct(writer, KMIP_TAG_RequestMessage); kmip_writer_begin_struct (writer, KMIP_TAG_RequestHeader); kmip_writer_begin_struct (writer, KMIP_TAG_ProtocolVersion); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMajor, 1); kmip_writer_write_integer (writer, KMIP_TAG_ProtocolVersionMinor, 2); kmip_writer_close_struct (writer); /* KMIP_TAG_ProtocolVersion */ kmip_writer_write_integer (writer, KMIP_TAG_BatchCount, 1); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestHeader */ kmip_writer_begin_struct (writer, KMIP_TAG_BatchItem); /* 0x1F == Encrypt, 0x20 == Decrypt*/ kmip_writer_write_enumeration (writer, KMIP_TAG_Operation, encrypt ? 0x1F : 0x20); kmip_writer_begin_struct (writer, KMIP_TAG_RequestPayload); kmip_writer_write_string (writer, KMIP_TAG_UniqueIdentifier, unique_identifer, strlen (unique_identifer)); kmip_writer_begin_struct (writer, KMIP_TAG_CryptographicParameters); kmip_writer_write_enumeration(writer, KMIP_TAG_BlockCipherMode, 1 /* CBC */); kmip_writer_write_enumeration(writer, KMIP_TAG_PaddingMethod, 3 /* PKCS5 */); kmip_writer_write_enumeration(writer, KMIP_TAG_CryptographicAlgorithm, 3 /* AES */); if (encrypt) kmip_writer_write_bool(writer, KMIP_TAG_RandomIV, true); kmip_writer_close_struct(writer); /* KMIP_TAG_CryptographicParameters */ kmip_writer_write_bytes(writer, KMIP_TAG_Data, (char *) data, len); if (!encrypt) kmip_writer_write_bytes(writer, KMIP_TAG_IVCounterNonce, (char *) iv_data, iv_len); kmip_writer_close_struct (writer); /* KMIP_TAG_RequestPayload */ kmip_writer_close_struct (writer); /* KMIP_TAG_BatchItem */ kmip_writer_close_struct (writer); /* KMIP_TAG_RequestMessage */ /* Copy the KMIP writer buffer to a KMIP request. */ copy_writer_buffer (req, writer); kmip_writer_destroy (writer); return req; } kms_request_t * kms_kmip_request_encrypt_new (void *reserved, const char* unique_identifer, const uint8_t *plaintext, size_t len) { /* Create a KMIP Encrypt request of this form: */ return kmip_encrypt_decrypt(unique_identifer, plaintext, len, NULL, 0, true); } kms_request_t * kms_kmip_request_decrypt_new (void *reserved, const char* unique_identifer, const uint8_t *ciphertext, size_t len, const uint8_t *iv_data, size_t iv_len) { /* Create a KMIP Decrypt request of this form: */ return kmip_encrypt_decrypt(unique_identifer, ciphertext, len, iv_data, iv_len, false); } libmongocrypt-1.19.0/kms-message/src/kms_kmip_response.c000066400000000000000000000362231521103432300233710ustar00rootroot00000000000000#include "kms_message/kms_kmip_response.h" #include "kms_kmip_item_type_private.h" #include "kms_kmip_tag_type_private.h" #include "kms_message_private.h" #include "kms_kmip_reader_writer_private.h" #include "kms_kmip_result_reason_private.h" #include "kms_kmip_result_status_private.h" #include #include #include /* CHAR_BIT */ static bool check_and_require_kmip (kms_response_t *res) { if (res->provider != KMS_REQUEST_PROVIDER_KMIP) { KMS_ERROR (res, "Function requires KMIP request"); return false; } return true; } /* Example of an error message: */ static bool kms_kmip_response_ok (kms_response_t *res) { kmip_reader_t *reader = NULL; size_t pos; size_t len; uint32_t result_status; uint32_t result_reason = 0; const char *result_message = ""; uint32_t result_message_len = 0; bool ok = false; reader = kmip_reader_new (res->kmip.data, res->kmip.len); if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponseMessage)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_BatchItem)); goto fail; } /* Look for optional Result Reason. */ if (kmip_reader_find (reader, KMIP_TAG_ResultReason, KMIP_ITEM_TYPE_Enumeration, &pos, &len)) { if (!kmip_reader_read_enumeration (reader, &result_reason)) { KMS_ERROR (res, "unable to read result reason value"); goto fail; } } /* Look for optional Result Message. */ if (kmip_reader_find (reader, KMIP_TAG_ResultMessage, KMIP_ITEM_TYPE_TextString, &pos, &len)) { if (!kmip_reader_read_string ( reader, (uint8_t **) &result_message, len)) { KMS_ERROR (res, "unable to read result message value"); goto fail; } result_message_len = (uint32_t) len; } /* Look for required Result Status. */ if (!kmip_reader_find (reader, KMIP_TAG_ResultStatus, KMIP_ITEM_TYPE_Enumeration, &pos, &len)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResultStatus)); goto fail; } if (!kmip_reader_read_enumeration (reader, &result_status)) { KMS_ERROR (res, "unable to read result status value"); goto fail; } if (result_status != KMIP_RESULT_STATUS_OperationSuccess) { KMS_ERROR (res, "KMIP response error. Result Status (%" PRIu32 "): %s. Result Reason (%" PRIu32 "): %s. Result Message: %.*s", result_status, kmip_result_status_to_string (result_status), result_reason, kmip_result_reason_to_string (result_reason), result_message_len, result_message); goto fail; } ok = true; fail: kmip_reader_destroy (reader); return ok; } /* Example of a successful response to a Register request: */ char * kms_kmip_response_get_unique_identifier (kms_response_t *res) { kmip_reader_t *reader = NULL; size_t pos; size_t len; char *uid = NULL; kms_request_str_t *nullterminated = NULL; if (!check_and_require_kmip (res)) { goto fail; } if (!kms_kmip_response_ok (res)) { goto fail; } reader = kmip_reader_new (res->kmip.data, res->kmip.len); if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponseMessage)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_BatchItem)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponsePayload)); goto fail; } if (!kmip_reader_find (reader, KMIP_TAG_UniqueIdentifier, KMIP_ITEM_TYPE_TextString, &pos, &len)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_UniqueIdentifier)); goto fail; } if (!kmip_reader_read_string (reader, (uint8_t **) &uid, len)) { KMS_ERROR (res, "unable to read unique identifier"); goto fail; } KMS_ASSERT (len <= SSIZE_MAX); nullterminated = kms_request_str_new_from_chars (uid, (ssize_t) len); fail: kmip_reader_destroy (reader); return kms_request_str_detach (nullterminated); } /* Example of a successful response to an Encrypt request: */ uint8_t * kms_kmip_response_get_iv (kms_response_t *res, size_t *datalen) { kmip_reader_t *reader = NULL; size_t pos; size_t len; uint8_t *data = NULL; uint8_t *tmp; if (!check_and_require_kmip (res)) { goto fail; } if (!kms_kmip_response_ok (res)) { goto fail; } reader = kmip_reader_new (res->kmip.data, res->kmip.len); if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponseMessage)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_BatchItem)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponsePayload)); goto fail; } if (!kmip_reader_find (reader, KMIP_TAG_IVCounterNonce, KMIP_ITEM_TYPE_ByteString, &pos, &len)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_Data)); goto fail; } if (!kmip_reader_read_bytes (reader, &tmp, len)) { KMS_ERROR (res, "unable to read data bytes"); goto fail; } data = malloc (len); KMS_ASSERT (data); memcpy (data, tmp, len); *datalen = len; fail: kmip_reader_destroy (reader); return data; } /* Example of a successful response to a Decrypt request: */ uint8_t * kms_kmip_response_get_data (kms_response_t *res, size_t *datalen) { kmip_reader_t *reader = NULL; size_t pos; size_t len; uint8_t *data = NULL; uint8_t *tmp; if (!check_and_require_kmip (res)) { goto fail; } if (!kms_kmip_response_ok (res)) { goto fail; } reader = kmip_reader_new (res->kmip.data, res->kmip.len); if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponseMessage)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_BatchItem)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponsePayload)); goto fail; } if (!kmip_reader_find (reader, KMIP_TAG_Data, KMIP_ITEM_TYPE_ByteString, &pos, &len)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_Data)); goto fail; } if (!kmip_reader_read_bytes (reader, &tmp, len)) { KMS_ERROR (res, "unable to read data bytes"); goto fail; } data = malloc (len); KMS_ASSERT (data); memcpy (data, tmp, len); *datalen = len; fail: kmip_reader_destroy (reader); return data; } /* Example of a successful response to a Get request: */ uint8_t * kms_kmip_response_get_secretdata (kms_response_t *res, size_t *secretdatalen) { kmip_reader_t *reader = NULL; size_t pos; size_t len; uint8_t *secretdata = NULL; uint8_t *tmp; if (!check_and_require_kmip (res)) { goto fail; } if (!kms_kmip_response_ok (res)) { goto fail; } reader = kmip_reader_new (res->kmip.data, res->kmip.len); if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponseMessage)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponseMessage)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_BatchItem)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_BatchItem)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_ResponsePayload)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_ResponsePayload)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_SecretData)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_SecretData)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_KeyBlock)) { KMS_ERROR ( res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_KeyBlock)); goto fail; } if (!kmip_reader_find_and_recurse (reader, KMIP_TAG_KeyValue)) { KMS_ERROR ( res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_KeyValue)); goto fail; } if (!kmip_reader_find (reader, KMIP_TAG_KeyMaterial, KMIP_ITEM_TYPE_ByteString, &pos, &len)) { KMS_ERROR (res, "unable to find tag: %s", kmip_tag_to_string (KMIP_TAG_KeyMaterial)); goto fail; } if (!kmip_reader_read_bytes (reader, &tmp, len)) { KMS_ERROR (res, "unable to read secretdata bytes"); goto fail; } secretdata = malloc (len); KMS_ASSERT (secretdata); memcpy (secretdata, tmp, len); *secretdatalen = len; fail: kmip_reader_destroy (reader); return secretdata; } libmongocrypt-1.19.0/kms-message/src/kms_kmip_response_parser.c000066400000000000000000000111501521103432300247350ustar00rootroot00000000000000/* * 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 "kms_message/kms_kmip_response_parser.h" #include "kms_endian_private.h" #include "kms_kmip_response_parser_private.h" #include "kms_message_private.h" #include "kms_request_str.h" struct _kms_kmip_response_parser_t { uint32_t first_len; uint32_t bytes_fed; kms_request_str_t *buf; bool failed; char error[512]; }; /* FIRST_LENGTH_OFFSET is the offset of the first Length in a TTLV sequence. The * sequence is: Tag (3 bytes), Type (1 byte), Length (4 bytes), Value (Length * bytes). */ static const uint32_t FIRST_LENGTH_OFFSET = 4; /* _parser_destroy destroys the fields of parser, but not the parser itself. */ static void _parser_destroy (kms_kmip_response_parser_t *parser) { kms_request_str_destroy (parser->buf); } /* _parser_init initializes the members of parser. */ static void _parser_init (kms_kmip_response_parser_t *parser) { memset (parser, 0, sizeof (*parser)); parser->buf = kms_request_str_new (); } kms_response_parser_t * kms_kmip_response_parser_new (void *reserved) { kms_response_parser_t *parser = kms_response_parser_new (); parser->kmip = malloc (sizeof (kms_kmip_response_parser_t)); KMS_ASSERT (parser->kmip); _parser_init (parser->kmip); return parser; } int32_t kms_kmip_response_parser_wants_bytes (const kms_kmip_response_parser_t *parser, int32_t max) { int32_t wants_bytes; uint32_t total_len; uint32_t want_bytes_pending; if (parser->bytes_fed < KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH) { wants_bytes = KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH - (int32_t) parser->bytes_fed; } else { KMS_ASSERT (parser->first_len <= UINT32_MAX - KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH); total_len = parser->first_len + KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH; KMS_ASSERT (total_len >= parser->bytes_fed); want_bytes_pending = total_len - parser->bytes_fed; KMS_ASSERT (want_bytes_pending <= (uint32_t) INT32_MAX); wants_bytes = (int32_t) want_bytes_pending; } if (max < wants_bytes) { return max; } return wants_bytes; } bool kms_kmip_response_parser_feed (kms_kmip_response_parser_t *parser, const uint8_t *buf, uint32_t len) { // As a precaution, limit the maximum size KMS response: if (len > KMS_PARSER_MAX_RESPONSE_LEN || parser->buf->len > KMS_PARSER_MAX_RESPONSE_LEN - len) { KMS_ERROR (parser, "KMS response too large"); return false; } kms_request_str_append_chars (parser->buf, (char *) buf, len); parser->bytes_fed += len; if (parser->first_len > 0) { if (parser->bytes_fed > parser->first_len + KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH) { KMS_ERROR (parser, "KMIP parser was fed too much data"); return false; } } else if (parser->first_len == 0 && parser->bytes_fed >= KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH) { uint32_t temp; memcpy (&temp, parser->buf->str + FIRST_LENGTH_OFFSET, sizeof (uint32_t)); parser->first_len = KMS_UINT32_FROM_BE (temp); } return true; } kms_response_t * kms_kmip_response_parser_get_response (kms_kmip_response_parser_t *parser) { kms_response_t *res; if (kms_kmip_response_parser_wants_bytes (parser, 1) != 0) { KMS_ERROR (parser, "KMIP parser does not have a complete message"); return NULL; } res = calloc (1, sizeof (kms_response_t)); KMS_ASSERT (res); res->provider = KMS_REQUEST_PROVIDER_KMIP; res->kmip.len = (uint32_t) parser->buf->len; res->kmip.data = (uint8_t *) kms_request_str_detach (parser->buf); parser->buf = NULL; /* Reinitialize for reuse. */ _parser_destroy (parser); _parser_init (parser); return res; } const char * kms_kmip_response_parser_error (const kms_kmip_response_parser_t *parser) { return parser->failed ? parser->error : NULL; } void kms_kmip_response_parser_destroy (kms_kmip_response_parser_t *parser) { if (!parser) { return; } _parser_destroy (parser); free (parser); } libmongocrypt-1.19.0/kms-message/src/kms_kmip_response_parser_private.h000066400000000000000000000037031521103432300265010ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_RESPONSE_PARSER_PRIVATE_H #define KMS_KMIP_RESPONSE_PARSER_PRIVATE_H #include "kms_message/kms_response.h" #include #include /* kms_kmip_response_parser_t is a private type used for parsing a KMIP * response. */ typedef struct _kms_kmip_response_parser_t kms_kmip_response_parser_t; /* KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH is the number of bytes needed by the * parser to determine the total length of the message being parsed. * It includes the first Length in a TTLV sequence. The sequence is: Tag (3 * bytes), Type (1 byte), Length (4 bytes), Value (Length bytes). */ #define KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH 8 int32_t kms_kmip_response_parser_wants_bytes (const kms_kmip_response_parser_t *parser, int32_t max); bool kms_kmip_response_parser_feed (kms_kmip_response_parser_t *parser, const uint8_t *buf, uint32_t len); /* kms_kmip_response_parser_get_response returns a kms_response_t and resets the * parser. */ kms_response_t * kms_kmip_response_parser_get_response (kms_kmip_response_parser_t *parser); void kms_kmip_response_parser_destroy (kms_kmip_response_parser_t *parser); const char * kms_kmip_response_parser_error (const kms_kmip_response_parser_t *parser); #endif /* KMS_KMIP_RESPONSE_PARSER_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kmip_result_reason_private.h000066400000000000000000000076571521103432300261700ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_RESULT_REASON_PRIVATE_H #define KMS_KMIP_RESULT_REASON_PRIVATE_H #include "kms_message/kms_message_defines.h" /* clang-format off */ #define KMS_XMACRO \ KMS_X (ItemNotFound, "Item Not Found", 0x00000001) \ KMS_X (ResponseTooLarge, "Response Too Large", 0x00000002) \ KMS_X (AuthenticationNotSuccessful, "Authentication Not Successful", 0x00000003) \ KMS_X (InvalidMessage, "Invalid Message", 0x00000004) \ KMS_X (OperationNotSupported, "Operation Not Supported", 0x00000005) \ KMS_X (MissingData, "Missing Data", 0x00000006) \ KMS_X (InvalidField, "Invalid Field", 0x00000007) \ KMS_X (FeatureNotSupported, "Feature Not Supported", 0x00000008) \ KMS_X (OperationCanceledByRequester, "Operation Canceled By Requester", 0x00000009) \ KMS_X (CryptographicFailure, "Cryptographic Failure", 0x0000000A) \ KMS_X (IllegalOperation, "Illegal Operation", 0x0000000B) \ KMS_X (PermissionDenied, "Permission Denied", 0x0000000C) \ KMS_X (Objectarchived, "Object archived", 0x0000000D) \ KMS_X (IndexOutofBounds, "Index Out of Bounds", 0x0000000E) \ KMS_X (ApplicationNamespaceNotSupported, "Application Namespace Not Supported", 0x0000000F) \ KMS_X (KeyFormatTypeNotSupported, "Key Format Type Not Supported", 0x00000010) \ KMS_X (KeyCompressionTypeNotSupported, "Key Compression Type Not Supported", 0x00000011) \ KMS_X (EncodingOptionError, "Encoding Option Error", 0x00000012) \ KMS_X (KeyValueNotPresent, "Key Value Not Present", 0x00000013) \ KMS_X (AttestationRequired, "Attestation Required", 0x00000014) \ KMS_X (AttestationFailed, "Attestation Failed", 0x00000015) \ KMS_X (Sensitive, "Sensitive", 0x00000016) \ KMS_X (NotExtractable, "Not Extractable", 0x00000017) \ KMS_X (ObjectAlreadyExists, "Object Already Exists", 0x00000018) \ KMS_X_LAST (GeneralFailure, "General Failure", 0x00000100) /* clang-format on */ /* Generate an enum with each result_reason value. */ #define KMS_X(RESULT_REASON, STR, VAL) KMIP_RESULT_REASON_##RESULT_REASON = VAL, #define KMS_X_LAST(RESULT_REASON, STR, VAL) \ KMIP_RESULT_REASON_##RESULT_REASON = VAL typedef enum { KMS_XMACRO } kmip_result_reason_t; #undef KMS_X #undef KMS_X_LAST #define KMS_X(RESULT_REASON, STR, VAL) \ case KMIP_RESULT_REASON_##RESULT_REASON: \ return STR; #define KMS_X_LAST KMS_X static KMS_MSG_INLINE const char * kmip_result_reason_to_string (kmip_result_reason_t result_reason) { switch (result_reason) { default: return "Unknown KMIP result reason"; KMS_XMACRO } } #undef KMS_X #undef KMS_X_LAST #undef KMS_XMACRO #endif /* KMS_KMIP_RESULT_REASON_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kmip_result_status_private.h000066400000000000000000000034071521103432300262110ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_RESULT_STATUS_PRIVATE_H #define KMS_KMIP_RESULT_STATUS_PRIVATE_H #include "kms_message/kms_message_defines.h" #define KMS_XMACRO \ KMS_X (OperationSuccess, "Success", 0x00000000) \ KMS_X (OperationFailed, "Operation Failed", 0x00000001) \ KMS_X (OperationPending, "Operation Pending", 0x00000002) \ KMS_X_LAST (OperationUndone, "Operation Undone", 0x00000003) /* Generate an enum with each result_status value. */ #define KMS_X(RESULT_STATUS, STR, VAL) KMIP_RESULT_STATUS_##RESULT_STATUS = VAL, #define KMS_X_LAST(RESULT_STATUS, STR, VAL) \ KMIP_RESULT_STATUS_##RESULT_STATUS = VAL typedef enum { KMS_XMACRO } kmip_result_status_t; #undef KMS_X #undef KMS_X_LAST #define KMS_X(RESULT_STATUS, STR, VAL) \ case KMIP_RESULT_STATUS_##RESULT_STATUS: \ return STR; #define KMS_X_LAST KMS_X static KMS_MSG_INLINE const char * kmip_result_status_to_string (kmip_result_status_t result_status) { switch (result_status) { default: return "Unknown KMIP result status"; KMS_XMACRO } } #undef KMS_X #undef KMS_X_LAST #undef KMS_XMACRO #endif /* KMS_KMIP_RESULT_STATUS_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kmip_tag_type_private.h000066400000000000000000000703661521103432300251140ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_TAG_TYPE_PRIVATE_H #define KMS_KMIP_TAG_TYPE_PRIVATE_H #include "kms_message/kms_message_defines.h" /* clang-format off */ #define KMS_XMACRO \ KMS_X (ActivationDate, 0x420001) \ KMS_X (ApplicationData, 0x420002) \ KMS_X (ApplicationNamespace, 0x420003) \ KMS_X (ApplicationSpecificInformation, 0x420004) \ KMS_X (ArchiveDate, 0x420005) \ KMS_X (AsynchronousCorrelationValue, 0x420006) \ KMS_X (AsynchronousIndicator, 0x420007) \ KMS_X (Attribute, 0x420008) \ KMS_X (AttributeIndex, 0x420009) \ KMS_X (AttributeName, 0x42000A) \ KMS_X (AttributeValue, 0x42000B) \ KMS_X (Authentication, 0x42000C) \ KMS_X (BatchCount, 0x42000D) \ KMS_X (BatchErrorContinuationOption, 0x42000E) \ KMS_X (BatchItem, 0x42000F) \ KMS_X (BatchOrderOption, 0x420010) \ KMS_X (BlockCipherMode, 0x420011) \ KMS_X (CancellationResult, 0x420012) \ KMS_X (Certificate, 0x420013) \ KMS_X (CertificateIdentifier, 0x420014) /* deprecated as of version 1.1 */ \ KMS_X (CertificateIssuer, 0x420015) /* deprecated as of version 1.1 */ \ KMS_X (CertificateIssuerAlternativeName, 0x420016) /* deprecated as of version 1.1 */ \ KMS_X (CertificateIssuerDistinguishedName, 0x420017) /* deprecated as of version 1.1 */ \ KMS_X (CertificateRequest, 0x420018) \ KMS_X (CertificateRequestType, 0x420019) \ KMS_X (CertificateSubject, 0x42001A) /* deprecated as of version 1.1 */ \ KMS_X (CertificateSubjectAlternativeName, 0x42001B) /* deprecated as of version 1.1 */ \ KMS_X (CertificateSubjectDistinguishedName, 0x42001C) /* deprecated as of version 1.1 */ \ KMS_X (CertificateType, 0x42001D) \ KMS_X (CertificateValue, 0x42001E) \ KMS_X (CommonTemplateAttribute, 0x42001F) \ KMS_X (CompromiseDate, 0x420020) \ KMS_X (CompromiseOccurrenceDate, 0x420021) \ KMS_X (ContactInformation, 0x420022) \ KMS_X (Credential, 0x420023) \ KMS_X (CredentialType, 0x420024) \ KMS_X (CredentialValue, 0x420025) \ KMS_X (CriticalityIndicator, 0x420026) \ KMS_X (CRTCoefficient, 0x420027) \ KMS_X (CryptographicAlgorithm, 0x420028) \ KMS_X (CryptographicDomainParameters, 0x420029) \ KMS_X (CryptographicLength, 0x42002A) \ KMS_X (CryptographicParameters, 0x42002B) \ KMS_X (CryptographicUsageMask, 0x42002C) \ KMS_X (CustomAttribute, 0x42002D) \ KMS_X (D, 0x42002E) \ KMS_X (DeactivationDate, 0x42002F) \ KMS_X (DerivationData, 0x420030) \ KMS_X (DerivationMethod, 0x420031) \ KMS_X (DerivationParameters, 0x420032) \ KMS_X (DestroyDate, 0x420033) \ KMS_X (Digest, 0x420034) \ KMS_X (DigestValue, 0x420035) \ KMS_X (EncryptionKeyInformation, 0x420036) \ KMS_X (G, 0x420037) \ KMS_X (HashingAlgorithm, 0x420038) \ KMS_X (InitialDate, 0x420039) \ KMS_X (InitializationVector, 0x42003A) \ KMS_X (Issuer, 0x42003B) /* deprecated as of version 1.1 */ \ KMS_X (IterationCount, 0x42003C) \ KMS_X (IVCounterNonce, 0x42003D) \ KMS_X (J, 0x42003E) \ KMS_X (Key, 0x42003F) \ KMS_X (KeyBlock, 0x420040) \ KMS_X (KeyCompressionType, 0x420041) \ KMS_X (KeyFormatType, 0x420042) \ KMS_X (KeyMaterial, 0x420043) \ KMS_X (KeyPartIdentifier, 0x420044) \ KMS_X (KeyValue, 0x420045) \ KMS_X (KeyWrappingData, 0x420046) \ KMS_X (KeyWrappingSpecification, 0x420047) \ KMS_X (LastChangeDate, 0x420048) \ KMS_X (LeaseTime, 0x420049) \ KMS_X (Link, 0x42004A) \ KMS_X (LinkType, 0x42004B) \ KMS_X (LinkedObjectIdentifier, 0x42004C) \ KMS_X (MACSignature, 0x42004D) \ KMS_X (MACSignatureKeyInformation, 0x42004E) \ KMS_X (MaximumItems, 0x42004F) \ KMS_X (MaximumResponseSize, 0x420050) \ KMS_X (MessageExtension, 0x420051) \ KMS_X (Modulus, 0x420052) \ KMS_X (Name, 0x420053) \ KMS_X (NameType, 0x420054) \ KMS_X (NameValue, 0x420055) \ KMS_X (ObjectGroup, 0x420056) \ KMS_X (ObjectType, 0x420057) \ KMS_X (Offset, 0x420058) \ KMS_X (OpaqueDataType, 0x420059) \ KMS_X (OpaqueDataValue, 0x42005A) \ KMS_X (OpaqueObject, 0x42005B) \ KMS_X (Operation, 0x42005C) \ KMS_X (OperationPolicyName, 0x42005D) /* deprecated */ \ KMS_X (P, 0x42005E) \ KMS_X (PaddingMethod, 0x42005F) \ KMS_X (PrimeExponentP, 0x420060) \ KMS_X (PrimeExponentQ, 0x420061) \ KMS_X (PrimeFieldSize, 0x420062) \ KMS_X (PrivateExponent, 0x420063) \ KMS_X (PrivateKey, 0x420064) \ KMS_X (PrivateKeyTemplateAttribute, 0x420065) \ KMS_X (PrivateKeyUniqueIdentifier, 0x420066) \ KMS_X (ProcessStartDate, 0x420067) \ KMS_X (ProtectStopDate, 0x420068) \ KMS_X (ProtocolVersion, 0x420069) \ KMS_X (ProtocolVersionMajor, 0x42006A) \ KMS_X (ProtocolVersionMinor, 0x42006B) \ KMS_X (PublicExponent, 0x42006C) \ KMS_X (PublicKey, 0x42006D) \ KMS_X (PublicKeyTemplateAttribute, 0x42006E) \ KMS_X (PublicKeyUniqueIdentifier, 0x42006F) \ KMS_X (PutFunction, 0x420070) \ KMS_X (Q, 0x420071) \ KMS_X (QString, 0x420072) \ KMS_X (Qlength, 0x420073) \ KMS_X (QueryFunction, 0x420074) \ KMS_X (RecommendedCurve, 0x420075) \ KMS_X (ReplacedUniqueIdentifier, 0x420076) \ KMS_X (RequestHeader, 0x420077) \ KMS_X (RequestMessage, 0x420078) \ KMS_X (RequestPayload, 0x420079) \ KMS_X (ResponseHeader, 0x42007A) \ KMS_X (ResponseMessage, 0x42007B) \ KMS_X (ResponsePayload, 0x42007C) \ KMS_X (ResultMessage, 0x42007D) \ KMS_X (ResultReason, 0x42007E) \ KMS_X (ResultStatus, 0x42007F) \ KMS_X (RevocationMessage, 0x420080) \ KMS_X (RevocationReason, 0x420081) \ KMS_X (RevocationReasonCode, 0x420082) \ KMS_X (KeyRoleType, 0x420083) \ KMS_X (Salt, 0x420084) \ KMS_X (SecretData, 0x420085) \ KMS_X (SecretDataType, 0x420086) \ KMS_X (SerialNumber, 0x420087) /* deprecated as of version 1.1 */ \ KMS_X (ServerInformation, 0x420088) \ KMS_X (SplitKey, 0x420089) \ KMS_X (SplitKeyMethod, 0x42008A) \ KMS_X (SplitKeyParts, 0x42008B) \ KMS_X (SplitKeyThreshold, 0x42008C) \ KMS_X (State, 0x42008D) \ KMS_X (StorageStatusMask, 0x42008E) \ KMS_X (SymmetricKey, 0x42008F) \ KMS_X (Template, 0x420090) \ KMS_X (TemplateAttribute, 0x420091) \ KMS_X (TimeStamp, 0x420092) \ KMS_X (UniqueBatchItemID, 0x420093) \ KMS_X (UniqueIdentifier, 0x420094) \ KMS_X (UsageLimits, 0x420095) \ KMS_X (UsageLimitsCount, 0x420096) \ KMS_X (UsageLimitsTotal, 0x420097) \ KMS_X (UsageLimitsUnit, 0x420098) \ KMS_X (Username, 0x420099) \ KMS_X (ValidityDate, 0x42009A) \ KMS_X (ValidityIndicator, 0x42009B) \ KMS_X (VendorExtension, 0x42009C) \ KMS_X (VendorIdentification, 0x42009D) \ KMS_X (WrappingMethod, 0x42009E) \ KMS_X (X, 0x42009F) \ KMS_X (Y, 0x4200A0) \ KMS_X (Password, 0x4200A1) \ KMS_X (DeviceIdentifier, 0x4200A2) \ KMS_X (EncodingOption, 0x4200A3) \ KMS_X (ExtensionInformation, 0x4200A4) \ KMS_X (ExtensionName, 0x4200A5) \ KMS_X (ExtensionTag, 0x4200A6) \ KMS_X (ExtensionType, 0x4200A7) \ KMS_X (Fresh, 0x4200A8) \ KMS_X (MachineIdentifier, 0x4200A9) \ KMS_X (MediaIdentifier, 0x4200AA) \ KMS_X (NetworkIdentifier, 0x4200AB) \ KMS_X (ObjectGroupMember, 0x4200AC) \ KMS_X (CertificateLength, 0x4200AD) \ KMS_X (DigitalSignatureAlgorithm, 0x4200AE) \ KMS_X (CertificateSerialNumber, 0x4200AF) \ KMS_X (DeviceSerialNumber, 0x4200B0) \ KMS_X (IssuerAlternativeName, 0x4200B1) \ KMS_X (IssuerDistinguishedName, 0x4200B2) \ KMS_X (SubjectAlternativeName, 0x4200B3) \ KMS_X (SubjectDistinguishedName, 0x4200B4) \ KMS_X (X509CertificateIdentifier, 0x4200B5) \ KMS_X (X509CertificateIssuer, 0x4200B6) \ KMS_X (X509CertificateSubject, 0x4200B7) \ KMS_X (KeyValueLocation, 0x4200B8) \ KMS_X (KeyValueLocationValue, 0x4200B9) \ KMS_X (KeyValueLocationType, 0x4200BA) \ KMS_X (KeyValuePresent, 0x4200BB) \ KMS_X (OriginalCreationDate, 0x4200BC) \ KMS_X (PGPKey, 0x4200BD) \ KMS_X (PGPKeyVersion, 0x4200BE) \ KMS_X (AlternativeName, 0x4200BF) \ KMS_X (AlternativeNameValue, 0x4200C0) \ KMS_X (AlternativeNameType, 0x4200C1) \ KMS_X (Data, 0x4200C2) \ KMS_X (SignatureData, 0x4200C3) \ KMS_X (DataLength, 0x4200C4) \ KMS_X (RandomIV, 0x4200C5) \ KMS_X (MACData, 0x4200C6) \ KMS_X (AttestationType, 0x4200C7) \ KMS_X (Nonce, 0x4200C8) \ KMS_X (NonceID, 0x4200C9) \ KMS_X (NonceValue, 0x4200CA) \ KMS_X (AttestationMeasurement, 0x4200CB) \ KMS_X (AttestationAssertion, 0x4200CC) \ KMS_X (IVLength, 0x4200CD) \ KMS_X (TagLength, 0x4200CE) \ KMS_X (FixedFieldLength, 0x4200CF) \ KMS_X (CounterLength, 0x4200D0) \ KMS_X (InitialCounterValue, 0x4200D1) \ KMS_X (InvocationFieldLength, 0x4200D2) \ KMS_X (AttestationCapableIndicator, 0x4200D3) \ KMS_X (OffsetItems, 0x4200D4) \ KMS_X (LocatedItems, 0x4200D5) \ KMS_X (CorrelationValue, 0x4200D6) \ KMS_X (InitIndicator, 0x4200D7) \ KMS_X (FinalIndicator, 0x4200D8) \ KMS_X (RNGParameters, 0x4200D9) \ KMS_X (RNGAlgorithm, 0x4200DA) \ KMS_X (DRBGAlgorithm, 0x4200DB) \ KMS_X (FIPS186Variation, 0x4200DC) \ KMS_X (PredictionResistance, 0x4200DD) \ KMS_X (RandomNumberGenerator, 0x4200DE) \ KMS_X (ValidationInformation, 0x4200DF) \ KMS_X (ValidationAuthorityType, 0x4200E0) \ KMS_X (ValidationAuthorityCountry, 0x4200E1) \ KMS_X (ValidationAuthorityURI, 0x4200E2) \ KMS_X (ValidationVersionMajor, 0x4200E3) \ KMS_X (ValidationVersionMinor, 0x4200E4) \ KMS_X (ValidationType, 0x4200E5) \ KMS_X (ValidationLevel, 0x4200E6) \ KMS_X (ValidationCertificateIdentifier, 0x4200E7) \ KMS_X (ValidationCertificateURI, 0x4200E8) \ KMS_X (ValidationVendorURI, 0x4200E9) \ KMS_X (ValidationProfile, 0x4200EA) \ KMS_X (ProfileInformation, 0x4200EB) \ KMS_X (ProfileName, 0x4200EC) \ KMS_X (ServerURI, 0x4200ED) \ KMS_X (ServerPort, 0x4200EE) \ KMS_X (StreamingCapability, 0x4200EF) \ KMS_X (AsynchronousCapability, 0x4200F0) \ KMS_X (AttestationCapability, 0x4200F1) \ KMS_X (UnwrapMode, 0x4200F2) \ KMS_X (DestroyAction, 0x4200F3) \ KMS_X (ShreddingAlgorithm, 0x4200F4) \ KMS_X (RNGMode, 0x4200F5) \ KMS_X (ClientRegistrationMethod, 0x4200F6) \ KMS_X (CapabilityInformation, 0x4200F7) \ KMS_X (KeyWrapType, 0x4200F8) \ KMS_X (BatchUndoCapability, 0x4200F9) \ KMS_X (BatchContinueCapability, 0x4200FA) \ KMS_X (PKCS12FriendlyName, 0x4200FB) \ KMS_X (Description, 0x4200FC) \ KMS_X (Comment, 0x4200FD) \ KMS_X (AuthenticatedEncryptionAdditionalData, 0x4200FE) \ KMS_X (AuthenticatedEncryptionTag, 0x4200FF) \ KMS_X (SaltLength, 0x420100) \ KMS_X (MaskGenerator, 0x420101) \ KMS_X (MaskGeneratorHashingAlgorithm, 0x420102) \ KMS_X (PSource, 0x420103) \ KMS_X (TrailerField, 0x420104) \ KMS_X (ClientCorrelationValue, 0x420105) \ KMS_X (ServerCorrelationValue, 0x420106) \ KMS_X (DigestedData, 0x420107) \ KMS_X (CertificateSubjectCN, 0x420108) \ KMS_X (CertificateSubjectO, 0x420109) \ KMS_X (CertificateSubjectOU, 0x42010A) \ KMS_X (CertificateSubjectEmail, 0x42010B) \ KMS_X (CertificateSubjectC, 0x42010C) \ KMS_X (CertificateSubjectST, 0x42010D) \ KMS_X (CertificateSubjectL, 0x42010E) \ KMS_X (CertificateSubjectUID, 0x42010F) \ KMS_X (CertificateSubjectSerialNumber, 0x420110) \ KMS_X (CertificateSubjectTitle, 0x420111) \ KMS_X (CertificateSubjectDC, 0x420112) \ KMS_X (CertificateSubjectDNQualifier, 0x420113) \ KMS_X (CertificateIssuerCN, 0x420114) \ KMS_X (CertificateIssuerO, 0x420115) \ KMS_X (CertificateIssuerOU, 0x420116) \ KMS_X (CertificateIssuerEmail, 0x420117) \ KMS_X (CertificateIssuerC, 0x420118) \ KMS_X (CertificateIssuerST, 0x420119) \ KMS_X (CertificateIssuerL, 0x42011A) \ KMS_X (CertificateIssuerUID, 0x42011B) \ KMS_X (CertificateIssuerSerialNumber, 0x42011C) \ KMS_X (CertificateIssuerTitle, 0x42011D) \ KMS_X (CertificateIssuerDC, 0x42011E) \ KMS_X (CertificateIssuerDNQualifier, 0x42011F) \ KMS_X (Sensitive, 0x420120) \ KMS_X (AlwaysSensitive, 0x420121) \ KMS_X (Extractable, 0x420122) \ KMS_X (NeverExtractable, 0x420123) \ KMS_X (ReplaceExisting, 0x420124) \ KMS_X_LAST (Attributes, 0x420125) /* clang-format on */ /* Generate an enum with each tag value. */ #define KMS_X(TAG, VAL) KMIP_TAG_##TAG = VAL, #define KMS_X_LAST(TAG, VAL) KMIP_TAG_##TAG = VAL typedef enum { KMS_XMACRO } kmip_tag_type_t; #undef KMS_X #undef KMS_X_LAST #define KMS_X(TAG, VAL) \ case KMIP_TAG_##TAG: \ return #TAG; #define KMS_X_LAST KMS_X static KMS_MSG_INLINE const char * kmip_tag_to_string (kmip_tag_type_t tag) { switch (tag) { default: return "Unknown KMIP tag"; KMS_XMACRO } } #undef KMS_X #undef KMS_X_LAST #undef KMS_XMACRO #endif /* KMS_KMIP_TAG_TYPE_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_kv_list.c000066400000000000000000000062741521103432300221710ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_kv_list.h" #include "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_request_str.h" #include "kms_port.h" #include "sort.h" static void kv_init (kms_kv_t *kv, kms_request_str_t *key, kms_request_str_t *value) { kv->key = kms_request_str_dup (key); kv->value = kms_request_str_dup (value); } static void kv_cleanup (kms_kv_t *kv) { kms_request_str_destroy (kv->key); kms_request_str_destroy (kv->value); } kms_kv_list_t * kms_kv_list_new (void) { kms_kv_list_t *lst = malloc (sizeof (kms_kv_list_t)); KMS_ASSERT (lst); lst->size = 16; lst->kvs = malloc (lst->size * sizeof (kms_kv_t)); KMS_ASSERT (lst->kvs); lst->len = 0; return lst; } void kms_kv_list_destroy (kms_kv_list_t *lst) { size_t i; if (!lst) { return; } for (i = 0; i < lst->len; i++) { kv_cleanup (&lst->kvs[i]); } free (lst->kvs); free (lst); } void kms_kv_list_add (kms_kv_list_t *lst, kms_request_str_t *key, kms_request_str_t *value) { if (lst->len == lst->size) { lst->size *= 2; lst->kvs = realloc (lst->kvs, lst->size * sizeof (kms_kv_t)); KMS_ASSERT (lst->kvs); } kv_init (&lst->kvs[lst->len], key, value); ++lst->len; } const kms_kv_t * kms_kv_list_find (const kms_kv_list_t *lst, const char *key) { size_t i; for (i = 0; i < lst->len; i++) { if (0 == kms_strcasecmp (lst->kvs[i].key->str, key)) { return &lst->kvs[i]; } } return NULL; } void kms_kv_list_del (kms_kv_list_t *lst, const char *key) { size_t i; for (i = 0; i < lst->len; i++) { if (0 == strcmp (lst->kvs[i].key->str, key)) { kv_cleanup (&lst->kvs[i]); memmove (&lst->kvs[i], &lst->kvs[i + 1], sizeof (kms_kv_t) * (lst->len - i - 1)); lst->len--; } } } kms_kv_list_t * kms_kv_list_dup (const kms_kv_list_t *lst) { kms_kv_list_t *dup; size_t i; if (lst->len == 0) { return kms_kv_list_new (); } dup = malloc (sizeof (kms_kv_list_t)); KMS_ASSERT (dup); dup->size = dup->len = lst->len; dup->kvs = malloc (lst->len * sizeof (kms_kv_t)); KMS_ASSERT (dup->kvs); for (i = 0; i < lst->len; i++) { kv_init (&dup->kvs[i], lst->kvs[i].key, lst->kvs[i].value); } return dup; } void kms_kv_list_sort (kms_kv_list_t *lst, int (*cmp) (const void *, const void *)) { /* A stable sort is required to sort headers when creating canonical * requests. qsort is not stable. */ insertionsort ( (unsigned char *) (lst->kvs), lst->len, sizeof (kms_kv_t), cmp); } libmongocrypt-1.19.0/kms-message/src/kms_kv_list.h000066400000000000000000000027271521103432300221750ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_KV_LIST_H #define KMS_KV_LIST_H #include "kms_message/kms_message.h" #include "kms_request_str.h" #include #include #include /* key-value pair */ typedef struct { kms_request_str_t *key; kms_request_str_t *value; } kms_kv_t; typedef struct { kms_kv_t *kvs; size_t len; size_t size; } kms_kv_list_t; kms_kv_list_t * kms_kv_list_new (void); void kms_kv_list_destroy (kms_kv_list_t *lst); void kms_kv_list_add (kms_kv_list_t *lst, kms_request_str_t *key, kms_request_str_t *value); const kms_kv_t * kms_kv_list_find (const kms_kv_list_t *lst, const char *key); void kms_kv_list_del (kms_kv_list_t *lst, const char *key); kms_kv_list_t * kms_kv_list_dup (const kms_kv_list_t *lst); void kms_kv_list_sort (kms_kv_list_t *lst, int (*cmp) (const void *, const void *)); #endif /* KMS_KV_LIST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message.c000066400000000000000000000021301521103432300221250ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_message/kms_b64.h" #include "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_crypto.h" #include #include void kms_set_error (char *error, size_t size, const char *fmt, ...) { va_list va; va_start (va, fmt); (void) vsnprintf (error, size, fmt, va); va_end (va); } int kms_message_init (void) { kms_message_b64_initialize_rmap (); return kms_crypto_init (); } void kms_message_cleanup (void) { kms_crypto_cleanup (); } libmongocrypt-1.19.0/kms-message/src/kms_message/000077500000000000000000000000001521103432300217655ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/src/kms_message/kms_azure_request.h000066400000000000000000000104021521103432300257030ustar00rootroot00000000000000/* * Copyright 2020-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. */ #ifndef KMS_AZURE_REQUEST_H #define KMS_AZURE_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include "kms_request_opt.h" #ifdef __cplusplus extern "C" { #endif /* Constructs an oauth client credentials grant request for Azure. * See * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#get-a-token. * * Parameters: * All parameters must be NULL terminated strings. * - host: The value of the Host header. This should be a custom host or * "login.microsoftonline.com". * - scope: The oauth scope. This should be a custom scope or * "https%3A%2F%2Fvault.azure.net%2F.default". Must be URL encoded. * - tenant_id: The Azure tenant ID. * - client_id: The client ID to authenticate. * - client_secret: The client secret to authenticate. * - opt: Additional options. This must have the Azure provider set via * kms_request_opt_set_provider. * * Returns: A new kms_request_t. * Always returns a new kms_request_t, even on error. * Caller must check if an error occurred by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_azure_request_oauth_new (const char *host, const char *scope, const char *tenant_id, const char *client_id, const char *client_secret, const kms_request_opt_t *opt); /* Constructs a wrapkey request for Azure. * See https://docs.microsoft.com/en-us/rest/api/keyvault/wrapkey/wrapkey * * Parameters: * All parameters must be NULL terminated strings. * - host: The value of the Host header, like "mykeyvault.vault.azure.net". * - access_token: The access_token obtained from an oauth response as a * base64url encoded string. * - key_name: The azure key name. * - key_version: An optional key version. May be NULL or empty string. * - plaintext: The plaintext key to encrypt. * - plaintext_len: The number of bytes of plaintext. * - opt: Additional options. This must have the Azure provider set via * kms_request_opt_set_provider. */ KMS_MSG_EXPORT (kms_request_t *) kms_azure_request_wrapkey_new (const char *host, const char *access_token, const char *key_name, const char *key_version, const uint8_t *plaintext, size_t plaintext_len, const kms_request_opt_t *opt); /* Constructs an unwrapkey request for Azure. * See https://docs.microsoft.com/en-us/rest/api/keyvault/unwrapkey/unwrapkey * * Parameters: * All parameters must be NULL terminated strings. * - host: The value of the Host header, like "mykeyvault.vault.azure.net". * - access_token: The access_token obtained from an oauth response as a * base64url encoded string. * - key_name: The azure key name. * - key_version: An optional key version. May be NULL or empty string. * - ciphertext: The ciphertext key to decrypt. * - ciphertext_len: The number of bytes of ciphertext. * - opt: Additional options. This must have the Azure provider set via * kms_request_opt_set_provider. */ KMS_MSG_EXPORT (kms_request_t *) kms_azure_request_unwrapkey_new (const char *host, const char *access_token, const char *key_name, const char *key_version, const uint8_t *ciphertext, size_t ciphertext_len, const kms_request_opt_t *opt); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_AZURE_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_b64.h000066400000000000000000000036711521103432300234120ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_B64_H #define KMS_MESSAGE_B64_H #include "kms_message.h" #include #include #ifdef __cplusplus extern "C" { #endif KMS_MSG_EXPORT (void) kms_message_b64_initialize_rmap (void); KMS_MSG_EXPORT (int) kms_message_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t targsize); KMS_MSG_EXPORT (int) kms_message_b64_pton (char const *src, uint8_t *target, size_t targsize); /* src and target may be the same string. Assumes no whitespace in src. */ KMS_MSG_EXPORT (int) kms_message_b64_to_b64url (const char *src, size_t srclength, char *target, size_t targsize); KMS_MSG_EXPORT (int) kms_message_b64url_to_b64 (const char *src, size_t srclength, char *target, size_t targsize); /* Convenience conversions which return copies. */ char * kms_message_raw_to_b64 (const uint8_t *raw, size_t raw_len); uint8_t * kms_message_b64_to_raw (const char *b64, size_t *out); char * kms_message_raw_to_b64url (const uint8_t *raw, size_t raw_len); uint8_t * kms_message_b64url_to_raw (const char *b64url, size_t *out); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_MESSAGE_B64_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_caller_identity_request.h000066400000000000000000000017451521103432300277420ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef KMS_CALLER_IDENTITY_REQUEST_H #define KMS_CALLER_IDENTITY_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include "kms_request_opt.h" #ifdef __cplusplus extern "C" { #endif KMS_MSG_EXPORT (kms_request_t *) kms_caller_identity_request_new (const kms_request_opt_t *opt); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_CALLER_IDENTITY_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_decrypt_request.h000066400000000000000000000020421521103432300262300ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_DECRYPT_REQUEST_H #define KMS_DECRYPT_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include "kms_request_opt.h" #ifdef __cplusplus extern "C" { #endif KMS_MSG_EXPORT (kms_request_t *) kms_decrypt_request_new (const uint8_t *ciphertext_blob, size_t len, const kms_request_opt_t *opt); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_DECRYPT_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_encrypt_request.h000066400000000000000000000021271521103432300262460ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_ENCRYPT_REQUEST_H #define KMS_ENCRYPT_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include "kms_request_opt.h" #ifdef __cplusplus extern "C" { #endif KMS_MSG_EXPORT (kms_request_t *) kms_encrypt_request_new (const uint8_t *plaintext, size_t plaintext_length, const char *key_id, const kms_request_opt_t *opt); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_ENCRYPT_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_gcp_request.h000066400000000000000000000116221521103432300253330ustar00rootroot00000000000000/* * Copyright 2020-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. */ #ifndef KMS_GCP_REQUEST_H #define KMS_GCP_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include "kms_request_opt.h" #ifdef __cplusplus extern "C" { #endif /* Constructs an oauth client credentials request for GCP. * See https://developers.google.com/identity/protocols/oauth2/service-account * * Parameters: * - host: The host header, like "oauth2.googleapis.com". * - email: The email for the service account to authenticate. * - audience: The "aud" field in the JSON Web Token (JWT). Should be a URL * like "https://oauth2.googleapis.com/token" * - scope: The "scope" field in the JSON Web Token (JWT). Should be a URL * like "https://www.googleapis.com/auth/cloudkms". * - private_key_data: Bytes pointing to a PKCS#8 private key. * - private_key_len: The length of private_key_data. * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP * with kms_request_opt_set_provider. Callers that want to use a custom crypto * callback to sign the request should set the callback on opt with * kms_request_opt_set_crypto_hook_rsaes_pkcs1_v1_5. * * Returns: A new kms_request_t. * Always returns a new kms_request_t, even on error. * Caller must check if an error occurred by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_gcp_request_oauth_new (const char *host, const char *email, const char *audience, const char *scope, const char *private_key_data, size_t private_key_len, const kms_request_opt_t *opt); /* Constructs the encrypt request for GCP. * See * https://cloud.google.com/kms/docs/encrypt-decrypt#kms-encrypt-symmetric-api * * Parameters: * - host: The value of the Host header, like "cloudkms.googleapis.com". * - project_id: The project id. * - location: The location id, like "global". * - key_ring_name: The key ring name. * - key_name: The key name. * - key_version: The optional key version. May be NULL. * - plaintext: The plaintext key to encrypt. * - plaintext_len: The number of bytes of plaintext. * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP * with kms_request_opt_set_provider. * * Returns: A new kms_request_t. * Always returns a new kms_request_t, even on error. * Caller must check if an error occurred by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_gcp_request_encrypt_new (const char *host, const char *access_token, const char *project_id, const char *location, const char *key_ring_name, const char *key_name, const char *key_version, const uint8_t *plaintext, size_t plaintext_len, const kms_request_opt_t *opt); /* Constructs the decrypt request for GCP. * See * https://cloud.google.com/kms/docs/encrypt-decrypt#kms-decrypt-symmetric-api * * Parameters: * - host: The value of the Host header, like "cloudkms.googleapis.com". * - project_id: The project id. * - location: The location id, like "global". * - key_ring_name: The key ring name. * - key_name: The key name. * - ciphertext: The ciphertext key to encrypt. * - ciphertext_len: The number of bytes of ciphertext. * - opt: Request options. The provider must be set to KMS_REQUEST_PROVIDER_GCP * with kms_request_opt_set_provider. * * Returns: A new kms_request_t. * Always returns a new kms_request_t, even on error. * Caller must check if an error occurred by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_gcp_request_decrypt_new (const char *host, const char *access_token, const char *project_id, const char *location, const char *key_ring_name, const char *key_name, const uint8_t *ciphertext, size_t ciphertext_len, const kms_request_opt_t *opt); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_GCP_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_kmip_request.h000066400000000000000000000051671521103432300255310ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_REQUEST_H #define KMS_KMIP_REQUEST_H #include "kms_message_defines.h" #include "kms_request.h" #include #ifdef __cplusplus extern "C" { #endif #define KMS_KMIP_REQUEST_SECRETDATA_LENGTH 96 /* kms_kmip_request_register_secretdata_new creates a KMIP Register request with * a SecretData payload of length KMS_KMIP_REQUEST_SECRETDATA_LENGTH. * - len must be KMS_KMIP_REQUEST_SECRETDATA_LENGTH. * - Callers must check for an error by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_register_secretdata_new (void *reserved, const uint8_t *data, size_t len); /* kms_kmip_request_activate_new creates a KMIP Activate request with the * provided unique identifer. * - unique_identifier must be a NULL terminated string. * - Callers must check for an error by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_activate_new (void *reserved, const char *unique_identifier); /* kms_kmip_request_get_new creates a KMIP Get request with the provided unique * identifer. * - unique_identifier must be a NULL terminated string. * - Callers must check for an error by calling kms_request_get_error. */ KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_get_new (void *reserved, const char *unique_identifier); KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_create_new (void *reserved); KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_encrypt_new (void *reserved, const char *unique_identifier, const uint8_t *plaintext, size_t len); KMS_MSG_EXPORT (kms_request_t *) kms_kmip_request_decrypt_new (void *reserved, const char *unique_identifier, const uint8_t *ciphertext, size_t len, const uint8_t *iv, size_t iv_len); #ifdef __cplusplus } #endif #endif /* KMS_KMIP_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_kmip_response.h000066400000000000000000000031271521103432300256710ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_RESPONSE_H #define KMS_KMIP_RESPONSE_H #include "kms_message_defines.h" #include #include "kms_response.h" /* kms_kmip_response_get_unique_identifier returns the UniqueIdentifier in the * first BatchItem in a ResponseMessage. * - Returns a NULL terminated string that the caller must free. * - Returns NULL on error and sets an error on kms_response_t. */ KMS_MSG_EXPORT (char *) kms_kmip_response_get_unique_identifier (kms_response_t *res); /* kms_kmip_response_get_secretdata returns the KeyMaterial in the * first BatchItem in a ResponseMessage. * - Caller must free returned data. * - Returns NULL on error and sets an error on kms_response_t. */ KMS_MSG_EXPORT (uint8_t *) kms_kmip_response_get_secretdata (kms_response_t *res, size_t *secretdatalen); KMS_MSG_EXPORT (uint8_t *) kms_kmip_response_get_data (kms_response_t *res, size_t *datalen); KMS_MSG_EXPORT (uint8_t *) kms_kmip_response_get_iv (kms_response_t *res, size_t *datalen); #endif /* KMS_KMIP_RESPONSE_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_kmip_response_parser.h000066400000000000000000000015471521103432300272510ustar00rootroot00000000000000/* * 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. */ #ifndef KMS_KMIP_RESPONSE_PARSER_H #define KMS_KMIP_RESPONSE_PARSER_H #include "kms_message_defines.h" #include "kms_response_parser.h" KMS_MSG_EXPORT (kms_response_parser_t *) kms_kmip_response_parser_new (void *reserved); #endif /* KMS_KMIP_RESPONSE_PARSER_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_message.h000066400000000000000000000020351521103432300244340ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_H #define KMS_MESSAGE_H #include #include "kms_message_defines.h" #include "kms_kmip_request.h" #include "kms_kmip_response.h" #include "kms_kmip_response_parser.h" #include "kms_request_opt.h" #include "kms_request.h" #include "kms_response.h" #include "kms_response_parser.h" #include "kms_caller_identity_request.h" #include "kms_decrypt_request.h" #include "kms_encrypt_request.h" #endif /* KMS_MESSAGE_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_message_defines.h000066400000000000000000000032201521103432300261260ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_DEFINES_H #define KMS_MESSAGE_DEFINES_H #ifdef _MSC_VER #ifdef KMS_MSG_STATIC #define KMS_MSG_API #elif defined(KMS_MSG_COMPILATION) #define KMS_MSG_API __declspec(dllexport) #else #define KMS_MSG_API __declspec(dllimport) #endif #define KMS_MSG_CALL __cdecl #elif defined(__GNUC__) #ifdef KMS_MSG_STATIC #define KMS_MSG_API #elif defined(KMS_MSG_COMPILATION) #define KMS_MSG_API __attribute__ ((visibility ("default"))) #else #define KMS_MSG_API #endif #define KMS_MSG_CALL #endif #define KMS_MSG_EXPORT(type) KMS_MSG_API type KMS_MSG_CALL #ifdef __cplusplus extern "C" { #endif KMS_MSG_EXPORT (int) kms_message_init (void); KMS_MSG_EXPORT (void) kms_message_cleanup (void); #ifdef __cplusplus } /* extern "C" */ #endif #ifdef _MSC_VER #include #pragma warning(disable : 4142) #ifndef _SSIZE_T_DEFINED #define _SSIZE_T_DEFINED typedef SSIZE_T ssize_t; #endif #pragma warning(default : 4142) #endif #if defined(_MSC_VER) #define KMS_MSG_INLINE __inline #else #define KMS_MSG_INLINE __inline__ #endif #endif /* KMS_MESSAGE_DEFINES_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_request.h000066400000000000000000000065151521103432300245070ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_REQUEST_H #define KMS_REQUEST_H #include "kms_message_defines.h" #include "kms_request_opt.h" #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* A KMS request is general enough to create arbitrary HTTP requests, but also * supports generating AWS signature v4. */ typedef struct _kms_request_t kms_request_t; KMS_MSG_EXPORT (kms_request_t *) kms_request_new (const char *method, const char *path_and_query, const kms_request_opt_t *opt); KMS_MSG_EXPORT (void) kms_request_destroy (kms_request_t *request); KMS_MSG_EXPORT (const char *) kms_request_get_error (kms_request_t *request); /* Begin: AWS specific */ KMS_MSG_EXPORT (bool) kms_request_set_date (kms_request_t *request, const struct tm *tm); KMS_MSG_EXPORT (bool) kms_request_set_region (kms_request_t *request, const char *region); KMS_MSG_EXPORT (bool) kms_request_set_service (kms_request_t *request, const char *service); KMS_MSG_EXPORT (bool) kms_request_set_access_key_id (kms_request_t *request, const char *akid); KMS_MSG_EXPORT (bool) kms_request_set_secret_key (kms_request_t *request, const char *key); /* End: AWS specific */ KMS_MSG_EXPORT (bool) kms_request_add_header_field (kms_request_t *request, const char *field_name, const char *value); KMS_MSG_EXPORT (bool) kms_request_append_header_field_value (kms_request_t *request, const char *value, size_t len); KMS_MSG_EXPORT (bool) kms_request_append_payload (kms_request_t *request, const char *payload, size_t len); /* Begin: AWS specific */ KMS_MSG_EXPORT (char *) kms_request_get_canonical (kms_request_t *request); KMS_MSG_EXPORT (const char *) kms_request_get_canonical_header (kms_request_t *request, const char *header); KMS_MSG_EXPORT (char *) kms_request_get_string_to_sign (kms_request_t *request); KMS_MSG_EXPORT (bool) kms_request_get_signing_key (kms_request_t *request, unsigned char *key); KMS_MSG_EXPORT (char *) kms_request_get_signature (kms_request_t *request); KMS_MSG_EXPORT (char *) kms_request_get_signed (kms_request_t *request); /* End: AWS specific */ KMS_MSG_EXPORT (void) kms_request_free_string (char *ptr); /* Finalize and obtain a plain HTTP request (no signing). */ KMS_MSG_EXPORT (char *) kms_request_to_string (kms_request_t *request); /* kms_request_to_bytes returns the data for a request. * - Returns NULL on error and sets an error on request. */ KMS_MSG_EXPORT (const uint8_t *) kms_request_to_bytes (kms_request_t *request, size_t *len); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_REQUEST_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_request_opt.h000066400000000000000000000055501521103432300253670ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_REQUEST_OPT_H #define KMS_REQUEST_OPT_H #include "kms_message_defines.h" #include #include #ifdef __cplusplus extern "C" { #endif typedef struct _kms_request_opt_t kms_request_opt_t; typedef size_t kms_request_provider_t; #define KMS_REQUEST_PROVIDER_AWS 0 #define KMS_REQUEST_PROVIDER_AZURE 1 #define KMS_REQUEST_PROVIDER_GCP 2 #define KMS_REQUEST_PROVIDER_KMIP 3 KMS_MSG_EXPORT (kms_request_opt_t *) kms_request_opt_new (void); /* The default provider is AWS. This will automatically set extra headers. * Returns false if provider is invalid. */ KMS_MSG_EXPORT (bool) kms_request_opt_set_provider (kms_request_opt_t *opt, kms_request_provider_t provider); KMS_MSG_EXPORT (void) kms_request_opt_destroy (kms_request_opt_t *request); KMS_MSG_EXPORT (void) kms_request_opt_set_connection_close (kms_request_opt_t *opt, bool connection_close); KMS_MSG_EXPORT (void) kms_request_opt_set_crypto_hooks (kms_request_opt_t *opt, bool (*sha256) (void *ctx, const char *input, size_t len, unsigned char *hash_out), bool (*sha256_hmac) (void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out), void *ctx); KMS_MSG_EXPORT (void) kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5 ( kms_request_opt_t *opt, bool (*sign_rsaes_pkcs1_v1_5) (void *ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out), void *ctx); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_REQUEST_OPT_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_response.h000066400000000000000000000023001521103432300246410ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_RESPONSE_H #define KMS_RESPONSE_H #include "kms_message_defines.h" #include #include #ifdef __cplusplus extern "C" { #endif typedef struct _kms_response_t kms_response_t; KMS_MSG_EXPORT (int) kms_response_get_status (kms_response_t *response); KMS_MSG_EXPORT (const char *) kms_response_get_body (kms_response_t *response, size_t *len); KMS_MSG_EXPORT (void) kms_response_destroy (kms_response_t *response); KMS_MSG_EXPORT (const char *) kms_response_get_error (const kms_response_t *response); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_RESPONSE_H */ libmongocrypt-1.19.0/kms-message/src/kms_message/kms_response_parser.h000066400000000000000000000037201521103432300262240ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_RESPONSE_PARSER_H #define KMS_RESPONSE_PARSER_H #include "kms_message_defines.h" #include "kms_response.h" #include #include #include #ifdef __cplusplus extern "C" { #endif #define KMS_PARSER_MAX_RESPONSE_LEN 16 * 1024 * 1024 /* 16 MiB */ typedef struct _kms_response_parser_t kms_response_parser_t; KMS_MSG_EXPORT (kms_response_parser_t *) kms_response_parser_new (void); KMS_MSG_EXPORT (int) kms_response_parser_wants_bytes (kms_response_parser_t *parser, int32_t max); KMS_MSG_EXPORT (bool) kms_response_parser_feed (kms_response_parser_t *parser, uint8_t *buf, uint32_t len); KMS_MSG_EXPORT (kms_response_t *) kms_response_parser_get_response (kms_response_parser_t *parser); /* kms_response_parser_status returns the HTTP response status if one was * parsed. * - Calling on a KMIP parser is an error. * - Returns an int for the HTTP status or 0 on error. */ KMS_MSG_EXPORT (int) kms_response_parser_status (kms_response_parser_t *parser); KMS_MSG_EXPORT (const char *) kms_response_parser_error (kms_response_parser_t *parser); KMS_MSG_EXPORT (void) kms_response_parser_destroy (kms_response_parser_t *parser); KMS_MSG_EXPORT (void) kms_response_parser_reset (kms_response_parser_t *parser); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* KMS_RESPONSE_PARSER_H */ libmongocrypt-1.19.0/kms-message/src/kms_message_private.h000066400000000000000000000102671521103432300236760ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_PRIVATE_H #define KMS_MESSAGE_PRIVATE_H #include #include "kms_message/kms_message.h" #include "kms_request_str.h" #include "kms_kv_list.h" #include "kms_crypto.h" #include "kms_kmip_response_parser_private.h" /* Sadly, Windows does not define SSIZE_MAX. It is defined in bson-compat.h, * but only since 1.22.x, so copy this from bson-compat.h for now. */ #ifndef SSIZE_MAX #define SSIZE_MAX \ (ssize_t) ( \ (((size_t) 0x01u) << (sizeof (ssize_t) * (size_t) CHAR_BIT - 1u)) - 1u) #endif struct _kms_request_t { char error[512]; bool failed; bool finalized; /* Begin: AWS specific */ kms_request_str_t *region; kms_request_str_t *service; kms_request_str_t *access_key_id; kms_request_str_t *secret_key; kms_request_str_t *datetime; kms_request_str_t *date; /* End: AWS specific */ kms_request_str_t *method; kms_request_str_t *path; kms_request_str_t *query; kms_request_str_t *payload; kms_kv_list_t *query_params; kms_kv_list_t *header_fields; /* turn off for tests only, not in public kms_request_opt_t API */ bool auto_content_length; _kms_crypto_t crypto; kms_request_str_t *to_string; kms_request_provider_t provider; /* TODO (MONGOCRYPT-342): make a union for each KMS provider type. kms_request_provider_t provider; union { struct {} aws; struct {} azure; struct {} gcp; struct {} kmip; } */ struct { uint8_t *data; uint32_t len; } kmip; }; struct _kms_response_t { int status; kms_kv_list_t *headers; kms_request_str_t *body; /* TODO (MONGOCRYPT-347): make a union for each KMS provider type. */ char error[512]; bool failed; kms_request_provider_t provider; struct { uint8_t *data; uint32_t len; } kmip; }; typedef enum { PARSING_STATUS_LINE, PARSING_HEADER, PARSING_BODY, PARSING_CHUNK_LENGTH, PARSING_CHUNK, PARSING_DONE } kms_response_parser_state_t; struct _kms_response_parser_t { char error[512]; bool failed; kms_response_t *response; kms_request_str_t *raw_response; int content_length; int start; /* start of the current thing getting parsed. */ /* Support two types of HTTP 1.1 responses. * - "Content-Length: x" header is present, indicating the body length. * - "Transfer-Encoding: chunked" header is present, indicating a stream of * chunks. */ bool transfer_encoding_chunked; int chunk_size; kms_response_parser_state_t state; /* TODO: MONGOCRYPT-348 reorganize this struct to better separate fields for * HTTP parsing and fields for KMIP parsing. */ kms_kmip_response_parser_t *kmip; }; #define CHECK_FAILED \ do { \ if (request->failed) { \ return false; \ } \ } while (0) #if defined(__clang__) __attribute__((format(printf, 3, 4))) #elif defined(__GNUC__) __attribute__((format(gnu_printf, 3, 4))) #endif void kms_set_error (char *error, size_t size, const char *fmt, ...); #define KMS_ERROR(obj, ...) \ do { \ obj->failed = true; \ kms_set_error (obj->error, sizeof (obj->error), __VA_ARGS__); \ } while (0) #define KMS_ASSERT(stmt) \ if (!(stmt)) { \ fprintf (stderr, "%s failed\n", #stmt); \ abort (); \ } else \ ((void)0) #endif /* KMS_MESSAGE_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_port.c000066400000000000000000000015701521103432300214740ustar00rootroot00000000000000/* * Copyright 2020-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 "kms_port.h" #if defined(_WIN32) #include #include char * kms_strndup (const char *src, size_t len) { char *dst = (char *) malloc (len + 1); if (!dst) { return 0; } memcpy (dst, src, len); dst[len] = '\0'; return dst; } #endif libmongocrypt-1.19.0/kms-message/src/kms_port.h000066400000000000000000000015741521103432300215050ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_PORT_H #define KMS_PORT_H #include #if defined(_WIN32) #define kms_strcasecmp _stricmp char * kms_strndup (const char *src, size_t len); #else #include #define kms_strndup strndup #define kms_strcasecmp strcasecmp #endif #endif /* KMS_PORT_H */ libmongocrypt-1.19.0/kms-message/src/kms_request.c000066400000000000000000000607371521103432300222120ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_crypto.h" #include "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_request_opt_private.h" #include "kms_port.h" #include /* CHAR_BIT */ static kms_kv_list_t * parse_query_params (kms_request_str_t *q) { kms_kv_list_t *lst = kms_kv_list_new (); char *p = q->str; char *end = q->str + q->len; char *amp, *equals; kms_request_str_t *k, *v; do { equals = strchr ((const char *) p, '='); if (!equals) { kms_kv_list_destroy (lst); return NULL; } amp = strchr ((const char *) equals, '&'); if (!amp) { amp = end; } k = kms_request_str_new_from_chars (p, equals - p); v = kms_request_str_new_from_chars (equals + 1, amp - equals - 1); kms_kv_list_add (lst, k, v); kms_request_str_destroy (k); kms_request_str_destroy (v); p = amp + 1; } while (p < end); return lst; } static bool check_and_prohibit_kmip (kms_request_t *req) { if (req->provider == KMS_REQUEST_PROVIDER_KMIP) { KMS_ERROR (req, "Function not applicable to KMIP"); return false; } return true; } kms_request_t * kms_request_new (const char *method, const char *path_and_query, const kms_request_opt_t *opt) { kms_request_t *request = calloc (1, sizeof (kms_request_t)); const char *question_mark; KMS_ASSERT (request); if (opt && opt->provider) { request->provider = opt->provider; } else { request->provider = KMS_REQUEST_PROVIDER_AWS; } if (!check_and_prohibit_kmip (request)) { return request; } /* parsing may set failed to true */ request->failed = false; request->finalized = false; request->region = kms_request_str_new (); request->service = kms_request_str_new (); request->access_key_id = kms_request_str_new (); request->secret_key = kms_request_str_new (); question_mark = strchr (path_and_query, '?'); if (question_mark) { request->path = kms_request_str_new_from_chars ( path_and_query, question_mark - path_and_query); request->query = kms_request_str_new_from_chars (question_mark + 1, -1); request->query_params = parse_query_params (request->query); if (!request->query_params) { KMS_ERROR (request, "Cannot parse query: %s", request->query->str); } } else { request->path = kms_request_str_new_from_chars (path_and_query, -1); request->query = kms_request_str_new (); request->query_params = kms_kv_list_new (); } request->payload = kms_request_str_new (); request->date = kms_request_str_new (); request->datetime = kms_request_str_new (); request->method = kms_request_str_new_from_chars (method, -1); request->header_fields = kms_kv_list_new (); request->auto_content_length = true; /* For AWS KMS requests, add a X-Amz-Date header. */ if (request->provider == KMS_REQUEST_PROVIDER_AWS && !kms_request_set_date (request, NULL)) { return request; } if (opt && opt->connection_close) { if (!kms_request_add_header_field (request, "Connection", "close")) { return request; } } if (opt && opt->crypto.sha256) { memcpy (&request->crypto, &opt->crypto, sizeof (opt->crypto)); } else { request->crypto.sha256 = kms_sha256; request->crypto.sha256_hmac = kms_sha256_hmac; } return request; } void kms_request_destroy (kms_request_t *request) { kms_request_str_destroy (request->region); kms_request_str_destroy (request->service); kms_request_str_destroy (request->access_key_id); kms_request_str_destroy (request->secret_key); kms_request_str_destroy (request->method); kms_request_str_destroy (request->path); kms_request_str_destroy (request->query); kms_request_str_destroy (request->payload); kms_request_str_destroy (request->datetime); kms_request_str_destroy (request->date); kms_kv_list_destroy (request->query_params); kms_kv_list_destroy (request->header_fields); kms_request_str_destroy (request->to_string); free (request->kmip.data); free (request); } const char * kms_request_get_error (kms_request_t *request) { return request->failed ? request->error : NULL; } #define AMZ_DT_FORMAT "YYYYmmDDTHHMMSSZ" bool kms_request_set_date (kms_request_t *request, const struct tm *tm) { char buf[sizeof AMZ_DT_FORMAT]; struct tm tmp_tm; if (request->failed) { return false; } if (!check_and_prohibit_kmip (request)) { return false; } if (!tm) { /* use current time */ time_t t; time (&t); #if defined(KMS_MESSAGE_HAVE_GMTIME_R) gmtime_r (&t, &tmp_tm); #elif defined(_MSC_VER) gmtime_s (&tmp_tm, &t); #else tmp_tm = *gmtime (&t); #endif tm = &tmp_tm; } if (0 == strftime (buf, sizeof AMZ_DT_FORMAT, "%Y%m%dT%H%M%SZ", tm)) { KMS_ERROR (request, "Invalid tm struct"); return false; } kms_request_str_set_chars (request->date, buf, sizeof "YYYYmmDD" - 1); kms_request_str_set_chars (request->datetime, buf, sizeof AMZ_DT_FORMAT - 1); kms_kv_list_del (request->header_fields, "X-Amz-Date"); if (!kms_request_add_header_field (request, "X-Amz-Date", buf)) { return false; } return true; } #undef AMZ_DT_FORMAT bool kms_request_set_region (kms_request_t *request, const char *region) { if (!check_and_prohibit_kmip (request)) { return false; } kms_request_str_set_chars (request->region, region, -1); return true; } bool kms_request_set_service (kms_request_t *request, const char *service) { if (!check_and_prohibit_kmip (request)) { return false; } kms_request_str_set_chars (request->service, service, -1); return true; } bool kms_request_set_access_key_id (kms_request_t *request, const char *akid) { if (!check_and_prohibit_kmip (request)) { return false; } kms_request_str_set_chars (request->access_key_id, akid, -1); return true; } bool kms_request_set_secret_key (kms_request_t *request, const char *key) { if (!check_and_prohibit_kmip (request)) { return false; } kms_request_str_set_chars (request->secret_key, key, -1); return true; } bool kms_request_add_header_field (kms_request_t *request, const char *field_name, const char *value) { kms_request_str_t *k, *v; CHECK_FAILED; if (!check_and_prohibit_kmip (request)) { return false; } k = kms_request_str_new_from_chars (field_name, -1); v = kms_request_str_new_from_chars (value, -1); kms_kv_list_add (request->header_fields, k, v); kms_request_str_destroy (k); kms_request_str_destroy (v); return true; } bool kms_request_append_header_field_value (kms_request_t *request, const char *value, size_t len) { kms_request_str_t *v; CHECK_FAILED; if (!check_and_prohibit_kmip (request)) { return false; } if (request->header_fields->len == 0) { KMS_ERROR ( request, "Ensure the request has at least one header field before calling %s", __func__); } v = request->header_fields->kvs[request->header_fields->len - 1].value; KMS_ASSERT (len <= SSIZE_MAX); kms_request_str_append_chars (v, value, (ssize_t) len); return true; } bool kms_request_append_payload (kms_request_t *request, const char *payload, size_t len) { CHECK_FAILED; if (!check_and_prohibit_kmip (request)) { return false; } KMS_ASSERT (len <= SSIZE_MAX); kms_request_str_append_chars (request->payload, payload, (ssize_t) len); return true; } /* docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html * * "Sort the parameter names by character code point in ascending order. For * example, a parameter name that begins with the uppercase letter F precedes a * parameter name that begins with a lowercase letter b." */ static int cmp_query_params (const void *a, const void *b) { int r = strcmp (((kms_kv_t *) a)->key->str, ((kms_kv_t *) b)->key->str); if (r != 0) { return r; } /* not in docs, but tested in get-vanilla-query-order-key: sort by value */ return strcmp (((kms_kv_t *) a)->value->str, ((kms_kv_t *) b)->value->str); } static void append_canonical_query (kms_request_t *request, kms_request_str_t *str) { size_t i; kms_kv_list_t *lst; if (!request->query_params->len) { return; } lst = kms_kv_list_dup (request->query_params); kms_kv_list_sort (lst, cmp_query_params); for (i = 0; i < lst->len; i++) { kms_request_str_append_escaped (str, lst->kvs[i].key, true); kms_request_str_append_char (str, '='); kms_request_str_append_escaped (str, lst->kvs[i].value, true); if (i < lst->len - 1) { kms_request_str_append_char (str, '&'); } } kms_kv_list_destroy (lst); } /* "lst" is a sorted list of headers */ static void append_canonical_headers (kms_kv_list_t *lst, kms_request_str_t *str) { size_t i; kms_kv_t *kv; const kms_request_str_t *previous_key = NULL; /* aws docs: "To create the canonical headers list, convert all header names * to lowercase and remove leading spaces and trailing spaces. Convert * sequential spaces in the header value to a single space." "Do not sort the * values in headers that have multiple values." */ for (i = 0; i < lst->len; i++) { kv = &lst->kvs[i]; if (previous_key && 0 == kms_strcasecmp (previous_key->str, kv->key->str)) { /* duplicate header */ kms_request_str_append_char (str, ','); kms_request_str_append_stripped (str, kv->value); continue; } if (i > 0) { kms_request_str_append_newline (str); } kms_request_str_append_lowercase (str, kv->key); kms_request_str_append_char (str, ':'); kms_request_str_append_stripped (str, kv->value); previous_key = kv->key; } kms_request_str_append_newline (str); } static void append_signed_headers (kms_kv_list_t *lst, kms_request_str_t *str) { size_t i; kms_kv_t *kv; const kms_request_str_t *previous_key = NULL; for (i = 0; i < lst->len; i++) { kv = &lst->kvs[i]; if (previous_key && 0 == kms_strcasecmp (previous_key->str, kv->key->str)) { /* duplicate header */ continue; } if (0 == kms_strcasecmp (kv->key->str, "connection")) { continue; } kms_request_str_append_lowercase (str, kv->key); if (i < lst->len - 1) { kms_request_str_append_char (str, ';'); } previous_key = kv->key; } } static bool finalize (kms_request_t *request) { kms_kv_list_t *lst; kms_request_str_t *k; kms_request_str_t *v; if (request->failed) { return false; } if (request->finalized) { return true; } request->finalized = true; lst = request->header_fields; if (!kms_kv_list_find (lst, "Host")) { if (request->provider != KMS_REQUEST_PROVIDER_AWS) { KMS_ERROR (request, "Required Host header not set"); return false; } /* For AWS requests, derive a default Host header from region + service. * E.g. "kms.us-east-1.amazonaws.com" */ k = kms_request_str_new_from_chars ("Host", -1); v = kms_request_str_dup (request->service); kms_request_str_append_char (v, '.'); kms_request_str_append (v, request->region); kms_request_str_append_chars (v, ".amazonaws.com", -1); kms_kv_list_add (lst, k, v); kms_request_str_destroy (k); kms_request_str_destroy (v); } if (!kms_kv_list_find (lst, "Content-Length") && request->payload->len && request->auto_content_length) { k = kms_request_str_new_from_chars ("Content-Length", -1); v = kms_request_str_new (); kms_request_str_appendf (v, "%zu", request->payload->len); kms_kv_list_add (lst, k, v); kms_request_str_destroy (k); kms_request_str_destroy (v); } return true; } /* docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html * * "Build the canonical headers list by sorting the (lowercase) headers by * character code... Do not sort the values in headers that have multiple * values." */ static int cmp_header_field_names (const void *a, const void *b) { return kms_strcasecmp (((kms_kv_t *) a)->key->str, ((kms_kv_t *) b)->key->str); } static kms_kv_list_t * canonical_headers (const kms_request_t *request) { kms_kv_list_t *lst; KMS_ASSERT (request->finalized); lst = kms_kv_list_dup (request->header_fields); kms_kv_list_sort (lst, cmp_header_field_names); kms_kv_list_del (lst, "Connection"); return lst; } char * kms_request_get_canonical (kms_request_t *request) { kms_request_str_t *canonical; kms_request_str_t *normalized; kms_kv_list_t *lst; if (request->failed) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } if (!finalize (request)) { return NULL; } canonical = kms_request_str_new (); kms_request_str_append (canonical, request->method); kms_request_str_append_newline (canonical); normalized = kms_request_str_path_normalized (request->path); kms_request_str_append_escaped (canonical, normalized, false); kms_request_str_destroy (normalized); kms_request_str_append_newline (canonical); append_canonical_query (request, canonical); kms_request_str_append_newline (canonical); lst = canonical_headers (request); append_canonical_headers (lst, canonical); kms_request_str_append_newline (canonical); append_signed_headers (lst, canonical); kms_kv_list_destroy (lst); kms_request_str_append_newline (canonical); if (!kms_request_str_append_hashed ( &request->crypto, canonical, request->payload)) { KMS_ERROR (request, "could not generate hash"); kms_request_str_destroy (canonical); return NULL; } return kms_request_str_detach (canonical); } const char * kms_request_get_canonical_header (kms_request_t *request, const char *header) { const kms_kv_t *value; if (request->failed) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } if (!finalize (request)) { return NULL; } value = kms_kv_list_find (request->header_fields, header); if (!value) { return NULL; } return value->value->str; } char * kms_request_get_string_to_sign (kms_request_t *request) { bool success = false; kms_request_str_t *sts; kms_request_str_t *creq = NULL; /* canonical request */ if (request->failed) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } if (!finalize (request)) { return NULL; } sts = kms_request_str_new (); kms_request_str_append_chars (sts, "AWS4-HMAC-SHA256\n", -1); kms_request_str_append (sts, request->datetime); kms_request_str_append_newline (sts); /* credential scope, like "20150830/us-east-1/service/aws4_request" */ kms_request_str_append (sts, request->date); kms_request_str_append_char (sts, '/'); kms_request_str_append (sts, request->region); kms_request_str_append_char (sts, '/'); kms_request_str_append (sts, request->service); kms_request_str_append_chars (sts, "/aws4_request\n", -1); creq = kms_request_str_wrap (kms_request_get_canonical (request), -1); if (!creq) { goto done; } if (!kms_request_str_append_hashed (&request->crypto, sts, creq)) { goto done; } success = true; done: kms_request_str_destroy (creq); if (!success) { kms_request_str_destroy (sts); sts = NULL; } return kms_request_str_detach (sts); } static bool kms_request_hmac (_kms_crypto_t *crypto, unsigned char *out, kms_request_str_t *key, kms_request_str_t *data) { return crypto->sha256_hmac ( crypto->ctx, key->str, key->len, data->str, data->len, out); } static bool kms_request_hmac_again (_kms_crypto_t *crypto, unsigned char *out, unsigned char *in, kms_request_str_t *data) { return crypto->sha256_hmac ( crypto->ctx, (const char *) in, 32, data->str, data->len, out); } bool kms_request_get_signing_key (kms_request_t *request, unsigned char *key) { bool success = false; kms_request_str_t *aws4_plus_secret = NULL; kms_request_str_t *aws4_request = NULL; unsigned char k_date[32]; unsigned char k_region[32]; unsigned char k_service[32]; if (request->failed) { return false; } if (!check_and_prohibit_kmip (request)) { return false; } /* docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html * Pseudocode for deriving a signing key * * kSecret = your secret access key * kDate = HMAC("AWS4" + kSecret, Date) * kRegion = HMAC(kDate, Region) * kService = HMAC(kRegion, Service) * kSigning = HMAC(kService, "aws4_request") */ aws4_plus_secret = kms_request_str_new_from_chars ("AWS4", -1); kms_request_str_append (aws4_plus_secret, request->secret_key); aws4_request = kms_request_str_new_from_chars ("aws4_request", -1); if (!(kms_request_hmac ( &request->crypto, k_date, aws4_plus_secret, request->date) && kms_request_hmac_again ( &request->crypto, k_region, k_date, request->region) && kms_request_hmac_again ( &request->crypto, k_service, k_region, request->service) && kms_request_hmac_again ( &request->crypto, key, k_service, aws4_request))) { goto done; } success = true; done: kms_request_str_destroy (aws4_plus_secret); kms_request_str_destroy (aws4_request); return success; } char * kms_request_get_signature (kms_request_t *request) { bool success = false; kms_kv_list_t *lst = NULL; kms_request_str_t *sig = NULL; kms_request_str_t *sts = NULL; unsigned char signing_key[32]; unsigned char signature[32]; if (request->failed) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } sts = kms_request_str_wrap (kms_request_get_string_to_sign (request), -1); if (!sts) { goto done; } sig = kms_request_str_new (); kms_request_str_append_chars (sig, "AWS4-HMAC-SHA256 Credential=", -1); kms_request_str_append (sig, request->access_key_id); kms_request_str_append_char (sig, '/'); kms_request_str_append (sig, request->date); kms_request_str_append_char (sig, '/'); kms_request_str_append (sig, request->region); kms_request_str_append_char (sig, '/'); kms_request_str_append (sig, request->service); kms_request_str_append_chars (sig, "/aws4_request, SignedHeaders=", -1); lst = canonical_headers (request); append_signed_headers (lst, sig); kms_request_str_append_chars (sig, ", Signature=", -1); if (!(kms_request_get_signing_key (request, signing_key) && kms_request_hmac_again ( &request->crypto, signature, signing_key, sts))) { goto done; } kms_request_str_append_hex (sig, signature, sizeof (signature)); success = true; done: kms_kv_list_destroy (lst); kms_request_str_destroy (sts); if (!success) { kms_request_str_destroy (sig); sig = NULL; } return kms_request_str_detach (sig); } static void kms_request_validate (kms_request_t *request) { if (!check_and_prohibit_kmip (request)) { return; } if (0 == request->region->len) { KMS_ERROR (request, "Region not set"); } else if (0 == request->service->len) { KMS_ERROR (request, "Service not set"); } else if (0 == request->access_key_id->len) { KMS_ERROR (request, "Access key ID not set"); } else if (0 == request->method->len) { KMS_ERROR (request, "Method not set"); } else if (0 == request->path->len) { KMS_ERROR (request, "Path not set"); } else if (0 == request->date->len) { KMS_ERROR (request, "Date not set"); } else if (0 == request->secret_key->len) { KMS_ERROR (request, "Secret key not set"); } } /* append_http_endofline appends an HTTP end-of-line marker: "\r\n". */ static void append_http_endofline (kms_request_str_t *str) { kms_request_str_append_chars (str, "\r\n", 2); } char * kms_request_get_signed (kms_request_t *request) { bool success = false; kms_kv_list_t *lst = NULL; char *signature = NULL; kms_request_str_t *sreq = NULL; size_t i; kms_request_validate (request); if (request->failed) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } if (!finalize (request)) { return NULL; } sreq = kms_request_str_new (); /* like "POST / HTTP/1.1" */ kms_request_str_append (sreq, request->method); kms_request_str_append_char (sreq, ' '); kms_request_str_append (sreq, request->path); if (request->query->len) { kms_request_str_append_char (sreq, '?'); kms_request_str_append (sreq, request->query); } kms_request_str_append_chars (sreq, " HTTP/1.1", -1); append_http_endofline (sreq); /* headers */ lst = kms_kv_list_dup (request->header_fields); kms_kv_list_sort (lst, cmp_header_field_names); for (i = 0; i < lst->len; i++) { kms_request_str_append (sreq, lst->kvs[i].key); kms_request_str_append_char (sreq, ':'); kms_request_str_append (sreq, lst->kvs[i].value); append_http_endofline (sreq); } /* authorization header */ signature = kms_request_get_signature (request); if (!signature) { goto done; } /* note space after ':', to match test .sreq files */ kms_request_str_append_chars (sreq, "Authorization: ", -1); kms_request_str_append_chars (sreq, signature, -1); /* body */ if (request->payload->len) { append_http_endofline (sreq); append_http_endofline (sreq); kms_request_str_append (sreq, request->payload); } success = true; done: free (signature); kms_kv_list_destroy (lst); if (!success) { kms_request_str_destroy (sreq); sreq = NULL; } return kms_request_str_detach (sreq); } char * kms_request_to_string (kms_request_t *request) { kms_kv_list_t *lst = NULL; kms_request_str_t *sreq = NULL; size_t i; if (!finalize (request)) { return NULL; } if (!check_and_prohibit_kmip (request)) { return NULL; } if (request->to_string) { return kms_request_str_detach (kms_request_str_dup (request->to_string)); } sreq = kms_request_str_new (); /* like "POST / HTTP/1.1" */ kms_request_str_append (sreq, request->method); kms_request_str_append_char (sreq, ' '); kms_request_str_append (sreq, request->path); if (request->query->len) { kms_request_str_append_char (sreq, '?'); kms_request_str_append (sreq, request->query); } kms_request_str_append_chars (sreq, " HTTP/1.1", -1); append_http_endofline (sreq); /* headers */ lst = kms_kv_list_dup (request->header_fields); kms_kv_list_sort (lst, cmp_header_field_names); for (i = 0; i < lst->len; i++) { kms_request_str_append (sreq, lst->kvs[i].key); kms_request_str_append_char (sreq, ':'); kms_request_str_append (sreq, lst->kvs[i].value); append_http_endofline (sreq); } append_http_endofline (sreq); /* body */ if (request->payload->len) { kms_request_str_append (sreq, request->payload); } kms_kv_list_destroy (lst); request->to_string = kms_request_str_dup (sreq); return kms_request_str_detach (sreq); } void kms_request_free_string (char *ptr) { free (ptr); } const uint8_t * kms_request_to_bytes (kms_request_t *request, size_t *len) { if (request->provider == KMS_REQUEST_PROVIDER_KMIP) { *len = request->kmip.len; return request->kmip.data; } if (!request->to_string && !kms_request_to_string (request)) { return NULL; } KMS_ASSERT (request->to_string); *len = request->to_string->len; return (const uint8_t*) request->to_string->str; } libmongocrypt-1.19.0/kms-message/src/kms_request_opt.c000066400000000000000000000055021521103432300230610ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_request_opt_private.h" #include kms_request_opt_t * kms_request_opt_new (void) { return calloc (1, sizeof (kms_request_opt_t)); } void kms_request_opt_destroy (kms_request_opt_t *request) { free (request); } void kms_request_opt_set_connection_close (kms_request_opt_t *opt, bool connection_close) { opt->connection_close = connection_close; } void kms_request_opt_set_crypto_hooks (kms_request_opt_t *opt, bool (*sha256) (void *ctx, const char *input, size_t len, unsigned char *hash_out), bool (*sha256_hmac) (void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out), void *ctx) { opt->crypto.sha256 = sha256; opt->crypto.sha256_hmac = sha256_hmac; opt->crypto.ctx = ctx; } bool kms_request_opt_set_provider (kms_request_opt_t *opt, kms_request_provider_t provider) { if (provider != KMS_REQUEST_PROVIDER_AWS && provider != KMS_REQUEST_PROVIDER_AZURE && provider != KMS_REQUEST_PROVIDER_GCP && provider != KMS_REQUEST_PROVIDER_KMIP) { return false; } opt->provider = provider; return true; } void kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5 ( kms_request_opt_t *opt, bool (*sign_rsaes_pkcs1_v1_5) (void *sign_ctx, const char *private_key, size_t private_key_len, const char *input, size_t input_len, unsigned char *signature_out), void *sign_ctx) { opt->crypto.sign_rsaes_pkcs1_v1_5 = sign_rsaes_pkcs1_v1_5; opt->crypto.sign_ctx = sign_ctx; } libmongocrypt-1.19.0/kms-message/src/kms_request_opt_private.h000066400000000000000000000017041521103432300246200ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_REQUEST_OPT_PRIVATE_H #define KMS_REQUEST_OPT_PRIVATE_H #include "kms_message/kms_message_defines.h" #include "kms_message/kms_request_opt.h" #include "kms_crypto.h" #include struct _kms_request_opt_t { bool connection_close; _kms_crypto_t crypto; kms_request_provider_t provider; }; #endif /* KMS_REQUEST_OPT_PRIVATE_H */ libmongocrypt-1.19.0/kms-message/src/kms_request_str.c000066400000000000000000000277401521103432300230770ustar00rootroot00000000000000/* * Copyright 2018-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 "hexlify.h" #include "kms_crypto.h" #include "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_request_str.h" #include "kms_port.h" #include #include #include #include #include /* CHAR_BIT */ static bool rfc_3986_tab[256] = {0}; static bool kms_initialized = false; static void tables_init (void) { int i; if (kms_initialized) { return; } for (i = 0; i < 256; ++i) { rfc_3986_tab[i] = isalnum (i) || i == '~' || i == '-' || i == '.' || i == '_'; } kms_initialized = true; } kms_request_str_t * kms_request_str_new (void) { kms_request_str_t *s = malloc (sizeof (kms_request_str_t)); KMS_ASSERT (s); s->len = 0; s->size = 16; s->str = malloc (s->size); KMS_ASSERT (s->str); s->str[0] = '\0'; return s; } kms_request_str_t * kms_request_str_new_from_chars (const char *chars, ssize_t len) { kms_request_str_t *s = malloc (sizeof (kms_request_str_t)); KMS_ASSERT (s); size_t actual_len; actual_len = len < 0 ? strlen (chars) : (size_t) len; s->size = actual_len + 1; s->str = malloc (s->size); KMS_ASSERT (s->str); memcpy (s->str, chars, actual_len); s->str[actual_len] = '\0'; s->len = actual_len; return s; } kms_request_str_t * kms_request_str_wrap (char *chars, ssize_t len) { kms_request_str_t *s; if (!chars) { return NULL; } s = malloc (sizeof (kms_request_str_t)); KMS_ASSERT (s); s->str = chars; s->len = len < 0 ? strlen (chars) : (size_t) len; s->size = s->len; return s; } void kms_request_str_destroy (kms_request_str_t *str) { if (!str) { return; } free (str->str); free (str); } char * kms_request_str_detach (kms_request_str_t *str) { if (!str) { return NULL; } char *r = str->str; free (str); return r; } bool kms_request_str_reserve (kms_request_str_t *str, size_t size) { size_t next_size = str->len + size + 1; if (str->size < next_size) { /* next power of 2 */ --next_size; next_size |= next_size >> 1U; next_size |= next_size >> 2U; next_size |= next_size >> 4U; next_size |= next_size >> 8U; next_size |= next_size >> 16U; ++next_size; str->size = next_size; str->str = realloc (str->str, next_size); KMS_ASSERT(str->str); } return str->str != NULL; } kms_request_str_t * kms_request_str_dup (kms_request_str_t *str) { kms_request_str_t *dup = malloc (sizeof (kms_request_str_t)); KMS_ASSERT (dup); dup->str = kms_strndup (str->str, str->len); dup->len = str->len; dup->size = str->len + 1; return dup; } void kms_request_str_set_chars (kms_request_str_t *str, const char *chars, ssize_t len) { size_t actual_len = len < 0 ? strlen (chars) : (size_t) len; kms_request_str_reserve (str, actual_len); /* adds 1 for nil */ memcpy (str->str, chars, actual_len + 1); str->len = actual_len; } bool kms_request_str_ends_with (kms_request_str_t *str, kms_request_str_t *suffix) { if (str->len >= suffix->len && 0 == strncmp ( &str->str[str->len - suffix->len], suffix->str, suffix->len)) { return true; } return false; } void kms_request_str_append (kms_request_str_t *str, kms_request_str_t *appended) { size_t next_len = str->len + appended->len; kms_request_str_reserve (str, next_len); memcpy (str->str + str->len, appended->str, appended->len); str->len += appended->len; str->str[str->len] = '\0'; } void kms_request_str_append_char (kms_request_str_t *str, char c) { kms_request_str_reserve (str, 1); *(str->str + str->len) = c; ++str->len; str->str[str->len] = '\0'; } void kms_request_str_append_chars (kms_request_str_t *str, const char *appended, ssize_t len) { size_t str_len; if (len < 0) { str_len = strlen (appended); } else { str_len = (size_t) len; } kms_request_str_reserve (str, str_len); memcpy (str->str + str->len, appended, str_len); str->len += str_len; str->str[str->len] = '\0'; } void kms_request_str_append_newline (kms_request_str_t *str) { kms_request_str_append_char (str, '\n'); } void kms_request_str_append_lowercase (kms_request_str_t *str, kms_request_str_t *appended) { size_t i; char *p; i = str->len; kms_request_str_append (str, appended); /* downcase the chars from the old end to the new end of str */ for (; i < str->len; ++i) { p = &str->str[i]; /* ignore UTF-8 non-ASCII chars, which have 1 in the top bit */ if (((unsigned int) (*p) & (0x1U << 7U)) == 0) { *p = (char) tolower (*p); } } } void kms_request_str_appendf (kms_request_str_t *str, const char *format, ...) { va_list args; size_t remaining; int n; KMS_ASSERT (format); while (true) { remaining = str->size - str->len; va_start (args, format); n = vsnprintf (&str->str[str->len], remaining, format, args); va_end (args); if (n > -1 && (size_t) n < remaining) { /* success */ str->len += (size_t) n; return; } if (n > -1) { kms_request_str_reserve (str, (size_t) n); } else { /* TODO: error! */ abort (); } } } void kms_request_str_append_escaped (kms_request_str_t *str, kms_request_str_t *appended, bool escape_slash) { uint8_t *in; uint8_t *out; size_t i; tables_init (); /* might replace each input char with 3 output chars: "%AB" */ kms_request_str_reserve (str, 3 * appended->len); in = (uint8_t *) appended->str; out = (uint8_t *) str->str + str->len; for (i = 0; i < appended->len; ++i) { if (rfc_3986_tab[*in] || (*in == '/' && !escape_slash)) { *out = *in; ++out; ++str->len; } else { KMS_ASSERT (3 == snprintf ((char *) out, 4, "%%%02X", *in)); out += 3; str->len += 3; } ++in; } } void kms_request_str_append_stripped (kms_request_str_t *str, kms_request_str_t *appended) { const char *src = appended->str; const char *end = appended->str + appended->len; bool space = false; bool comma = false; kms_request_str_reserve (str, appended->len); /* msvcrt is unhappy when it gets non-ANSI characters in isspace */ while (*src >= 0 && isspace (*src)) { ++src; } while (src < end) { /* replace newlines with commas. not documented but see * get-header-value-multiline.creq */ if (*src == '\n') { comma = true; space = false; } else if (*src >= 0 && isspace (*src)) { space = true; } else { if (comma) { kms_request_str_append_char (str, ','); comma = false; space = false; } /* is there a run of spaces waiting to be written as one space? */ if (space) { kms_request_str_append_char (str, ' '); space = false; } kms_request_str_append_char (str, *src); } ++src; } } bool kms_request_str_append_hashed (_kms_crypto_t *crypto, kms_request_str_t *str, kms_request_str_t *appended) { uint8_t hash[32] = {0}; char *hex_chars; if (!crypto->sha256 (crypto->ctx, appended->str, appended->len, hash)) { return false; } hex_chars = hexlify (hash, sizeof (hash)); kms_request_str_append_chars (str, hex_chars, 2 * sizeof (hash)); free (hex_chars); return true; } bool kms_request_str_append_hex (kms_request_str_t *str, unsigned char *data, size_t len) { char *hex_chars; hex_chars = hexlify (data, len); KMS_ASSERT (len <= SSIZE_MAX / 2); kms_request_str_append_chars (str, hex_chars, (ssize_t) (len * 2)); free (hex_chars); return true; } static bool starts_with (char *s, const char *prefix) { if (strstr (s, prefix) == s) { return true; } return false; } /* remove from last slash to the end, but don't remove slash from start */ static void delete_last_segment (kms_request_str_t *str, bool is_absolute) { ssize_t i; if (!str->len) { return; } KMS_ASSERT (str->len < SSIZE_MAX); for (i = (ssize_t) str->len - 1; i >= 0; --i) { if (str->str[i] == '/') { if (i == 0 && is_absolute) { str->len = 1; } else { str->len = (size_t) i; } goto done; } } /* no slashes */ str->len = 0; done: str->str[str->len] = '\0'; } /* follow algorithm in https://tools.ietf.org/html/rfc3986#section-5.2.4, * the block comments are copied from there */ kms_request_str_t * kms_request_str_path_normalized (kms_request_str_t *str) { kms_request_str_t *slash = kms_request_str_new_from_chars ("/", 1); kms_request_str_t *out = kms_request_str_new (); char *in = strdup (str->str); char *p = in; char *end = in + str->len; bool is_absolute = (*p == '/'); if (0 == strcmp (p, "/")) { goto done; } while (p < end) { /* If the input buffer begins with a prefix of "../" or "./", * then remove that prefix from the input buffer */ if (starts_with (p, "../")) { p += 3; } else if (starts_with (p, "./")) { p += 2; } /* otherwise, if the input buffer begins with a prefix of "/./" or "/.", * where "." is a complete path segment, then replace that prefix with "/" * in the input buffer */ else if (starts_with (p, "/./")) { p += 2; } else if (0 == strcmp (p, "/.")) { break; } /* otherwise, if the input buffer begins with a prefix of "/../" or "/..", * where ".." is a complete path segment, then replace that prefix with * "/" in the input buffer and remove the last segment and its preceding * "/" (if any) from the output buffer */ else if (starts_with (p, "/../")) { p += 3; delete_last_segment (out, is_absolute); } else if (0 == strcmp (p, "/..")) { delete_last_segment (out, is_absolute); break; } /* otherwise, if the input buffer consists only of "." or "..", then remove that from the input buffer */ else if (0 == strcmp (p, ".") || 0 == strcmp (p, "..")) { break; } /* otherwise, move the first path segment in the input buffer to the end * of the output buffer, including the initial "/" character (if any) and * any subsequent characters up to, but not including, the next "/" * character or the end of the input buffer. */ else { char *next_slash = strchr (p + 1, '/'); if (!next_slash) { next_slash = end; } /* fold repeated slashes */ if (kms_request_str_ends_with (out, slash) && *p == '/') { ++p; } /* normalize "a/../b" as "b", not as "/b" */ if (out->len == 0 && !is_absolute && *p == '/') { ++p; } kms_request_str_append_chars (out, p, next_slash - p); p = next_slash; } } done: free (in); kms_request_str_destroy (slash); if (!out->len) { kms_request_str_append_char (out, '/'); } return out; } libmongocrypt-1.19.0/kms-message/src/kms_request_str.h000066400000000000000000000070211521103432300230720ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef KMS_MESSAGE_KMS_REQUEST_STR_H #define KMS_MESSAGE_KMS_REQUEST_STR_H #include "kms_message/kms_message.h" #include "kms_crypto.h" #include #include #include #include typedef struct { char *str; size_t len; size_t size; } kms_request_str_t; KMS_MSG_EXPORT (kms_request_str_t *) kms_request_str_new (void); KMS_MSG_EXPORT (kms_request_str_t *) kms_request_str_new_from_chars (const char *chars, ssize_t len); KMS_MSG_EXPORT (kms_request_str_t *) kms_request_str_wrap (char *chars, ssize_t len); KMS_MSG_EXPORT (void) kms_request_str_destroy (kms_request_str_t *str); KMS_MSG_EXPORT (char *) kms_request_str_detach (kms_request_str_t *str); KMS_MSG_EXPORT (bool) kms_request_str_reserve (kms_request_str_t *str, size_t size); KMS_MSG_EXPORT (kms_request_str_t *) kms_request_str_dup (kms_request_str_t *str); KMS_MSG_EXPORT (void) kms_request_str_set_chars (kms_request_str_t *str, const char *chars, ssize_t len); KMS_MSG_EXPORT (bool) kms_request_str_ends_with (kms_request_str_t *str, kms_request_str_t *suffix); KMS_MSG_EXPORT (void) kms_request_str_append (kms_request_str_t *str, kms_request_str_t *appended); KMS_MSG_EXPORT (void) kms_request_str_append_char (kms_request_str_t *str, char c); KMS_MSG_EXPORT (void) kms_request_str_append_chars (kms_request_str_t *str, const char *appended, ssize_t len); KMS_MSG_EXPORT (void) kms_request_str_append_newline (kms_request_str_t *str); KMS_MSG_EXPORT (void) kms_request_str_append_lowercase (kms_request_str_t *str, kms_request_str_t *appended); #if defined(__clang__) __attribute__((format(printf, 2, 3))) #elif defined(__GNUC__) __attribute__((format(gnu_printf, 2, 3))) #endif KMS_MSG_EXPORT (void) kms_request_str_appendf (kms_request_str_t *str, const char *format, ...); #if defined(__clang__) __attribute__((format(printf, 2, 3))) #elif defined(__GNUC__) __attribute__((format(gnu_printf, 2, 3))) #endif KMS_MSG_EXPORT (void) kms_request_strdupf (kms_request_str_t *str, const char *format, ...); KMS_MSG_EXPORT (void) kms_request_str_append_escaped (kms_request_str_t *str, kms_request_str_t *appended, bool escape_slash); KMS_MSG_EXPORT (void) kms_request_str_append_stripped (kms_request_str_t *str, kms_request_str_t *appended); KMS_MSG_EXPORT (bool) kms_request_str_append_hashed (_kms_crypto_t *crypto, kms_request_str_t *str, kms_request_str_t *appended); KMS_MSG_EXPORT (bool) kms_request_str_append_hex (kms_request_str_t *str, unsigned char *data, size_t len); KMS_MSG_EXPORT (kms_request_str_t *) kms_request_str_path_normalized (kms_request_str_t *str); #endif // KMS_MESSAGE_KMS_REQUEST_STR_H libmongocrypt-1.19.0/kms-message/src/kms_response.c000066400000000000000000000024631521103432300223500ustar00rootroot00000000000000/* * Copyright 2018-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 "kms_message/kms_message.h" #include "kms_message_private.h" #include "kms_request_str.h" void kms_response_destroy (kms_response_t *response) { if (response == NULL) { return; } free (response->kmip.data); kms_kv_list_destroy (response->headers); kms_request_str_destroy (response->body); free (response); } const char * kms_response_get_body (kms_response_t *response, size_t *len) { if (len) { *len = response->body->len; } return response->body->str; } int kms_response_get_status (kms_response_t *response) { return response->status; } const char * kms_response_get_error (const kms_response_t *response) { return response->failed ? response->error : NULL; } libmongocrypt-1.19.0/kms-message/src/kms_response_parser.c000066400000000000000000000271461521103432300237310ustar00rootroot00000000000000#include "kms_message/kms_response_parser.h" #include "kms_message_private.h" #include "kms_kmip_response_parser_private.h" #include #include #include #include #include "hexlify.h" /* destroys the members of parser, but not the parser itself. */ static void _parser_destroy (kms_response_parser_t *parser) { kms_request_str_destroy (parser->raw_response); parser->raw_response = NULL; parser->content_length = -1; kms_response_destroy (parser->response); parser->response = NULL; kms_kmip_response_parser_destroy (parser->kmip); } /* initializes the members of parser. */ static void _parser_init (kms_response_parser_t *parser) { parser->raw_response = kms_request_str_new (); parser->content_length = -1; parser->response = calloc (1, sizeof (kms_response_t)); KMS_ASSERT (parser->response); parser->response->headers = kms_kv_list_new (); parser->state = PARSING_STATUS_LINE; parser->start = 0; parser->failed = false; parser->chunk_size = 0; parser->transfer_encoding_chunked = false; parser->kmip = NULL; } void kms_response_parser_reset (kms_response_parser_t *parser) { KMS_ASSERT(!parser->kmip); // KMIP is not-yet supported. _parser_destroy(parser); _parser_init(parser); } kms_response_parser_t * kms_response_parser_new (void) { kms_response_parser_t *parser = malloc (sizeof (kms_response_parser_t)); KMS_ASSERT (parser); _parser_init (parser); return parser; } int kms_response_parser_wants_bytes (kms_response_parser_t *parser, int32_t max) { if (parser->kmip) { return kms_kmip_response_parser_wants_bytes (parser->kmip, max); } switch (parser->state) { case PARSING_DONE: return 0; case PARSING_STATUS_LINE: case PARSING_HEADER: return max; case PARSING_CHUNK_LENGTH: return max; case PARSING_CHUNK: /* add 2 for trailing \r\n */ return (parser->chunk_size + 2) - ((int) parser->raw_response->len - parser->start); case PARSING_BODY: KMS_ASSERT (parser->content_length != -1); return parser->content_length - ((int) parser->raw_response->len - parser->start); default: KMS_ASSERT (false && "Invalid kms_response_parser HTTP state"); } } static bool _parse_int (const char *str, int *result) { char *endptr = NULL; int64_t long_result; errno = 0; long_result = strtol (str, &endptr, 10); if (endptr == str) { /* No digits were parsed. Consider this an error */ return false; } if (endptr != NULL && *endptr != '\0') { /* endptr points to the first invalid character. */ return false; } if (errno == EINVAL || errno == ERANGE) { return false; } if (long_result > INT32_MAX || long_result < INT32_MIN) { return false; } *result = (int) long_result; return true; } /* parse an int from a substring inside of a string. */ static bool _parse_int_from_view (const char *str, int start, int end, int *result) { KMS_ASSERT (end >= start); char *num_str = malloc ((size_t) (end - start + 1)); KMS_ASSERT (num_str); bool ret; strncpy (num_str, str + start, (size_t) (end - start)); num_str[end - start] = '\0'; ret = _parse_int (num_str, result); free (num_str); return ret; } static bool _parse_hex_from_view (const char *str, int len, int *result) { KMS_ASSERT (len >= 0); *result = unhexlify (str, (size_t) len); if (*result < 0) { return false; } return true; } /* returns true if char is "linear white space". This *ignores* the folding case * of CRLF followed by WSP. See https://stackoverflow.com/a/21072806/774658 */ static bool _is_lwsp (char c) { return c == ' ' || c == 0x09 /* HTAB */; } /* parse a header line or status line. */ static kms_response_parser_state_t _parse_line (kms_response_parser_t *parser, int end) { int i = parser->start; const char *raw = parser->raw_response->str; kms_response_t *response = parser->response; if (parser->state == PARSING_STATUS_LINE) { /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ int j; int status; if (strncmp (raw + i, "HTTP/1.1 ", 9) != 0) { KMS_ERROR (parser, "Could not parse HTTP-Version."); return PARSING_DONE; } i += 9; for (j = i; j < end; j++) { if (raw[j] == ' ') break; } if (!_parse_int_from_view (raw, i, j, &status)) { KMS_ERROR (parser, "Could not parse Status-Code."); return PARSING_DONE; } response->status = status; /* ignore the Reason-Phrase. */ return PARSING_HEADER; } else if (parser->state == PARSING_HEADER) { /* Treating a header as: * message-header = field-name ":" [ field-value ] CRLF * This is not completely correct, and does not take folding into acct. * See https://tools.ietf.org/html/rfc822#section-3.1 */ int j; kms_request_str_t *key; kms_request_str_t *val; if (i == end) { /* empty line, this signals the start of the body. */ if (parser->transfer_encoding_chunked) { return PARSING_CHUNK_LENGTH; } return PARSING_BODY; } for (j = i; j < end; j++) { if (raw[j] == ':') break; } if (j == end) { KMS_ERROR (parser, "Could not parse header, no colon found."); return PARSING_DONE; } key = kms_request_str_new_from_chars (raw + i, j - i); i = j + 1; /* remove leading and trailing whitespace from the value. */ for (j = i; j < end; j++) { if (!_is_lwsp (raw[j])) break; } i = j; /* find the end of the header by backtracking. */ for (j = end; j > i; j--) { if (!_is_lwsp (raw[j])) break; } if (i == j) { val = kms_request_str_new (); } else { val = kms_request_str_new_from_chars (raw + i, j - i); } kms_kv_list_add (response->headers, key, val); /* if we have *not* read the Content-Length yet, check. */ if (parser->content_length == -1 && strcmp (key->str, "Content-Length") == 0) { if (!_parse_int (val->str, &parser->content_length) || parser->content_length > KMS_PARSER_MAX_RESPONSE_LEN || parser->content_length < 0) { KMS_ERROR (parser, "Could not parse Content-Length header."); kms_request_str_destroy (key); kms_request_str_destroy (val); return PARSING_DONE; } } if (0 == strcmp (key->str, "Transfer-Encoding")) { if (0 == strcmp (val->str, "chunked")) { parser->transfer_encoding_chunked = true; } else { KMS_ERROR (parser, "Unsupported Transfer-Encoding: %s", val->str); kms_request_str_destroy (key); kms_request_str_destroy (val); return PARSING_DONE; } } kms_request_str_destroy (key); kms_request_str_destroy (val); return PARSING_HEADER; } else if (parser->state == PARSING_CHUNK_LENGTH) { int result = 0; if (!_parse_hex_from_view (raw + i, end - i, &result) || result > KMS_PARSER_MAX_RESPONSE_LEN || result < 0) { KMS_ERROR (parser, "Failed to parse hex chunk length."); return PARSING_DONE; } parser->chunk_size = result; return PARSING_CHUNK; } return PARSING_DONE; } bool kms_response_parser_feed (kms_response_parser_t *parser, uint8_t *buf, uint32_t len) { kms_request_str_t *raw = parser->raw_response; int curr, body_read, chunk_read; if (parser->kmip) { return kms_kmip_response_parser_feed (parser->kmip, buf, len); } // As a precaution, limit the maximum size KMS response: if (len > KMS_PARSER_MAX_RESPONSE_LEN || raw->len > KMS_PARSER_MAX_RESPONSE_LEN - len) { KMS_ERROR (parser, "KMS response too large"); return false; } curr = (int) raw->len; kms_request_str_append_chars (raw, (char *) buf, len); /* process the new data appended. */ while (curr < (int) raw->len) { switch (parser->state) { case PARSING_STATUS_LINE: case PARSING_HEADER: case PARSING_CHUNK_LENGTH: /* find the next \r\n. */ if (curr && strncmp (raw->str + (curr - 1), "\r\n", 2) == 0) { parser->state = _parse_line (parser, curr - 1); if (parser->failed) { return false; } parser->start = curr + 1; } curr++; if (parser->state == PARSING_BODY && parser->content_length <= 0) { /* Ok, no Content-Length header, or explicitly 0, so empty body */ parser->response->body = kms_request_str_new (); parser->state = PARSING_DONE; } break; case PARSING_BODY: body_read = (int) raw->len - parser->start; if (parser->content_length == -1 || body_read > parser->content_length) { KMS_ERROR (parser, "Unexpected: exceeded content length"); return false; } /* check if we have the entire body. */ if (body_read == parser->content_length) { parser->response->body = kms_request_str_new_from_chars ( raw->str + parser->start, parser->content_length); parser->state = PARSING_DONE; } curr = (int) raw->len; break; case PARSING_CHUNK: chunk_read = (int) raw->len - parser->start; /* check if we've read the full chunk and the trailing \r\n */ if (chunk_read >= parser->chunk_size + 2) { if (!parser->response->body) { parser->response->body = kms_request_str_new (); } kms_request_str_append_chars (parser->response->body, raw->str + parser->start, parser->chunk_size); curr = parser->start + parser->chunk_size + 2; parser->start = curr; if (parser->chunk_size == 0) { /* last chunk. */ parser->state = PARSING_DONE; } else { parser->state = PARSING_CHUNK_LENGTH; } } else { curr = (int) raw->len; } break; case PARSING_DONE: KMS_ERROR (parser, "Unexpected extra HTTP content"); return false; default: KMS_ASSERT (false && "Invalid kms_response_parser HTTP state"); } } if (parser->failed) { return false; } return true; } /* steals the response from the parser. */ kms_response_t * kms_response_parser_get_response (kms_response_parser_t *parser) { kms_response_t *response; if (parser->kmip) { return kms_kmip_response_parser_get_response (parser->kmip); } response = parser->response; parser->response = NULL; /* reset the parser. */ _parser_destroy (parser); _parser_init (parser); return response; } int kms_response_parser_status (kms_response_parser_t *parser) { if (!parser) { return 0; } if (parser->kmip) { KMS_ERROR (parser, "kms_response_parser_status not applicable to KMIP"); return 0; } if (!parser->response) { return 0; } return parser->response->status; } const char * kms_response_parser_error (kms_response_parser_t *parser) { if (!parser) { return NULL; } if (parser->kmip) { return kms_kmip_response_parser_error (parser->kmip); } return parser->error; } void kms_response_parser_destroy (kms_response_parser_t *parser) { _parser_destroy (parser); free (parser); } libmongocrypt-1.19.0/kms-message/src/sort.c000066400000000000000000000050741521103432300206300ustar00rootroot00000000000000/* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Peter McIlroy. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * This code is originally from: * https://github.com/freebsd/freebsd/blob/e7c6cef9514d3bb1f14a30a5ee871231523e43db/lib/libc/stdlib/merge.c */ #include "sort.h" // #include /* * This is to avoid out-of-bounds addresses in sorting the * last 4 elements. */ #define CMP(x, y) cmp (x, y) #define swap(a, b) \ if (1) { \ s = b; \ i = size; \ do { \ tmp = *a; \ *a++ = *s; \ *s++ = tmp; \ } while (--i); \ a -= size; \ } else \ ((void)0) void insertionsort (unsigned char *a, size_t n, size_t size, cmp_t cmp) { unsigned char *ai, *s, *t, *u, tmp; size_t i; for (ai = a + size; --n >= 1; ai += size) for (t = ai; t > a; t -= size) { u = t - size; if (CMP (u, t) <= 0) break; swap (u, t); } } libmongocrypt-1.19.0/kms-message/src/sort.h000066400000000000000000000013621521103432300206310ustar00rootroot00000000000000/* * Copyright 2018-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 typedef int (*cmp_t) (const void *, const void *); void insertionsort (unsigned char *a, size_t n, size_t size, cmp_t cmp); libmongocrypt-1.19.0/kms-message/test/000077500000000000000000000000001521103432300176575ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/connection_close/000077500000000000000000000000001521103432300232035ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/connection_close/connection_close.sreq000066400000000000000000000004621521103432300274250ustar00rootroot00000000000000POST / HTTP/1.1 Connection:close Host:foo-service.foo-region.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=foo-akid/20150830/foo-region/foo-service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8b264695a2fd7fe28b309c611e0770da5a4c5ca0c040209941f78d773d9297b2libmongocrypt-1.19.0/kms-message/test/content_length/000077500000000000000000000000001521103432300226725ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/content_length/content_length.sreq000066400000000000000000000005171521103432300266040ustar00rootroot00000000000000POST / HTTP/1.1 Content-Length:11 Host:foo-service.foo-region.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=foo-akid/20150830/foo-region/foo-service/aws4_request, SignedHeaders=content-length;host;x-amz-date, Signature=9025f1937fb114f77158a8bf0170e1ec1a9aa29806bc7800ca850966cd32920b foo-payloadlibmongocrypt-1.19.0/kms-message/test/decrypt/000077500000000000000000000000001521103432300213315ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/decrypt/decrypt.creq000066400000000000000000000004401521103432300236550ustar00rootroot00000000000000POST / content-length:234 content-type:application/x-amz-json-1.1 host:service.us-east-1.amazonaws.com x-amz-date:20150830T123600Z x-amz-target:TrentService.Decrypt content-length;content-type;host;x-amz-date;x-amz-target 48384de310621c850968f3948bddd004ecf5cae592a24530f778e4d190bd9dbclibmongocrypt-1.19.0/kms-message/test/decrypt/decrypt.sreq000066400000000000000000000012141521103432300236750ustar00rootroot00000000000000POST / HTTP/1.1 Content-Length:234 Content-Type:application/x-amz-json-1.1 Host:service.us-east-1.amazonaws.com X-Amz-Date:20150830T123600Z X-Amz-Target:TrentService.Decrypt Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-target, Signature=977a49f82aa74b38944cbd299880dcc3ec2bbcd953c36b1a19a3cde56f415f31 {"CiphertextBlob": "AQICAHjzjtjUxrr7oc/BHmjyoZGeNk10osSeMGcIUzMNzeDJGwFgMNRznpAfp0NVhCb51fCxAAAAZDBiBgkqhkiG9w0BBwagVTBTAgEAME4GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMoscSHCU4DuwIHyMJAgEQgCFhA83L4qw2T3PbG3MuM9pFUfTNwP/S4bnEwg6/U5BGGEI="}libmongocrypt-1.19.0/kms-message/test/encrypt/000077500000000000000000000000001521103432300213435ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/encrypt/encrypt.creq000066400000000000000000000004371521103432300237070ustar00rootroot00000000000000POST / content-length:45 content-type:application/x-amz-json-1.1 host:service.us-east-1.amazonaws.com x-amz-date:20150830T123600Z x-amz-target:TrentService.Encrypt content-length;content-type;host;x-amz-date;x-amz-target 5fc6e5b49ee8c6926b9eeefb69e108be4c8e1cf81d8af8f51c7d5b992afd029clibmongocrypt-1.19.0/kms-message/test/encrypt/encrypt.sreq000066400000000000000000000007161521103432300237270ustar00rootroot00000000000000POST / HTTP/1.1 Content-Length:45 Content-Type:application/x-amz-json-1.1 Host:service.us-east-1.amazonaws.com X-Amz-Date:20150830T123600Z X-Amz-Target:TrentService.Encrypt Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-target, Signature=28d8a139a4a7da0bd7082979fa938ce20514e0500cdd441fd46f702ae3d3cfd2 {"Plaintext": "Zm9vYmFy", "KeyId": "alias/1"}libmongocrypt-1.19.0/kms-message/test/example-chunked-response.bin000066400000000000000000000015561521103432300252660ustar00rootroot00000000000000HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Vary: X-Origin Vary: Referer Date: Thu, 24 Sep 2020 14:21:44 GMT Server: scaffolding on HTTPServer2 Cache-Control: private X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Alt-Svc: h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43" Accept-Ranges: none Vary: Origin,Accept-Encoding Connection: close Transfer-Encoding: chunked 105 {"access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","expires_in":3599,"token_type":"Bearer"} 0 libmongocrypt-1.19.0/kms-message/test/example-multi-chunked-response.bin000066400000000000000000000015631521103432300264140ustar00rootroot00000000000000HTTP/1.1 200 OK Content-Type: application/json; charset=UTF-8 Vary: X-Origin Vary: Referer Date: Thu, 24 Sep 2020 14:21:44 GMT Server: scaffolding on HTTPServer2 Cache-Control: private X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Alt-Svc: h3-Q050=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43" Accept-Ranges: none Vary: Origin,Accept-Encoding Connection: close Transfer-Encoding: chunked DD {"access_token":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 28 "expires_in":3599,"token_type":"Bearer"} 0 libmongocrypt-1.19.0/kms-message/test/example-response.bin000077500000000000000000000007111521103432300236420ustar00rootroot00000000000000HTTP/1.1 200 OK x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e Content-Type: application/x-amz-json-1.1 Content-Length: 319 {"CiphertextBlob":"AQICAHifzrL6n/3uqZyz+z1bJj80DhqPcSAibAaIoYc+HOVP6QEplwbM0wpvU5zsQG/1SBKvAAAAZDBiBgkqhkiG9w0BBwagVTBTAgEAME4GCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM5syMJE7RodxDaqYqAgEQgCHMFCnFso4Lih0CNbLT1kiET0hQyzjgoa9733353GQkGlM=","KeyId":"arn:aws:kms:us-east-1:524754917239:key/bd05530b-0a7f-4fbd-8362-ab3667370db0"}libmongocrypt-1.19.0/kms-message/test/host/000077500000000000000000000000001521103432300206345ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/host/host.sreq000066400000000000000000000004411521103432300225040ustar00rootroot00000000000000POST / HTTP/1.1 Host:foo-service.foo-region.amazonaws.com X-Amz-Date:20150830T123600Z Authorization: AWS4-HMAC-SHA256 Credential=foo-akid/20150830/foo-region/foo-service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8b264695a2fd7fe28b309c611e0770da5a4c5ca0c040209941f78d773d9297b2libmongocrypt-1.19.0/kms-message/test/multibyte/000077500000000000000000000000001521103432300216755ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/multibyte/multibyte.creq000066400000000000000000000003321521103432300245650ustar00rootroot00000000000000GET /%E2%82%AC/ euro=%E2%82%AC content-length:4 host:€.€.amazonaws.com x-amz-date:20150830T123600Z €:€asdf€ content-length;host;x-amz-date;€ 5bf858e4b892817978d798623768af7080d99ded72501cf15374e12861725f12libmongocrypt-1.19.0/kms-message/test/multibyte/multibyte.sreq000066400000000000000000000005121521103432300246050ustar00rootroot00000000000000GET /€/?euro=€ HTTP/1.1 Content-Length:4 Host:€.€.amazonaws.com X-Amz-Date:20150830T123600Z €:€asdf€ Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/€/€/aws4_request, SignedHeaders=content-length;host;x-amz-date;€, Signature=88d2d4b0639c6b6db1ce366d2578b8407562caa905fc375fdf1ddb7f0b789f5b €libmongocrypt-1.19.0/kms-message/test/test_kmip_reader_writer.c000066400000000000000000000441541521103432300247500ustar00rootroot00000000000000/* * 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 "test_kms_assert.h" #include "test_kms_util.h" #include "kms_kmip_reader_writer_private.h" #include #include typedef struct { char *desc; char *expected_hex; } kms_kmip_writer_test_case_t; static void kms_kmip_writer_test_evaluate (kmip_writer_t *writer, const char *expected_hex_in, char *desc) { char *expected_hex; const uint8_t *actual_buf; size_t actual_len; char *actual_hex; expected_hex = copy_and_filter_hex (expected_hex_in); actual_buf = kmip_writer_get_buffer (writer, &actual_len); actual_hex = data_to_hex (actual_buf, actual_len); if (0 != strcmp (expected_hex, actual_hex)) { TEST_STDERR_PRINTF ( "expected '%s' but got '%s' for test description: %s\n", expected_hex, actual_hex, desc); abort (); } free (actual_hex); free (expected_hex); } void kms_kmip_writer_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_writer_test (void) { kmip_writer_t *writer; /* The following test cases come from section 9.1.2 of * http://docs.oasis-open.org/kmip/spec/v1.4/os/kmip-spec-v1.4-os.html */ writer = kmip_writer_new (); kmip_writer_write_integer (writer, KMIP_TAG_CompromiseDate, 8); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 02 | 00 00 00 04 | 00 00 00 08 00 00 00 00", "An Integer containing the decimal value 8"); kmip_writer_destroy (writer); writer = kmip_writer_new (); kmip_writer_write_long_integer ( writer, KMIP_TAG_CompromiseDate, 123456789000000000LL); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 03 | 00 00 00 08 | 01 B6 9B 4B A5 74 92 00", "A Long Integer containing the decimal value 123456789000000000"); kmip_writer_destroy (writer); /* BigInteger is not implemented. */ writer = kmip_writer_new (); kmip_writer_write_enumeration (writer, KMIP_TAG_CompromiseDate, 255); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 05 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", "An Enumeration with value 255"); kmip_writer_destroy (writer); writer = kmip_writer_new (); kmip_writer_write_bool (writer, KMIP_TAG_CompromiseDate, true); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 06 | 00 00 00 08 | 00 00 00 00 00 00 00 01", "An boolean containing the value true"); kmip_writer_destroy (writer); writer = kmip_writer_new (); kmip_writer_write_string ( writer, KMIP_TAG_CompromiseDate, "Hello World", 11); kms_kmip_writer_test_evaluate (writer, "42 00 20 | 07 | 00 00 00 0B | 48 65 6C 6C " "6F 20 57 6F 72 6C 64 00 00 00 00 00", "A Text String with the value 'Hello World'"); kmip_writer_destroy (writer); writer = kmip_writer_new (); kmip_writer_write_bytes (writer, KMIP_TAG_CompromiseDate, "\01\02\03", 3); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 08 | 00 00 00 03 | 01 02 03 00 00 00 00 00", "A Byte String with the value { 0x01, 0x02, 0x03 }"); kmip_writer_destroy (writer); writer = kmip_writer_new (); kmip_writer_write_datetime ( writer, KMIP_TAG_CompromiseDate, 0x0000000047DA67F8LL); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 09 | 00 00 00 08 | 00 00 00 00 47 DA 67 F8", "A Date-Time, containing the value for Friday, March 14, 2008, 11:56:40 " "GMT"); kmip_writer_destroy (writer); /* Interval is not implemented. */ writer = kmip_writer_new (); kmip_writer_begin_struct (writer, KMIP_TAG_CompromiseDate); kmip_writer_write_enumeration ( writer, KMIP_TAG_ApplicationSpecificInformation, 254); kmip_writer_write_integer (writer, KMIP_TAG_ArchiveDate, 255); kmip_writer_close_struct (writer); kms_kmip_writer_test_evaluate ( writer, "42 00 20 | 01 | 00 00 00 20 | 42 00 04 | 05 | 00 00 00 04 | 00 00 00 FE " "00 00 00 00 | 42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", "A Structure containing an Enumeration, value 254, followed by an " "Integer, value 255, having tags 420004 and 420005 respectively"); kmip_writer_destroy (writer); } void kms_kmip_reader_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; kmip_tag_type_t tag; kmip_item_type_t type; uint32_t length; int32_t i32; int64_t i64; uint32_t u32; uint8_t *ptr; bool b; /* The following test cases come from section 9.1.2 of * http://docs.oasis-open.org/kmip/spec/v1.4/os/kmip-spec-v1.4-os.html */ /* An Integer containing the decimal value 8 */ data = hex_to_data ("42 00 20 | 02 | 00 00 00 04 | 00 00 00 08 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Integer); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_integer (reader, &i32)); ASSERT (i32 == 8); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* A Long Integer containing the decimal value 123456789000000000 */ data = hex_to_data ("42 00 20 | 03 | 00 00 00 08 | 01 B6 9B 4B A5 74 92 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_LongInteger); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 8); ASSERT (kmip_reader_read_long_integer (reader, &i64)); ASSERT (i64 == 123456789000000000LL); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* Big Integer is not implemented. */ /* An Enumeration with value 255 */ data = hex_to_data ("42 00 20 | 05 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Enumeration); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_enumeration (reader, &u32)); ASSERT (u32 == 255); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* A boolean with value true */ data = hex_to_data ("42 00 20 | 06 | 00 00 00 08 | 00 00 00 00 00 00 00 01", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Boolean); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 8); ASSERT (kmip_reader_read_bool (reader, &b)); ASSERT (b); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* A Text String with the value 'Hello World' */ data = hex_to_data ("42 00 20 | 07 | 00 00 00 0B | 48 65 6C " "6C 6F 20 57 6F 72 6C 64 00 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_TextString); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 11); ASSERT (kmip_reader_read_string (reader, &ptr, length)); ASSERT (0 == strncmp ("Hello World", (const char *) ptr, length)); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* A Byte String with the value { 0x01, 0x02, 0x03 } */ data = hex_to_data ("42 00 20 | 08 | 00 00 00 03 | 01 02 03 00 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_ByteString); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 3); ASSERT (kmip_reader_read_bytes (reader, &ptr, length)); ASSERT (0 == strncmp ("\01\02\03", (const char *) ptr, length)); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* A Date-Time, containing the value for Friday, March 14, 2008, 11:56:40 GMT */ data = hex_to_data ("42 00 20 | 09 | 00 00 00 08 | 00 00 00 00 47 DA 67 F8", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_DateTime); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 8); kmip_reader_read_long_integer (reader, &i64); ASSERT (i64 == 0x47DA67F8); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* Interval is not implemented. */ /* A Structure containing an Enumeration, value 254, followed by an Integer, * value 255, having tags 420004 and 420005 respectively */ data = hex_to_data ( "42 00 20 | 01 | 00 00 00 20 | 42 00 04 | 05 | 00 00 00 04 | 00 00 00 FE " "00 00 00 00 | 42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Structure); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 0x20); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_ApplicationSpecificInformation); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Enumeration); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_enumeration (reader, &u32)); ASSERT (u32 == 254); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_ArchiveDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Integer); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_integer (reader, &i32)); ASSERT (i32 == 255); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_negative_int_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_negative_int_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; kmip_tag_type_t tag; kmip_item_type_t type; uint32_t length; int32_t i32; /* Test reading the integer -1. */ data = hex_to_data ("42 00 20 | 02 | 00 00 00 04 | FF FF FF FF 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Integer); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_integer (reader, &i32)); ASSERT (i32 == -1); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); /* Test reading the integer INT32_MIN (-2^31). */ data = hex_to_data ("42 00 20 | 02 | 00 00 00 04 | 80 00 00 00 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_read_tag (reader, &tag)); ASSERT (tag == KMIP_TAG_CompromiseDate); ASSERT (kmip_reader_read_type (reader, &type)); ASSERT (type == KMIP_ITEM_TYPE_Integer); ASSERT (kmip_reader_read_length (reader, &length)); ASSERT (length == 4); ASSERT (kmip_reader_read_integer (reader, &i32)); ASSERT (i32 == INT32_MIN); ASSERT (!kmip_reader_has_data (reader)); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_find_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_find_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; bool found; size_t pos = 0; size_t len = 0; /* A Structure containing an Enumeration, value 254, followed by an Integer, * value 255, having tags 420004 and 420005 respectively */ data = hex_to_data ( "42 00 20 | 01 | 00 00 00 20 | 42 00 04 | 05 | 00 00 00 04 | 00 00 00 FE " "00 00 00 00 | 42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); /* Finds the top-level Structure. */ found = kmip_reader_find ( reader, KMIP_TAG_CompromiseDate, KMIP_ITEM_TYPE_Structure, &pos, &len); ASSERT (found); ASSERT (pos == 0); ASSERT (len == 32); /* Mismatched tag does not find the Structure. */ found = kmip_reader_find ( reader, KMIP_TAG_ActivationDate, KMIP_ITEM_TYPE_Structure, &pos, &len); ASSERT (!found); /* Mismatched type does not find the Structure. */ found = kmip_reader_find ( reader, KMIP_TAG_CompromiseDate, KMIP_ITEM_TYPE_Integer, &pos, &len); ASSERT (!found); /* Values nested within the Structure are not found. */ found = kmip_reader_find (reader, KMIP_TAG_ApplicationSpecificInformation, KMIP_ITEM_TYPE_Enumeration, &pos, &len); ASSERT (!found); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_find_and_recurse_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_find_and_recurse_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; bool found; size_t pos = 0; size_t len = 0; /* A Structure containing an Enumeration, value 254, followed by an Integer, * value 255, having tags 420004 and 420005 respectively */ data = hex_to_data ( "42 00 20 | 01 | 00 00 00 20 | 42 00 04 | 05 | 00 00 00 04 | 00 00 00 FE " "00 00 00 00 | 42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); ASSERT (kmip_reader_find_and_recurse (reader, KMIP_TAG_CompromiseDate)); /* Values nested within the Structure are found. */ found = kmip_reader_find (reader, KMIP_TAG_ApplicationSpecificInformation, KMIP_ITEM_TYPE_Enumeration, &pos, &len); ASSERT (found); ASSERT (pos == 8); ASSERT (len == 4); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_find_and_recurse_invalid_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_find_and_recurse_invalid_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; // Structure with overly large length: data = hex_to_data ( "42 00 20 | 01 | 00 00 00 FF", // Struct with bad length &datalen); reader = kmip_reader_new (data, datalen); // Expect error: ASSERT (!kmip_reader_find_and_recurse (reader, KMIP_TAG_CompromiseDate)); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_find_and_read_enum_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_find_and_read_enum_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; bool found; uint32_t value; /* An Integer, value 255, followed by an Enumeration, value 254 having tags * 420005 and 420004 respectively. */ data = hex_to_data ( "42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00 | 42 00 04 | 05 | " "00 00 00 04 | 00 00 00 FE 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); found = kmip_reader_find_and_read_enum ( reader, KMIP_TAG_ApplicationSpecificInformation, &value); ASSERT (found); ASSERT (value == 254); /* The Integer should not be found. */ found = kmip_reader_find_and_read_enum (reader, KMIP_TAG_ArchiveDate, &value); ASSERT (!found); kmip_reader_destroy (reader); free (data); } void kms_kmip_reader_find_and_read_bytes_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_reader_find_and_read_bytes_test (void) { uint8_t *data; size_t datalen; kmip_reader_t *reader; bool found; uint8_t *outptr; size_t outlen; /* An Integer, value 255, followed by ByteString of value 0x1122 having tags * 420005 and 420004 respectively. */ data = hex_to_data ( "42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00 | 42 00 04 | 08 | " "00 00 00 02 | 11 22 00 00 00 00 00 00", &datalen); reader = kmip_reader_new (data, datalen); found = kmip_reader_find_and_read_bytes ( reader, KMIP_TAG_ApplicationSpecificInformation, &outptr, &outlen); ASSERT (found); ASSERT (outlen == 2); ASSERT (outptr[0] == 0x11); ASSERT (outptr[1] == 0x22); /* The Integer should not be found. */ found = kmip_reader_find_and_read_bytes ( reader, KMIP_TAG_ArchiveDate, &outptr, &outlen); ASSERT (!found); kmip_reader_destroy (reader); free (data); } libmongocrypt-1.19.0/kms-message/test/test_kms_assert.h000066400000000000000000000201641521103432300232450ustar00rootroot00000000000000/* * 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. */ #ifndef TEST_KMS_ASSERT_H #define TEST_KMS_ASSERT_H #include "kms_request_str.h" #include "test_kms_util.h" #include #include // TEST_PRINTF ensures stdout and stderr are flushed. #define TEST_PRINTF(...) \ if (1) { \ fflush (stderr); \ fprintf (stdout, __VA_ARGS__); \ fflush (stdout); \ } else \ ((void) 0) // TEST_STDERR_PRINTF ensures stdout and stderr are flushed. #define TEST_STDERR_PRINTF(...) \ if (1) { \ fflush (stdout); \ fprintf (stderr, __VA_ARGS__); \ fflush (stderr); \ } else \ ((void) 0) #define TEST_ERROR(...) \ do { \ TEST_STDERR_PRINTF ( \ "test error %s:%d %s(): ", __FILE__, __LINE__, __func__); \ TEST_STDERR_PRINTF (__VA_ARGS__); \ TEST_STDERR_PRINTF ("\n"); \ abort (); \ } while (0) #define ASSERT(stmt) \ if (!(stmt)) { \ TEST_ERROR ("statement failed %s", #stmt); \ } else \ ((void)0) #define ASSERT_CMPSTR_WITH_LEN(_expect, _expect_len, _actual, _actual_len) \ do { \ kms_request_str_t *_expect_str = \ kms_request_str_new_from_chars ((_expect), _expect_len); \ kms_request_str_t *_actual_str = \ kms_request_str_new_from_chars ((_actual), _actual_len); \ if (0 != strcmp (_expect_str->str, _actual_str->str)) { \ TEST_ERROR ( \ "strings not equal:\n%s\n%s", _expect_str->str, _actual_str->str); \ } \ kms_request_str_destroy (_actual_str); \ kms_request_str_destroy (_expect_str); \ } while (0) #define ASSERT_CMPSTR(_expect, _actual) \ ASSERT_CMPSTR_WITH_LEN ( \ (_expect), strlen (_expect), (_actual), strlen (_actual)) #define ASSERT_CONTAINS(_a, _b) \ do { \ kms_request_str_t *_a_str = kms_request_str_new_from_chars ((_a), -1); \ kms_request_str_t *_b_str = kms_request_str_new_from_chars ((_b), -1); \ kms_request_str_t *_a_lower = kms_request_str_new (); \ kms_request_str_t *_b_lower = kms_request_str_new (); \ kms_request_str_append_lowercase (_a_lower, (_a_str)); \ kms_request_str_append_lowercase (_b_lower, (_b_str)); \ if (NULL == strstr ((_a_lower->str), (_b_lower->str))) { \ TEST_ERROR ("string \"%s\" does not contain \"%s\"", _a, _b); \ } \ kms_request_str_destroy (_a_str); \ kms_request_str_destroy (_b_str); \ kms_request_str_destroy (_a_lower); \ kms_request_str_destroy (_b_lower); \ } while (0) #define ASSERT_CMPINT(_a, _operator, _b) \ do { \ int _a_int = _a; \ int _b_int = _b; \ if (!(_a_int _operator _b_int)) { \ TEST_ERROR ( \ "comparison failed: %d %s %d", _a_int, #_operator, _b_int); \ } \ } while (0) #define ASSERT_CMPBYTES( \ expected_bytes, expected_len, actual_bytes, actual_len) \ do { \ char *_actual_hex = data_to_hex (actual_bytes, actual_len); \ char *_expected_hex = data_to_hex (expected_bytes, expected_len); \ ASSERT_CMPSTR (_actual_hex, _expected_hex); \ free (_actual_hex); \ free (_expected_hex); \ } while (0) #define ASSERT_REQUEST_OK(req) \ do { \ if (kms_request_get_error (req)) { \ TEST_ERROR ("expected request ok but got error: %s", \ kms_request_get_error (req)); \ } \ } while (0) #define ASSERT_REQUEST_ERROR(req, expect_substring) \ do { \ if (!kms_request_get_error (req)) { \ TEST_ERROR ("expected request error but got ok"); \ } \ const char *_error_str = kms_request_get_error (req); \ ASSERT_CONTAINS (_error_str, expect_substring); \ } while (0) #define ASSERT_RESPONSE_OK(req) \ do { \ if (kms_response_get_error (req)) { \ TEST_ERROR ("expected response ok but got error: %s", \ kms_response_get_error (req)); \ } \ } while (0) #define ASSERT_RESPONSE_ERROR(req, expect_substring) \ do { \ if (!kms_response_get_error (req)) { \ TEST_ERROR ("expected response error but got ok"); \ } \ const char *_error_str = kms_response_get_error (req); \ ASSERT_CONTAINS (_error_str, expect_substring); \ } while (0) #define ASSERT_PARSER_OK(parser) \ do { \ if (kms_response_parser_error (parser)) { \ TEST_ERROR ("expected parser ok but got error: %s", \ kms_response_parser_error (parser)); \ } \ } while (0) #define ASSERT_PARSER_ERROR(parser, expect_substring) \ do { \ if (!kms_response_parser_error (parser)) { \ TEST_ERROR ("expected parser error but got ok"); \ } \ const char *_error_str = kms_response_parser_error (parser); \ ASSERT_CONTAINS (_error_str, expect_substring); \ } while (0) #endif /* TEST_KMS_ASSERT_H */ libmongocrypt-1.19.0/kms-message/test/test_kms_azure_online.c000066400000000000000000000200201521103432300244200ustar00rootroot00000000000000/* * Copyright 2020-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 "kms_message/kms_azure_request.h" #include "kms_message/kms_b64.h" #include "kms_message/kms_request.h" #include "kms_message/kms_response.h" #include "kms_message/kms_response_parser.h" #define MONGOC_LOG_DOMAIN "test_kms_azure_online" #include #include "test_kms_assert.h" #include #include "test_kms_online_util.h" #define SCOPE "https%3A%2F%2Fvault.azure.net%2F.default" /* Define TEST_TRACING_INSECURE in compiler flags to enable * log output with sensitive information (for debugging). */ #ifdef TEST_TRACING_INSECURE #define TEST_TRACE(...) MONGOC_DEBUG (__VA_ARGS__) #else #define TEST_TRACE(...) (void) 0 #endif typedef struct { char *tenant_id; char *client_id; char *client_secret; char *key_url; char *key_vault_url; char *key_path; char *key_host; char *key_name; char *key_version; } test_env_t; static char * test_getenv (const char *key) { char *value = getenv (key); if (!value) { TEST_STDERR_PRINTF ("Environment variable: %s not set (@@ctest-skip@@)\n", key); exit (2); } TEST_TRACE ("Env: %s = %s", key, value); return value; } static void test_env_init (test_env_t *test_env) { char *azure_domain = "vault.azure.net"; char *loc; test_env->tenant_id = test_getenv ("AZURE_TENANT_ID"); test_env->client_id = test_getenv ("AZURE_CLIENT_ID"); test_env->client_secret = test_getenv ("AZURE_CLIENT_SECRET"); test_env->key_url = test_getenv ("AZURE_KEY_URL"); test_env->key_name = test_getenv ("AZURE_KEY_NAME"); test_env->key_version = test_getenv ("AZURE_KEY_VERSION"); loc = strstr (test_env->key_url, azure_domain); ASSERT (loc); test_env->key_vault_url = bson_strndup ( test_env->key_url, strlen (azure_domain) + loc - test_env->key_url); test_env->key_path = bson_strdup (loc + strlen (azure_domain)); loc = strstr (test_env->key_vault_url, "//"); test_env->key_host = bson_strdup (loc + 2); } static void test_env_cleanup (test_env_t *test_env) { bson_free (test_env->key_vault_url); bson_free (test_env->key_path); bson_free (test_env->key_host); } /* Authenticate to Azure by sending an oauth request with client_id and client_secret (set in environment variables). Returns the base64url encoded bearer token that must be freed with bson_free. Subsequent requests to Azure can use the returned token by setting the header Authorization: Bearer . References: [1] https://docs.microsoft.com/en-us/azure/key-vault/general/authentication-requests-and-responses */ static char * azure_authenticate (void) { kms_request_t *req; kms_request_opt_t *opt; char *req_str; const char *res_str; bson_t *res_bson; bson_iter_t iter; char *bearer_token; kms_response_t *res; test_env_t test_env; test_env_init (&test_env); opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE); req = kms_azure_request_oauth_new ("login.microsoftonline.com", SCOPE, test_env.tenant_id, test_env.client_id, test_env.client_secret, opt); req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, "login.microsoftonline.com"); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s\n", res_str); ASSERT (kms_response_get_status (res) == 200); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); if (!bson_iter_init_find (&iter, res_bson, "access_token")) { TEST_ERROR ("could not find 'access_token' in HTTP response"); } bearer_token = bson_strdup (bson_iter_utf8 (&iter, NULL)); kms_request_free_string (req_str); kms_response_destroy (res); kms_request_destroy (req); bson_destroy (res_bson); test_env_cleanup (&test_env); kms_request_opt_destroy (opt); return bearer_token; } /* Test wrapping a 96 byte payload (the size of a data key) and unwrapping it * back. */ static void test_azure_wrapkey (void) { test_env_t test_env; kms_request_opt_t *opt; kms_request_t *req; char *req_str; char *bearer_token; kms_response_t *res; const char *res_str; uint8_t *encrypted_raw; size_t encrypted_raw_len; char *decrypted; bson_t *res_bson; bson_iter_t iter; uint8_t *key_data; char *key_data_b64url; int i; #define KEYLEN 96 key_data = bson_malloc0 (KEYLEN); for (i = 0; i < KEYLEN; i++) { key_data[i] = i; } key_data_b64url = kms_message_raw_to_b64url (key_data, KEYLEN); test_env_init (&test_env); bearer_token = azure_authenticate (); opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE); req = kms_azure_request_wrapkey_new (test_env.key_host, bearer_token, test_env.key_name, test_env.key_version, key_data, KEYLEN, opt); req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, test_env.key_host); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s", res_str); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); ASSERT (bson_iter_init_find (&iter, res_bson, "value")); encrypted_raw = kms_message_b64url_to_raw (bson_iter_utf8 (&iter, NULL), &encrypted_raw_len); ASSERT (encrypted_raw); bson_destroy (res_bson); bson_free (req_str); kms_request_destroy (req); kms_response_destroy (res); /* Send a request to unwrap the encrypted key. */ req = kms_azure_request_unwrapkey_new (test_env.key_host, bearer_token, test_env.key_name, test_env.key_version, encrypted_raw, encrypted_raw_len, opt); req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, test_env.key_host); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s", res_str); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); ASSERT (bson_iter_init_find (&iter, res_bson, "value")); decrypted = bson_strdup (bson_iter_utf8 (&iter, NULL)); ASSERT_CMPSTR (decrypted, key_data_b64url); bson_destroy (res_bson); kms_response_destroy (res); bson_free (req_str); bson_free (bearer_token); test_env_cleanup (&test_env); kms_request_destroy (req); bson_free (encrypted_raw); bson_free (key_data_b64url); bson_free (key_data); bson_free (decrypted); kms_request_opt_destroy (opt); } int main (int argc, char **argv) { kms_message_init (); test_azure_wrapkey (); return 0; } libmongocrypt-1.19.0/kms-message/test/test_kms_gcp_online.c000066400000000000000000000175761521103432300240710ustar00rootroot00000000000000/* * Copyright 2020-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 "kms_message/kms_gcp_request.h" #include "kms_message/kms_b64.h" #include "kms_message/kms_request.h" #include "kms_message/kms_response.h" #include "kms_message/kms_response_parser.h" #define MONGOC_LOG_DOMAIN "test_kms_gcp_online" #include #include "test_kms_assert.h" #include #include "test_kms_online_util.h" /* Define TEST_TRACING_INSECURE in compiler flags to enable * log output with sensitive information (for debugging). */ #ifdef TEST_TRACING_INSECURE #define TEST_TRACE(...) MONGOC_DEBUG (__VA_ARGS__) #else #define TEST_TRACE(...) (void) 0 #endif typedef struct { const char *email; const char *audience; const char *scope; const char *auth_host; const char *private_key_b64; const char *kms_host; const char *project_id; const char *location; const char *key_ring_name; const char *key_name; const char *key_version; } test_env_t; static char * test_getenv (const char *key) { char *value = getenv (key); if (!value) { TEST_STDERR_PRINTF ("Environment variable: %s not set (@@ctest-skip@@)\n", key); exit (2); } TEST_TRACE ("Env: %s = %s", key, value); return value; } static void test_env_init (test_env_t *test_env) { test_env->email = test_getenv ("GCP_EMAIL"); test_env->audience = test_getenv ("GCP_AUDIENCE"); test_env->scope = test_getenv ("GCP_SCOPE"); test_env->auth_host = test_getenv ("GCP_AUTH_HOST"); test_env->private_key_b64 = test_getenv ("GCP_PRIVATE_KEY_B64"); test_env->kms_host = test_getenv ("GCP_KMS_HOST"); test_env->project_id = test_getenv ("GCP_PROJECT_ID"); test_env->location = test_getenv ("GCP_LOCATION"); test_env->key_ring_name = test_getenv ("GCP_KEY_RING_NAME"); test_env->key_name = test_getenv ("GCP_KEY_NAME"); test_env->key_version = test_getenv ("GCP_KEY_VERSION"); } static char * gcp_authenticate (void) { kms_request_t *req; kms_request_opt_t *opt; char *req_str; const char *res_str; bson_t *res_bson; bson_iter_t iter; char *bearer_token; char *private_key_data; size_t private_key_len; kms_response_t *res; test_env_t test_env; test_env_init (&test_env); opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_GCP); private_key_data = (char *) kms_message_b64_to_raw (test_env.private_key_b64, &private_key_len); if (!private_key_data) { TEST_ERROR ("unable to base64 decode private key"); } req = kms_gcp_request_oauth_new (test_env.auth_host, test_env.email, test_env.audience, test_env.scope, private_key_data, private_key_len, opt); req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, "oauth2.googleapis.com"); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s\n", res_str); ASSERT (kms_response_get_status (res) == 200); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); if (!bson_iter_init_find (&iter, res_bson, "access_token")) { TEST_ERROR ("could not find 'access_token' in HTTP response"); } bearer_token = bson_strdup (bson_iter_utf8 (&iter, NULL)); kms_request_free_string (req_str); kms_response_destroy (res); kms_request_destroy (req); bson_destroy (res_bson); kms_request_opt_destroy (opt); bson_free (private_key_data); return bearer_token; } static void test_gcp (void) { test_env_t test_env; kms_request_opt_t *opt; kms_request_t *req; char *req_str; char *bearer_token; kms_response_t *res; const char *res_str; uint8_t *encrypted_raw; size_t encrypted_raw_len; char *decrypted; bson_t *res_bson; bson_iter_t iter; uint8_t *key_data; char *key_data_b64url; int i; #define KEYLEN 96 key_data = bson_malloc0 (KEYLEN); for (i = 0; i < KEYLEN; i++) { key_data[i] = i; } key_data_b64url = kms_message_raw_to_b64 (key_data, KEYLEN); test_env_init (&test_env); bearer_token = gcp_authenticate (); opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_GCP); req = kms_gcp_request_encrypt_new (test_env.kms_host, bearer_token, test_env.project_id, test_env.location, test_env.key_ring_name, test_env.key_name, test_env.key_version, key_data, KEYLEN, opt); ASSERT (req); if (kms_request_get_error (req)) { TEST_STDERR_PRINTF ("error: %s\n", kms_request_get_error (req)); ASSERT (false); } req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, test_env.kms_host); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s", res_str); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); ASSERT (bson_iter_init_find (&iter, res_bson, "ciphertext")); encrypted_raw = kms_message_b64_to_raw (bson_iter_utf8 (&iter, NULL), &encrypted_raw_len); ASSERT (encrypted_raw); bson_destroy (res_bson); bson_free (req_str); kms_request_destroy (req); kms_response_destroy (res); /* Send a request to decrypt the encrypted key. */ req = kms_gcp_request_decrypt_new (test_env.kms_host, bearer_token, test_env.project_id, test_env.location, test_env.key_ring_name, test_env.key_name, encrypted_raw, encrypted_raw_len, opt); req_str = kms_request_to_string (req); TEST_TRACE ("--> HTTP request:\n%s\n", req_str); res = send_kms_request (req, test_env.kms_host); res_str = kms_response_get_body (res, NULL); TEST_TRACE ("<-- HTTP response:\n%s", res_str); res_bson = bson_new_from_json ((const uint8_t *) res_str, strlen (res_str), NULL); ASSERT (res_bson); ASSERT (bson_iter_init_find (&iter, res_bson, "plaintext")); decrypted = bson_strdup (bson_iter_utf8 (&iter, NULL)); ASSERT_CMPSTR (decrypted, key_data_b64url); bson_destroy (res_bson); kms_response_destroy (res); bson_free (req_str); bson_free (bearer_token); kms_request_destroy (req); bson_free (encrypted_raw); bson_free (key_data_b64url); bson_free (key_data); bson_free (decrypted); kms_request_opt_destroy (opt); } int main (int argc, char **argv) { kms_message_init (); test_gcp (); return 0; } libmongocrypt-1.19.0/kms-message/test/test_kms_kmip_request.c000066400000000000000000000250011521103432300244420ustar00rootroot00000000000000#include "test_kms_assert.h" #include "kms_message/kms_kmip_request.h" /* */ #define REGISTER_SECRETDATA_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, 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 #define REGISTER_SECRETDATA_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 void kms_kmip_request_register_secretdata_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_request_register_secretdata_test (void) { kms_request_t *req; uint8_t secret_data[] = {REGISTER_SECRETDATA_SECRETDATA}; const uint8_t *actual_bytes; size_t actual_len; uint8_t expected_bytes[] = {REGISTER_SECRETDATA_REQUEST}; size_t expected_len = sizeof (expected_bytes); req = kms_kmip_request_register_secretdata_new ( NULL, secret_data, sizeof (secret_data)); ASSERT_REQUEST_OK (req); actual_bytes = kms_request_to_bytes (req, &actual_len); ASSERT (actual_bytes != NULL); ASSERT_CMPBYTES (expected_bytes, expected_len, actual_bytes, actual_len); kms_request_destroy (req); } void kms_kmip_request_register_secretdata_invalid_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_request_register_secretdata_invalid_test (void) { kms_request_t *req; uint8_t secret_data[KMS_KMIP_REQUEST_SECRETDATA_LENGTH] = {0}; req = kms_kmip_request_register_secretdata_new ( NULL, secret_data, KMS_KMIP_REQUEST_SECRETDATA_LENGTH - 1); ASSERT_REQUEST_ERROR (req, "SecretData length"); kms_request_destroy (req); } /* */ #define GET_REQUEST \ 0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x00, 0x88, 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, 0x40, 0x42, 0x00, 0x5c, 0x05, \ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, \ 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x94, 0x07, \ 0x00, 0x00, 0x00, 0x20, 0x37, 0x46, 0x4a, 0x59, 0x76, 0x6e, 0x56, 0x36, \ 0x58, 0x6b, 0x61, 0x55, 0x43, 0x57, 0x75, 0x59, 0x39, 0x36, 0x62, 0x43, \ 0x53, 0x63, 0x36, 0x41, 0x75, 0x68, 0x76, 0x6b, 0x50, 0x70, 0x71, 0x49 void kms_kmip_request_get_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_request_get_test (void) { kms_request_t *req; const uint8_t *actual_bytes; size_t actual_len; uint8_t expected_bytes[] = {GET_REQUEST}; size_t expected_len = sizeof (expected_bytes); static const char *const GET_UNIQUE_IDENTIFIER = "7FJYvnV6XkaUCWuY96bCSc6AuhvkPpqI"; req = kms_kmip_request_get_new (NULL, GET_UNIQUE_IDENTIFIER); ASSERT_REQUEST_OK (req); actual_bytes = kms_request_to_bytes (req, &actual_len); ASSERT (actual_bytes != NULL); ASSERT_CMPBYTES (actual_bytes, actual_len, expected_bytes, expected_len); kms_request_destroy (req); } /* */ #define ACTIVATE_REQUEST \ 0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x00, 0x88, 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, 0x40, 0x42, 0x00, 0x5c, 0x05, \ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, \ 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x94, 0x07, \ 0x00, 0x00, 0x00, 0x20, 0x37, 0x46, 0x4a, 0x59, 0x76, 0x6e, 0x56, 0x36, \ 0x58, 0x6b, 0x61, 0x55, 0x43, 0x57, 0x75, 0x59, 0x39, 0x36, 0x62, 0x43, \ 0x53, 0x63, 0x36, 0x41, 0x75, 0x68, 0x76, 0x6b, 0x50, 0x70, 0x71, 0x49 void kms_kmip_request_activate_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_request_activate_test (void) { kms_request_t *req; const uint8_t *actual_bytes; size_t actual_len; uint8_t expected_bytes[] = {ACTIVATE_REQUEST}; size_t expected_len = sizeof (expected_bytes); static const char *const ACTIVATE_UNIQUE_IDENTIFIER = "7FJYvnV6XkaUCWuY96bCSc6AuhvkPpqI"; req = kms_kmip_request_activate_new (NULL, ACTIVATE_UNIQUE_IDENTIFIER); ASSERT_REQUEST_OK (req); actual_bytes = kms_request_to_bytes (req, &actual_len); ASSERT (actual_bytes != NULL); ASSERT_CMPBYTES (actual_bytes, actual_len, expected_bytes, expected_len); kms_request_destroy (req); } libmongocrypt-1.19.0/kms-message/test/test_kms_kmip_response.c000066400000000000000000000246671521103432300246310ustar00rootroot00000000000000/* * 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 "test_kms_assert.h" #include "test_kms_util.h" #include "kms_message/kms_kmip_response.h" #include "kms_message_private.h" /* */ 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"; void kms_kmip_response_get_unique_identifier_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_get_unique_identifier_test (void) { kms_response_t res = {0}; char *actual_uid; res.provider = KMS_REQUEST_PROVIDER_KMIP; res.kmip.data = (uint8_t *) SUCCESS_REGISTER_RESPONSE; res.kmip.len = sizeof (SUCCESS_REGISTER_RESPONSE); actual_uid = kms_kmip_response_get_unique_identifier (&res); ASSERT_RESPONSE_OK (&res); ASSERT_CMPSTR (SUCCESS_REGISTER_RESPONSE_UNIQUE_IDENTIFIER, actual_uid); free (actual_uid); } /* */ 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}; void kms_kmip_response_get_secretdata_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_get_secretdata_test (void) { kms_response_t res = {0}; uint8_t *actual_secretdata; size_t actual_secretdata_len; res.provider = KMS_REQUEST_PROVIDER_KMIP; res.kmip.data = (uint8_t *) SUCCESS_GET_RESPONSE; res.kmip.len = sizeof (SUCCESS_GET_RESPONSE); actual_secretdata = kms_kmip_response_get_secretdata (&res, &actual_secretdata_len); ASSERT_RESPONSE_OK (&res); ASSERT_CMPBYTES (SUCCESS_GET_RESPONSE_SECRETDATA, sizeof (SUCCESS_GET_RESPONSE_SECRETDATA), actual_secretdata, actual_secretdata_len); free (actual_secretdata); } /* */ static const uint8_t ERROR_GET_RESPOSE_NOTFOUND[] = { 0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0xa8, 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, 0x57, 0x1e, 0x81, 0x42, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x50, 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, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x7d, 0x07, 0x00, 0x00, 0x00, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64}; void kms_kmip_response_get_secretdata_notfound_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_get_secretdata_notfound_test (void) { kms_response_t res = {0}; uint8_t *secretdata; size_t secretdata_len; res.provider = KMS_REQUEST_PROVIDER_KMIP; res.kmip.data = (uint8_t *) ERROR_GET_RESPOSE_NOTFOUND; res.kmip.len = sizeof (ERROR_GET_RESPOSE_NOTFOUND); secretdata = kms_kmip_response_get_secretdata (&res, &secretdata_len); ASSERT_RESPONSE_ERROR (&res, "ResultReasonItemNotFound"); ASSERT (NULL == secretdata); } libmongocrypt-1.19.0/kms-message/test/test_kms_kmip_response_parser.c000066400000000000000000000165061521103432300261760ustar00rootroot00000000000000/* * 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 "test_kms_assert.h" #include "test_kms_util.h" #include "kms_message/kms_kmip_response_parser.h" #include "kms_kmip_response_parser_private.h" /* The following sample data come from section 9.1.2 of * http://docs.oasis-open.org/kmip/spec/v1.4/os/kmip-spec-v1.4-os.html. The * data describes "A Structure containing an Enumeration, value 254, followed * by an Integer, value 255, having tags 420004 and 420005 respectively." */ static const char *const SAMPLE_KMIP = "42 00 20 | 01 | 00 00 00 20 | 42 00 04 | 05 | 00 00 00 04 | 00 00 00 FE 00 " "00 00 00 | 42 00 05 | 02 | 00 00 00 04 | 00 00 00 FF 00 00 00 00"; /* SAMPLE_KMIP_FIRST_LENGTH is the length of message after the first tag, type, * and length. */ static const int SAMPLE_KMIP_FIRST_LENGTH = 32; /* SAMPLE_KMIP_LARGE_LENGTH is a byte size larger than the message. */ static const int SAMPLE_KMIP_LARGE_LENGTH = 1024; void kms_kmip_response_parser_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_parser_test (void) { kms_response_parser_t *parser; uint8_t *data; size_t outlen; int32_t want_bytes; kms_response_t *res; bool ok; data = hex_to_data (SAMPLE_KMIP, &outlen); parser = kms_kmip_response_parser_new (NULL); want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); ASSERT_CMPINT (KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH, ==, want_bytes); /* A smaller maximum size caps the requested bytes. */ want_bytes = kms_response_parser_wants_bytes (parser, 1); ASSERT_CMPINT (1, ==, want_bytes); /* Feed one byte */ ok = kms_response_parser_feed (parser, data, 1); ASSERT_PARSER_OK (parser); ASSERT (ok); want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); ASSERT_CMPINT (KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH - 1, ==, want_bytes); /* Feed the remaining bytes. */ ok = kms_response_parser_feed ( parser, data + 1, KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH - 1); ASSERT_PARSER_OK (parser); ASSERT (ok); /* The parser knows first length. Expect the parser wants the remaining * length. */ want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); ASSERT_CMPINT (want_bytes, ==, SAMPLE_KMIP_FIRST_LENGTH); ok = kms_response_parser_feed (parser, data + KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH, SAMPLE_KMIP_FIRST_LENGTH); ASSERT_PARSER_OK (parser); ASSERT (ok); /* Parser has full message. */ want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); ASSERT_CMPINT (want_bytes, ==, 0); res = kms_response_parser_get_response (parser); ASSERT_PARSER_OK (parser); ASSERT (res); kms_response_destroy (res); kms_response_parser_destroy (parser); free (data); } static void feed_full_response (kms_response_parser_t *parser, uint8_t *data) { uint32_t i = 0; int32_t want_bytes; bool ok; want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); ASSERT_CMPINT (KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH, ==, want_bytes); while (want_bytes > 0) { ok = kms_response_parser_feed (parser, data + i, want_bytes); ASSERT_PARSER_OK (parser); ASSERT (ok); i += want_bytes; want_bytes = kms_response_parser_wants_bytes (parser, SAMPLE_KMIP_LARGE_LENGTH); } } void kms_kmip_response_parser_reuse_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_parser_reuse_test (void) { kms_response_parser_t *parser; uint8_t *data; size_t outlen; kms_response_t *res; data = hex_to_data (SAMPLE_KMIP, &outlen); parser = kms_kmip_response_parser_new (NULL); feed_full_response (parser, data); ASSERT_PARSER_OK (parser); res = kms_response_parser_get_response (parser); ASSERT_PARSER_OK (parser); ASSERT (res); kms_response_destroy (res); /* Feed another full response. */ feed_full_response (parser, data); ASSERT_PARSER_OK (parser); res = kms_response_parser_get_response (parser); ASSERT_PARSER_OK (parser); ASSERT (res); kms_response_destroy (res); kms_response_parser_destroy (parser); free (data); } void kms_kmip_response_parser_excess_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_parser_excess_test (void) { kms_response_parser_t *parser; uint8_t *data; size_t outlen; bool ok; data = hex_to_data (SAMPLE_KMIP, &outlen); parser = kms_kmip_response_parser_new (NULL); feed_full_response (parser, data); ASSERT_PARSER_OK (parser); ok = kms_response_parser_feed (parser, data, 1); ASSERT_PARSER_ERROR (parser, "KMIP parser was fed too much data"); ASSERT (!ok); kms_response_parser_destroy (parser); free (data); } void kms_kmip_response_parser_notenough_test (void); // -Wmissing-prototypes: for testing only. void kms_kmip_response_parser_notenough_test (void) { kms_response_parser_t *parser; uint8_t *data; size_t outlen; kms_response_t *res; bool ok; data = hex_to_data (SAMPLE_KMIP, &outlen); parser = kms_kmip_response_parser_new (NULL); ok = kms_response_parser_feed ( parser, data, KMS_KMIP_RESPONSE_PARSER_FIRST_LENGTH); ASSERT_PARSER_OK (parser); ASSERT (ok); res = kms_response_parser_get_response (parser); ASSERT_PARSER_ERROR (parser, "KMIP parser does not have a complete message"); ASSERT (!res); kms_response_parser_destroy (parser); free (data); } void kms_response_parser_response_too_big_test (void); // -Wmissing-prototypes: for testing only. void kms_response_parser_response_too_big_test (void) { uint8_t *data = (uint8_t*) malloc(KMS_PARSER_MAX_RESPONSE_LEN); // 16 MiB memset(data, 0, KMS_PARSER_MAX_RESPONSE_LEN); // Test HTTP parser: { kms_response_parser_t *parser = kms_response_parser_new (); // Feed max length: { bool ok = kms_response_parser_feed (parser, data, KMS_PARSER_MAX_RESPONSE_LEN); ASSERT (ok); } // Feed one more byte: { bool ok = kms_response_parser_feed (parser, data, 1); ASSERT_PARSER_ERROR (parser, "KMS response too large"); ASSERT (!ok); } kms_response_parser_destroy (parser); } // Test KMIP parser: { kms_response_parser_t *parser = kms_kmip_response_parser_new (NULL); // Feed max length: { bool ok = kms_response_parser_feed (parser, data, KMS_PARSER_MAX_RESPONSE_LEN); ASSERT (ok); } // Feed one more byte: { bool ok = kms_response_parser_feed (parser, data, 1); ASSERT_PARSER_ERROR (parser, "KMS response too large"); ASSERT (!ok); } kms_response_parser_destroy (parser); } free (data); } libmongocrypt-1.19.0/kms-message/test/test_kms_online_util.c000066400000000000000000000066101521103432300242600ustar00rootroot00000000000000/* * Copyright 2020-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 "test_kms_online_util.h" #include "test_kms_assert.h" #include "kms_message/kms_response_parser.h" mongoc_stream_t * connect_with_tls (const char *host, const char *port, mongoc_ssl_opt_t *ssl_opt) { mongoc_stream_t *stream; mongoc_socket_t *sock = NULL; struct addrinfo hints; struct addrinfo *result, *rp; int64_t expire_at; int s; const int connecttimeoutms = 5000; memset (&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; if (!port) { port = "443"; } s = getaddrinfo (host, port, &hints, &result); ASSERT_CMPINT (s, ==, 0); for (rp = result; rp; rp = rp->ai_next) { if (!(sock = mongoc_socket_new ( rp->ai_family, rp->ai_socktype, rp->ai_protocol))) { continue; } expire_at = bson_get_monotonic_time () + (connecttimeoutms * 1000L); if (0 != mongoc_socket_connect ( sock, rp->ai_addr, (mongoc_socklen_t) rp->ai_addrlen, expire_at)) { mongoc_socket_destroy (sock); sock = NULL; continue; } break; } if (!sock) { TEST_ERROR ("Failed to connect: %s", host); } freeaddrinfo (result); stream = mongoc_stream_socket_new (sock); ASSERT (stream); if (ssl_opt == NULL) { ssl_opt = (mongoc_ssl_opt_t *) mongoc_ssl_opt_get_default (); } return mongoc_stream_tls_new_with_hostname ( stream, host, ssl_opt, 1); } /* Helper to send an HTTP request and receive a response. */ kms_response_t * send_kms_request (kms_request_t *req, const char *host) { mongoc_stream_t *tls_stream; char *req_str; int32_t socket_timeout_ms = 5000; ssize_t write_ret; kms_response_parser_t *response_parser; int bytes_to_read; int bytes_read; uint8_t buf[1024]; kms_response_t *response; tls_stream = connect_with_tls (host, NULL, NULL); req_str = kms_request_to_string (req); write_ret = mongoc_stream_write ( tls_stream, req_str, strlen (req_str), socket_timeout_ms); ASSERT_CMPINT ((int) write_ret, ==, (int) strlen (req_str)); response_parser = kms_response_parser_new (); while ((bytes_to_read = kms_response_parser_wants_bytes (response_parser, 1024)) > 0) { bytes_read = (int) mongoc_stream_read ( tls_stream, buf, bytes_to_read, 0, socket_timeout_ms); if (!kms_response_parser_feed (response_parser, buf, bytes_read)) { TEST_ERROR ("read failed: %s", kms_response_parser_error (response_parser)); } } response = kms_response_parser_get_response (response_parser); ASSERT (response); kms_request_free_string (req_str); kms_response_parser_destroy (response_parser); mongoc_stream_destroy (tls_stream); return response; } libmongocrypt-1.19.0/kms-message/test/test_kms_online_util.h000066400000000000000000000022241521103432300242620ustar00rootroot00000000000000/* * Copyright 2020-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. */ #ifndef TEST_KMS_ONLINE_UTIL_H #define TEST_KMS_ONLINE_UTIL_H #include #include "kms_message/kms_request.h" #include "kms_message/kms_response.h" /* connect_with_tls creates a TLS stream. * port may be NULL. It defaults to "443". * ssl_opt may be NULL. It defaults to mongoc_ssl_opt_default (). */ mongoc_stream_t * connect_with_tls (const char *host, const char *port, mongoc_ssl_opt_t *ssl_opt); kms_response_t * send_kms_request (kms_request_t *req, const char *host); #endif /* TEST_KMS_ONLINE_UTIL_H */ libmongocrypt-1.19.0/kms-message/test/test_kms_request.c000066400000000000000000001211711521103432300234270ustar00rootroot00000000000000/* * Copyright 2018-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. */ /* Needed for strptime */ #if !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include "kms_message/kms_message.h" #include "kms_message_private.h" #ifndef _WIN32 #include #else #include "windows/dirent.h" #endif #include #include #include #include #include #include #include #include "kms_message/kms_azure_request.h" #include "kms_message/kms_b64.h" #include "hexlify.h" #include "kms_request_str.h" #include "kms_kv_list.h" #include "kms_port.h" #include "test_kms_assert.h" static const char *aws_test_suite_dir = "aws-sig-v4-test-suite"; static const char *skipped_aws_tests[] = { /* we don't yet support temporary security credentials provided by the AWS * Security Token Service (AWS STS). see post-sts-token/readme.txt */ "post-sts-token", }; static bool skip_aws_test (const char *test_name) { size_t i; for (i = 0; i < sizeof (skipped_aws_tests) / sizeof (char *); i++) { if (0 == strcmp (test_name, skipped_aws_tests[i])) { return true; } } return false; } static bool ends_with (const char *str, const char *suffix) { size_t str_len = strlen (str); size_t suf_len = strlen (suffix); if (str_len >= suf_len && 0 == strncmp (&str[str_len - suf_len], suffix, suf_len)) { return true; } return false; } static char * last_segment (const char *str) { const char *p = str + strlen (str); while (--p > str) { if (*p == '/') { return strdup (p + 1); } } return strdup (str); } static char * test_file_path (const char *path, const char *suffix) { char *r; char *test_name = last_segment (path); char file_path[PATH_MAX]; int ret = snprintf (file_path, PATH_MAX, "%s/%s.%s", path, test_name, suffix); KMS_ASSERT (ret > 0 && ret < PATH_MAX); r = strdup (file_path); free (test_name); return r; } static void realloc_buffer (char **buffer, size_t *n, size_t len) { if (*buffer == NULL) { *buffer = malloc (len); KMS_ASSERT (*buffer); } else { *buffer = realloc (*buffer, len); KMS_ASSERT(*buffer); } *n = len; } static ssize_t test_getline (char **lineptr, size_t *n, FILE *stream) { if (*lineptr == NULL && *n == 0) { realloc_buffer (lineptr, n, 128); } // Sanity check if ((*lineptr == NULL && *n != 0) || (*lineptr != NULL && *n == 0)) { abort (); } ssize_t count = 0; while (true) { // Read a character int c = fgetc (stream); // If the buffer is full, grow the buffer if ((*n - count) <= 1) { realloc_buffer (lineptr, n, *n + 128); } if (c == EOF) { *(*lineptr + count) = '\0'; if (count > 0) { return count; } return -1; } *(*lineptr + count) = c; ++count; // If we hit the end of the line, we are done if (c == '\n') { *(*lineptr + count) = '\0'; return count; } } } static char * read_test (const char *path, const char *suffix) { char *file_path = test_file_path (path, suffix); FILE *f; struct stat file_stat; size_t f_size; char *str; if (0 != stat (file_path, &file_stat)) { perror (file_path); abort (); } f = fopen (file_path, "r"); if (!f) { perror (file_path); abort (); } f_size = (size_t) file_stat.st_size; str = malloc (f_size + 1); KMS_ASSERT (str); memset (str, 0, f_size + 1); // Windows will convert crlf to lf // We want this behavior in this function call but // it prevents us from validating we read the whole file here. #ifndef _WIN32 if (f_size != fread (str, 1, f_size, f)) { perror (file_path); abort (); } #else fread (str, 1, f_size, f); #endif fclose (f); str[f_size] = '\0'; free (file_path); return str; } static void set_test_date (kms_request_t *request) { struct tm tm; /* all tests use the same date and time: 20150830T123600Z */ tm.tm_year = 115; tm.tm_mon = 7; tm.tm_mday = 30; tm.tm_yday = 241; tm.tm_wday = 0; tm.tm_hour = 12; tm.tm_min = 36; tm.tm_sec = 0; KMS_ASSERT (kms_request_set_date (request, &tm)); } static kms_request_t * read_req (const char *path) { kms_request_t *request; char *file_path = test_file_path (path, "req"); FILE *f; size_t len = 0; ssize_t line_len; char *line = NULL; char *method; char *uri_path; char *field_name; char *field_value; bool r; f = fopen (file_path, "r"); if (!f) { perror (file_path); abort (); } /* like "GET /path HTTP/1.1" */ line_len = test_getline (&line, &len, f); method = kms_strndup (line, strchr (line, ' ') - line); uri_path = kms_strndup (line + strlen (method) + 1, line_len - strlen (method) - 1 - strlen (" HTTP/1.1\n")); request = kms_request_new (method, uri_path, NULL); request->auto_content_length = false; /* from docs.aws.amazon.com/general/latest/gr/signature-v4-test-suite.html */ kms_request_set_region (request, "us-east-1"); kms_request_set_service (request, "service"); kms_request_set_access_key_id (request, "AKIDEXAMPLE"); kms_request_set_secret_key (request, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); while ((line_len = test_getline (&line, &len, f)) != -1) { if (strchr (line, ':')) { /* new header field like Host:example.com */ field_name = strtok (line, ": "); KMS_ASSERT (field_name); field_value = strtok (NULL, "\n"); KMS_ASSERT (field_value); r = kms_request_add_header_field (request, field_name, field_value); KMS_ASSERT (r); } else if (0 == strcmp (line, "\n")) { /* end of header */ break; } else if (line_len > 2) { /* continuing a multiline header value from previous line */ /* Header line folding is deprecated by RFC 7230 section 3.2. */ /* get-header-value-multiline tests this behavior. */ kms_request_append_header_field_value (request, "\r\n", 2); /* omit this line's newline */ kms_request_append_header_field_value ( request, line, (size_t) (line_len - 1)); } } while ((line_len = test_getline (&line, &len, f)) != -1) { KMS_ASSERT (kms_request_append_payload (request, line, (size_t) line_len)); } fclose (f); free (file_path); free (line); free (uri_path); free (method); set_test_date (request); return request; } static void test_compare (kms_request_t *request, char *(*func) (kms_request_t *), const char *dir_path, const char *suffix) { char *test_name = last_segment (dir_path); char *expect; char *actual; expect = read_test (dir_path, suffix); if (0 == strcmp (suffix, "sreq")) { /* The final signed request is an HTTP request. * The expected output must contain \r\n, not \n. */ char* tmp = replace_all (expect, "\n", "\r\n"); free (expect); expect = tmp; } actual = func (request); ASSERT_CMPSTR (expect, actual); free (actual); free (expect); free (test_name); } static void test_compare_creq (kms_request_t *request, const char *dir_path) { test_compare (request, kms_request_get_canonical, dir_path, "creq"); } static void test_compare_sts (kms_request_t *request, const char *dir_path) { test_compare (request, kms_request_get_string_to_sign, dir_path, "sts"); } static void test_compare_authz (kms_request_t *request, const char *dir_path) { test_compare (request, kms_request_get_signature, dir_path, "authz"); } static void test_compare_sreq (kms_request_t *request, const char *dir_path) { test_compare (request, kms_request_get_signed, dir_path, "sreq"); } static void aws_sig_v4_test (const char *dir_path) { kms_request_t *request; request = read_req (dir_path); test_compare_creq (request, dir_path); test_compare_sts (request, dir_path); test_compare_authz (request, dir_path); test_compare_sreq (request, dir_path); kms_request_destroy (request); } static bool all_aws_sig_v4_tests (const char *path, const char *selected) { /* Amazon supplies tests, one per directory, 5 files per test, see * docs.aws.amazon.com/general/latest/gr/signature-v4-test-suite.html */ DIR *dp; struct dirent *ent; bool ran_tests = false; char *test_name = last_segment (path); char sub[PATH_MAX]; dp = opendir (path); if (!dp) { perror (path); abort (); } if (skip_aws_test (test_name) && !selected) { TEST_PRINTF ("SKIP: %s\n", test_name); goto done; } while ((ent = readdir (dp))) { if (ent->d_name[0] == '.') { continue; } if (ent->d_type & DT_DIR) { snprintf (sub, PATH_MAX, "%s/%s", path, ent->d_name); ran_tests |= all_aws_sig_v4_tests (sub, selected); } if (!(ent->d_type & DT_REG) || !ends_with (ent->d_name, ".req")) { continue; } /* "ent" is a "test.req" request file, this is a test directory */ /* skip the test if it doesn't match the name passed to us */ if (selected && 0 != strcmp (test_name, selected)) { continue; } TEST_PRINTF ("%s\n", path); aws_sig_v4_test (path); ran_tests = true; } done: (void) closedir (dp); free (test_name); return ran_tests; } /* docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html */ static void example_signature_test (void) { const char *expect = "c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9"; kms_request_t *request; unsigned char signing[32]; char *sig; request = kms_request_new ("GET", "uri", NULL); set_test_date (request); kms_request_set_region (request, "us-east-1"); kms_request_set_service (request, "iam"); kms_request_set_secret_key (request, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); KMS_ASSERT (kms_request_get_signing_key (request, signing)); sig = hexlify (signing, 32); ASSERT_CMPSTR (expect, sig); free (sig); kms_request_destroy (request); } static void path_normalization_test (void) { const char *tests[][2] = { {"", "/"}, {"/", "/"}, {"/..", "/"}, {"./..", "/"}, {"../..", "/"}, {"/../..", "/"}, {"a", "a"}, {"a/", "a/"}, {"a//", "a/"}, {"a///", "a/"}, {"/a", "/a"}, {"//a", "/a"}, {"///a", "/a"}, {"/a/", "/a/"}, {"/a/..", "/"}, {"/a/../..", "/"}, {"/a/b/../..", "/"}, {"/a/b/c/../..", "/a"}, {"/a/b/../../d", "/d"}, {"/a/b/c/../../d", "/a/d"}, {"/a/b", "/a/b"}, {"a/..", "/"}, {"a/../..", "/"}, {"a/b/../..", "/"}, {"a/b/c/../..", "a"}, {"a/b/../../d", "d"}, {"a/b/c/../../d", "a/d"}, {"a/b", "a/b"}, {"/a//b", "/a/b"}, {"/a///b", "/a/b"}, {"/a////b", "/a/b"}, {"//", "/"}, {"//a///", "/a/"}, }; const char **test; const char *out; size_t i; kms_request_str_t *in, *norm; for (i = 0; i < sizeof (tests) / (2 * sizeof (const char *)); i++) { test = tests[i]; in = kms_request_str_new_from_chars (test[0], -1); out = test[1]; norm = kms_request_str_path_normalized (in); ASSERT_CMPSTR (out, norm->str); kms_request_str_destroy (in); kms_request_str_destroy (norm); } } static kms_request_t * make_test_request (void) { kms_request_t *request = kms_request_new ("POST", "/", NULL); kms_request_set_region (request, "foo-region"); kms_request_set_service (request, "foo-service"); kms_request_set_access_key_id (request, "foo-akid"); kms_request_set_secret_key (request, "foo-key"); set_test_date (request); return request; } static void host_test (void) { kms_request_t *request = make_test_request (); test_compare_sreq (request, "test/host"); kms_request_destroy (request); } static void content_length_test (void) { const char *payload = "foo-payload"; kms_request_t *request = make_test_request (); KMS_ASSERT (kms_request_append_payload (request, payload, strlen (payload))); test_compare_sreq (request, "test/content_length"); kms_request_destroy (request); } static void bad_query_test (void) { kms_request_t *request = kms_request_new ("GET", "/?asdf", NULL); ASSERT_CONTAINS (kms_request_get_error (request), "Cannot parse"); kms_request_destroy (request); } static void append_header_field_value_test (void) { kms_request_t *request = kms_request_new ("GET", "/", NULL); KMS_ASSERT (kms_request_add_header_field (request, "a", "b")); KMS_ASSERT (kms_request_append_header_field_value (request, "asdf", 4)); /* header field 0 is "X-Amz-Date", field 1 is "a" */ ASSERT_CMPSTR (request->header_fields->kvs[1].value->str, "basdf"); kms_request_destroy (request); } static void set_date_test (void) { // Windows CRT asserts on this negative test because it is a negative test // so it is skipped on Windows. #ifndef _WIN32 struct tm tm = {0}; kms_request_t *request = kms_request_new ("GET", "/", NULL); tm.tm_sec = 9999; /* invalid, shouldn't be > 60 */ KMS_ASSERT (!kms_request_set_date (request, &tm)); ASSERT_CONTAINS (kms_request_get_error (request), "Invalid tm struct"); kms_request_destroy (request); #endif } static void multibyte_test (void) { /* euro currency symbol */ #define EU "\xe2\x82\xac" kms_request_t *request = kms_request_new ("GET", "/" EU "/?euro=" EU, NULL); set_test_date (request); KMS_ASSERT (kms_request_set_region (request, EU)); KMS_ASSERT (kms_request_set_service (request, EU)); kms_request_set_access_key_id (request, "AKIDEXAMPLE"); kms_request_set_secret_key (request, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); KMS_ASSERT (kms_request_add_header_field (request, EU, EU)); KMS_ASSERT (kms_request_append_header_field_value (request, "asdf" EU, 7)); KMS_ASSERT (kms_request_append_payload (request, EU, sizeof (EU))); /* header field 0 is "X-Amz-Date" */ ASSERT_CMPSTR (request->header_fields->kvs[1].value->str, EU "asdf" EU); test_compare_creq (request, "test/multibyte"); test_compare_sreq (request, "test/multibyte"); kms_request_destroy (request); #undef EU } static void connection_close_test (void) { kms_request_opt_t *opt; kms_request_t *request; opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); request = kms_request_new ("POST", "/", opt); kms_request_set_region (request, "foo-region"); kms_request_set_service (request, "foo-service"); kms_request_set_access_key_id (request, "foo-akid"); kms_request_set_secret_key (request, "foo-key"); set_test_date (request); test_compare_sreq (request, "test/connection_close"); kms_request_opt_destroy (opt); kms_request_destroy (request); } /* the ciphertext blob from a response to an "Encrypt" API call */ static const char ciphertext_blob[] = "\x01\x02\x02\x00\x78\xf3\x8e\xd8\xd4\xc6\xba\xfb\xa1\xcf\xc1\x1e\x68\xf2" "\xa1\x91\x9e\x36\x4d\x74\xa2\xc4\x9e\x30\x67\x08\x53\x33\x0d\xcd\xe0\xc9" "\x1b\x01\x60\x30\xd4\x73\x9e\x90\x1f\xa7\x43\x55\x84\x26\xf9\xd5\xf0\xb1" "\x00\x00\x00\x64\x30\x62\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x07\x06\xa0" "\x55\x30\x53\x02\x01\x00\x30\x4e\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x07" "\x01\x30\x1e\x06\x09\x60\x86\x48\x01\x65\x03\x04\x01\x2e\x30\x11\x04\x0c" "\xa2\xc7\x12\x1c\x25\x38\x0e\xec\x08\x1f\x23\x09\x02\x01\x10\x80\x21\x61" "\x03\xcd\xcb\xe2\xac\x36\x4f\x73\xdb\x1b\x73\x2e\x33\xda\x45\x51\xf4\xcd" "\xc0\xff\xd2\xe1\xb9\xc4\xc2\x0e\xbf\x53\x90\x46\x18\x42"; static void decrypt_request_test (void) { kms_request_t *request = kms_decrypt_request_new ( (uint8_t *) ciphertext_blob, sizeof (ciphertext_blob) - 1, NULL); set_test_date (request); kms_request_set_region (request, "us-east-1"); kms_request_set_service (request, "service"); kms_request_set_access_key_id (request, "AKIDEXAMPLE"); kms_request_set_secret_key (request, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); test_compare_creq (request, "test/decrypt"); test_compare_sreq (request, "test/decrypt"); kms_request_destroy (request); } static void encrypt_request_test (void) { char *plaintext = "foobar"; kms_request_t *request = kms_encrypt_request_new ( (uint8_t *) plaintext, strlen (plaintext), "alias/1", NULL); set_test_date (request); kms_request_set_region (request, "us-east-1"); kms_request_set_service (request, "service"); kms_request_set_access_key_id (request, "AKIDEXAMPLE"); kms_request_set_secret_key (request, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"); test_compare_creq (request, "test/encrypt"); test_compare_sreq (request, "test/encrypt"); kms_request_destroy (request); } static void kv_list_del_test (void) { kms_kv_list_t *lst = kms_kv_list_new (); kms_request_str_t *k = kms_request_str_new_from_chars ("one", -1); kms_request_str_t *v = kms_request_str_new_from_chars ("v", -1); kms_kv_list_add (lst, k, v); kms_request_str_set_chars (k, "two", -1); kms_kv_list_add (lst, k, v); kms_request_str_set_chars (k, "three", -1); kms_kv_list_add (lst, k, v); kms_request_str_set_chars (k, "two", -1); /* dupe */ kms_kv_list_add (lst, k, v); kms_request_str_set_chars (k, "four", -1); kms_kv_list_add (lst, k, v); KMS_ASSERT (lst->len == 5); kms_kv_list_del (lst, "two"); /* delete both "two" keys */ KMS_ASSERT (lst->len == 3); ASSERT_CMPSTR (lst->kvs[0].key->str, "one"); ASSERT_CMPSTR (lst->kvs[1].key->str, "three"); ASSERT_CMPSTR (lst->kvs[2].key->str, "four"); kms_request_str_destroy (k); kms_request_str_destroy (v); kms_kv_list_destroy (lst); } static void b64_test (void) { uint8_t *expected = (uint8_t *) "\x01\x02\x03\x04"; char encoded[9]; int r; uint8_t data[5]; r = kms_message_b64_ntop (expected, 4, encoded, 9); KMS_ASSERT (r == 8); ASSERT_CMPSTR (encoded, "AQIDBA=="); r = kms_message_b64_pton (encoded, data, 5); /* +1 for terminator */ KMS_ASSERT (r == 4); KMS_ASSERT (0 == memcmp (expected, data, 4)); } static void b64_b64url_test (void) { char base64_data[64]; char base64url_data[64]; int ret; memset (base64_data, 0, sizeof (base64_data)); memset (base64url_data, 0, sizeof (base64url_data)); strcpy (base64_data, "PDw/Pz8+Pg=="); ret = kms_message_b64_to_b64url (base64_data, strlen (base64_data), base64url_data, sizeof (base64url_data)); ASSERT (ret == 12); ASSERT_CMPSTR (base64url_data, "PDw_Pz8-Pg=="); memset (base64_data, 0, sizeof (base64_data)); ret = kms_message_b64url_to_b64 (base64url_data, strlen (base64url_data), base64_data, sizeof (base64_data)); ASSERT (ret == 12); ASSERT_CMPSTR (base64_data, "PDw/Pz8+Pg=="); /* Convert to base64url in-place. */ ret = kms_message_b64_to_b64url ( base64_data, strlen (base64_data), base64_data, sizeof (base64_data)); ASSERT (ret == 12); ASSERT_CMPSTR (base64_data, "PDw_Pz8-Pg=="); } static void kms_response_parser_test (void) { kms_response_parser_t *parser = kms_response_parser_new (); kms_response_t *response; /* the parser resets after returning a response. */ ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "Content-Length: 15\r\n", 20)); ASSERT (kms_response_parser_feed (parser, (uint8_t *) "\r\n", 2)); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "This is a test.", 15)); ASSERT (0 == kms_response_parser_wants_bytes (parser, 123)); response = kms_response_parser_get_response (parser); ASSERT (response->status == 200); ASSERT_CMPSTR (response->body->str, "This is a test."); kms_response_destroy (response); kms_response_parser_destroy (parser); /* We fail to parse invalid HTTP */ parser = kms_response_parser_new (); ASSERT (!kms_response_parser_feed ( parser, (uint8_t *) "To Whom it May Concern\r\n", 24)); kms_response_parser_destroy (parser); /* We fail on HTTP other than 1.1 */ parser = kms_response_parser_new (); ASSERT (!kms_response_parser_feed ( parser, (uint8_t *) "HTTP/6.1 200 OK\r\n", 17)); kms_response_parser_destroy (parser); /* We fail if there is no status */ parser = kms_response_parser_new (); ASSERT (!kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 CREATED\r\n", 18)); kms_response_parser_destroy (parser); /* We do not fail when parsing a non-200 status code, * as the content may provide a useful error message. */ parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 100 CONTINUE\r\n", 23)); ASSERT (kms_response_parser_status (parser) == 100); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 201 CREATED\r\n", 22)); ASSERT (kms_response_parser_status (parser) == 201); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 301 MOVED PERMANENTLY\r\n", 32)); ASSERT (kms_response_parser_status (parser) == 301); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 400 BAD REQUEST\r\n", 26)); ASSERT (kms_response_parser_status (parser) == 400); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 404 NOT FOUND\r\n", 24)); ASSERT (kms_response_parser_status (parser) == 404); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "HTTP/1.1 500 INTERNAL SERVER ERROR\r\n", 36)); ASSERT (kms_response_parser_status (parser) == 500); kms_response_parser_destroy (parser); /* We fail if the header doesn't have a colon in it */ parser = kms_response_parser_new (); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_status (parser) == 200); ASSERT (!kms_response_parser_feed ( parser, (uint8_t *) "Content-Length= 15\r\n", 20)); ASSERT (strstr (kms_response_parser_error (parser), "Could not parse header, no colon found.")); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_status (parser) == 200); ASSERT ( !kms_response_parser_feed (parser, (uint8_t *) "Anything else\r\n", 15)); ASSERT (strstr (kms_response_parser_error (parser), "Could not parse header, no colon found.")); kms_response_parser_destroy (parser); /* An empty body is ok. */ parser = kms_response_parser_new (); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_status (parser) == 200); ASSERT (kms_response_parser_feed (parser, (uint8_t *) "\r\n", 2)); kms_response_parser_destroy (parser); /* Extra content is not ok. */ parser = kms_response_parser_new (); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_status (parser) == 200); ASSERT (kms_response_parser_feed (parser, (uint8_t *) "\r\n", 2)); ASSERT (!kms_response_parser_feed (parser, (uint8_t *) "\r\n", 2)); ASSERT (strstr (kms_response_parser_error (parser), "Unexpected extra HTTP content")); kms_response_parser_destroy (parser); parser = kms_response_parser_new (); ASSERT ( kms_response_parser_feed (parser, (uint8_t *) "HTTP/1.1 200 OK\r\n", 17)); ASSERT (kms_response_parser_status (parser) == 200); ASSERT (kms_response_parser_feed ( parser, (uint8_t *) "Content-Length: 5\r\n", 19)); ASSERT (kms_response_parser_feed (parser, (uint8_t *) "\r\n", 2)); ASSERT (!kms_response_parser_feed (parser, (uint8_t *) "abcdefghi", 9)); ASSERT (strstr (kms_response_parser_error (parser), "Unexpected: exceeded content length")); kms_response_parser_destroy (parser); } typedef struct { const char *filepath; const char *expected_body; int max_to_read; int expected_status; } parser_testcase_t; /* File should have \r\n line endings (use /etc/rewrite.py) */ static void parser_testcase_run (parser_testcase_t *testcase) { FILE *response_file; kms_response_parser_t *parser; kms_response_t *response; uint8_t buf[512] = {0}; int bytes_to_read; response_file = fopen (testcase->filepath, "rb"); ASSERT (response_file); parser = kms_response_parser_new (); while ((bytes_to_read = kms_response_parser_wants_bytes ( parser, testcase->max_to_read)) > 0) { if (bytes_to_read > testcase->max_to_read) { bytes_to_read = testcase->max_to_read; } size_t ret = fread (buf, 1, (size_t) bytes_to_read, response_file); if (!kms_response_parser_feed (parser, buf, (int) ret)) { TEST_PRINTF ("feed error: %s\n", parser->error); ASSERT (false); } } fclose (response_file); ASSERT (0 == kms_response_parser_wants_bytes (parser, 123)); response = kms_response_parser_get_response (parser); ASSERT_CMPSTR (testcase->expected_body, response->body->str); ASSERT (response->status == testcase->expected_status); kms_response_parser_destroy (parser); kms_response_destroy (response); } static void kms_response_parser_files (void) { const char *body = "{\"CiphertextBlob\":\"AQICAHifzrL6n/" "3uqZyz+z1bJj80DhqPcSAibAaIoYc+HOVP6QEplwbM0wpvU5zsQG/" "1SBKvAAAAZDBiBgkqhkiG9w0BBwagVTBTAgEAME4GCSqGSIb3DQEHATA" "eBglghkgBZQMEAS4wEQQM5syMJE7RodxDaqYqAgEQgCHMFCnFso4Lih0" "CNbLT1kiET0hQyzjgoa9733353GQkGlM=\",\"KeyId\":\"arn:aws:" "kms:us-east-1:524754917239:key/" "bd05530b-0a7f-4fbd-8362-ab3667370db0\"}"; const char *chunked_body = "{\"access_token\":" "\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"," "\"expires_in\":3599,\"token_type\":\"Bearer\"}"; parser_testcase_t tests[] = { {"./test/example-response.bin", body, 512, 200}, {"./test/example-response.bin", body, 1, 200}, {"./test/example-chunked-response.bin", chunked_body, 512, 200}, {"./test/example-chunked-response.bin", chunked_body, 1, 200}, {"./test/example-multi-chunked-response.bin", chunked_body, 512, 200}, {"./test/example-multi-chunked-response.bin", chunked_body, 1, 200}}; size_t i; for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) { TEST_PRINTF (" parser testcase: %d\n", (int) i); parser_testcase_run (tests + i); } } #define CLEAR(_field) \ do { \ kms_request_str_destroy (_field); \ _field = kms_request_str_new (); \ } while (0) static void kms_request_validate_test (void) { kms_request_t *request = NULL; request = make_test_request (); CLEAR (request->region); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Region not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->service); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Service not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->access_key_id); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Access key ID not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->method); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Method not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->path); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Path not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->date); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Date not set", kms_request_get_error (request)); kms_request_destroy (request); request = make_test_request (); CLEAR (request->secret_key); ASSERT (NULL == kms_request_get_signed (request)); ASSERT_CMPSTR ("Secret key not set", kms_request_get_error (request)); kms_request_destroy (request); } /* Test private key signing. * * private_key_b64 was generated by taking the base64 data out of * test-private-key.pem generated as follows: openssl genpkey -out * test-private-key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:2048 * * It was used to generate a test signature of the string "data to sign" with: * openssl dgst -sign test-private-key.pem -sha256 signme.txt | base64 */ static void kms_signature_test (void) { const char *private_key_b64 = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4JOyv5z05cL18ztpknRC7" "CFY2gYol4DAKerdVUoDJxCTmFMf39dVUEqD0WDiw/qcRtSO1/" "FRut08PlSPmvbyKetsLoxlpS8lukSzEFpFK7+L+R4miFOl6HvECyg7lbC1H/" "WGAhIz9yZRlXhRo9qmO/" "fB6PV9IeYtU+" "1xYuXicjCDPp36uuxBAnCz7JfvxJ3mdVc0vpSkbSb141nWuKNYR1mgyvvL6KzxO6mYsCo4hR" "AdhuizD9C4jDHk0V2gDCFBk0h8SLEdzStX8L0jG90/Og4y7J1b/cPo/" "kbYokkYisxe8cPlsvGBf+rZex7XPxc1yWaP080qeABJb+S88O//" "LAgMBAAECggEBAKVxP1m3FzHBUe2NZ3fYCc0Qa2zjK7xl1KPFp2u4CU+" "9sy0oZJUqQHUdm5CMprqWwIHPTftWboFenmCwrSXFOFzujljBO7Z3yc1WD3NJl1ZNepLcsRJ" "3WWFH5V+NLJ8Bdxlj1DMEZCwr7PC5+vpnCuYWzvT0qOPTl9RNVaW9VVjHouJ9Fg+" "s2DrShXDegFabl1iZEDdI4xScHoYBob06A5lw0WOCTayzw0Naf37lM8Y4psRAmI46XLiF/" "Vbuorna4hcChxDePlNLEfMipICcuxTcei1RBSlBa2t1tcnvoTy6cuYDqqImRYjp1KnMKlKQB" "nQ1NjS2TsRGm+F0FbreVCECgYEA4IDJlm8q/hVyNcPe4OzIcL1rsdYN3bNm2Y2O/" "YtRPIkQ446ItyxD06d9VuXsQpFp9jNACAPfCMSyHpPApqlxdc8z/" "xATlgHkcGezEOd1r4E7NdTpGg8y6Rj9b8kVlED6v4grbRhKcU6moyKUQT3+" "1B6ENZTOKyxuyDEgTwZHtFECgYEA0fqdv9h9s77d6eWmIioP7FSymq93pC4umxf6TVicpjpM" "ErdD2ZfJGulN37dq8FOsOFnSmFYJdICj/PbJm6p1i8O21lsFCltEqVoVabJ7/" "0alPfdG2U76OeBqI8ZubL4BMnWXAB/" "VVEYbyWCNpQSDTjHQYs54qa2I0dJB7OgJt1sCgYEArctFQ02/" "7H5Rscl1yo3DBXO94SeiCFSPdC8f2Kt3MfOxvVdkAtkjkMACSbkoUsgbTVqTYSEOEc2jTgR3" "iQ13JgpHaFbbsq64V0QP3TAxbLIQUjYGVgQaF1UfLOBv8hrzgj45z/ST/" "G80lOl595+0nCUbmBcgG1AEWrmdF0/" "3RmECgYAKvIzKXXB3+19vcT2ga5Qq2l3TiPtOGsppRb2XrNs9qKdxIYvHmXo/" "9QP1V3SRW0XoD7ez8FpFabp42cmPOxUNk3FK3paQZABLxH5pzCWI9PzIAVfPDrm+" "sdnbgG7vAnwfL2IMMJSA3aDYGCbF9EgefG+" "STcpfqq7fQ6f5TBgLFwKBgCd7gn1xYL696SaKVSm7VngpXlczHVEpz3kStWR5gfzriPBxXgM" "VcWmcbajRser7ARpCEfbxM1UJyv6oAYZWVSNErNzNVb4POqLYcCNySuC6xKhs9FrEQnyKjyk" "8wI4VnrEMGrQ8e+qYSwYk9Gh6dKGoRMAPYVXQAO0fIsHF/T0a"; const char *data_to_sign = "data to sign"; const char *expected_signature = "VocBRhpMmQ2XCzVehWSqheQLnU889gf3dhU4AnVnQTJjsKx/CM23qKDPkZDd2A/" "BnQsp99SN7ksIX5Raj0TPwyN5OCN/YrNFNGoOFlTsGhgP/" "hyE8X3Duiq6sNO0SMvRYNPFFGlJFsp1Fw3Z94eYMg4/Wpw5s4+Jo5Zm/" "qY7aTJIqDKDQ3CNHLeJgcMUOc9sz01/" "GzoUYKDVODHSxrYEk5ireFJFz9vP8P7Ha+" "VDUZuQIQdXer9NBbGFtYmWprY3nn4D3Dw93Sn0V0dIqYeIo91oKyslvMebmUM95S2PyIJdEp" "Pb2DJDxjvX/0LLwSWlSXRWy9gapWoBkb4ynqZBsg=="; uint8_t *private_key_raw; size_t private_key_len; unsigned char *signature_raw; bool ret; char *signature_b64; private_key_raw = kms_message_b64_to_raw (private_key_b64, &private_key_len); signature_raw = malloc (256); ret = kms_sign_rsaes_pkcs1_v1_5 (NULL /* unused ctx */, (const char *) private_key_raw, private_key_len, data_to_sign, strlen (data_to_sign), signature_raw); KMS_ASSERT (ret); signature_b64 = kms_message_raw_to_b64 (signature_raw, 256); if (0 != strcmp (signature_b64, expected_signature)) { TEST_PRINTF ("generated signature: %s\n", signature_b64); TEST_PRINTF ("but expected signature: %s\n", expected_signature); abort (); } /* Test with an invalid key. */ ret = kms_sign_rsaes_pkcs1_v1_5 ( NULL, "blah", 4, data_to_sign, strlen (data_to_sign), signature_raw); free (private_key_raw); free (signature_raw); free (signature_b64); KMS_ASSERT (!ret); } static void kms_request_kmip_prohibited_test (void) { kms_request_opt_t *opt; kms_request_t *req; opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_KMIP); req = kms_request_new ("method", "path_and_query", opt); ASSERT_REQUEST_ERROR (req, "Function not applicable to KMIP"); kms_request_destroy (req); kms_request_opt_destroy (opt); } static int count_substrings (const char *big, const char *little) { const char *iter; int count = 0; iter = strstr (big, little); while (iter != NULL) { count += 1; iter += strlen (little); iter = strstr (iter, little); } return count; } /* Test that outgoing HTTP requests use \r\n line delimitters, not \n. * This is a regression test for MONGOCRYPT-457. */ static void test_request_newlines (void) { bool ok; kms_request_t *req; kms_request_opt_t *opt; uint8_t example_data[] = {0x01, 0x02, 0x03, 0x04}; // Test kms_request_to_string. { opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); ASSERT (kms_request_opt_set_provider (opt, KMS_REQUEST_PROVIDER_AZURE)); req = kms_azure_request_wrapkey_new ("example-host", "example-access-token", "example-key-name", "example-key-version", example_data, sizeof (example_data), opt); ASSERT_REQUEST_OK (req); char *req_str = kms_request_to_string (req); ASSERT_REQUEST_OK (req); ASSERT (req_str); /* Check that all \n have a \r. */ int n = count_substrings (req_str, "\n"); int rn = count_substrings (req_str, "\r\n"); ASSERT_CMPINT (n, ==, rn); ASSERT_CMPINT (rn, ==, 8); free (req_str); kms_request_opt_destroy (opt); kms_request_destroy (req); } // Test kms_request_get_signed. { opt = kms_request_opt_new (); ASSERT (opt); kms_request_opt_set_connection_close (opt, true); req = kms_caller_identity_request_new (opt); ASSERT_REQUEST_OK (req); ok = kms_request_set_region (req, "example-region"); ASSERT_REQUEST_OK (req); ASSERT (ok); ok = kms_request_set_service (req, "example-service"); ASSERT_REQUEST_OK (req); ASSERT (ok); ok = kms_request_set_access_key_id (req, "example-access-key-id"); ASSERT_REQUEST_OK (req); ASSERT (ok); ok = kms_request_set_secret_key (req, "example-secret-key"); ASSERT_REQUEST_OK (req); ASSERT (ok); char *req_str = kms_request_get_signed (req); ASSERT_REQUEST_OK (req); ASSERT (req_str); /* Check that all \n have a \r. */ int n = count_substrings (req_str, "\n"); int rn = count_substrings (req_str, "\r\n"); ASSERT_CMPINT (n, ==, rn); ASSERT_CMPINT (rn, ==, 8); free (req_str); kms_request_opt_destroy (opt); kms_request_destroy (req); } } #define RUN_TEST(_func) \ do { \ if (!selector || 0 == kms_strcasecmp (#_func, selector)) { \ TEST_PRINTF ("%s\n", #_func); \ _func (); \ ran_tests = true; \ } \ } while (0) extern void kms_kmip_writer_test (void); extern void kms_kmip_reader_test (void); extern void kms_kmip_reader_negative_int_test (void); extern void kms_kmip_reader_find_test (void); extern void kms_kmip_reader_find_and_recurse_test (void); extern void kms_kmip_reader_find_and_recurse_invalid_test (void); extern void kms_kmip_reader_find_and_read_enum_test (void); extern void kms_kmip_reader_find_and_read_bytes_test (void); extern void kms_kmip_request_register_secretdata_test (void); extern void kms_kmip_request_register_secretdata_invalid_test (void); extern void kms_kmip_request_get_test (void); extern void kms_kmip_request_activate_test (void); extern void kms_kmip_response_parser_test (void); extern void kms_kmip_response_get_unique_identifier_test (void); extern void kms_kmip_response_get_secretdata_test (void); extern void kms_kmip_response_get_secretdata_notfound_test (void); extern void kms_kmip_response_parser_reuse_test (void); extern void kms_kmip_response_parser_excess_test (void); extern void kms_kmip_response_parser_notenough_test (void); extern void kms_response_parser_response_too_big_test (void); int main (int argc, char *argv[]) { const char *help; char *selector = NULL; bool ran_tests = false; help = "Usage: test_kms_request [TEST_NAME]"; if (argc > 2) { TEST_STDERR_PRINTF ("%s\n", help); abort (); } else if (argc == 2) { selector = argv[1]; } int ret = kms_message_init (); if (ret != 0) { TEST_PRINTF ("kms_message_init failed: 0x%d\n", ret); abort (); } RUN_TEST (example_signature_test); RUN_TEST (path_normalization_test); RUN_TEST (host_test); RUN_TEST (content_length_test); RUN_TEST (bad_query_test); RUN_TEST (append_header_field_value_test); RUN_TEST (set_date_test); RUN_TEST (multibyte_test); RUN_TEST (connection_close_test); RUN_TEST (decrypt_request_test); RUN_TEST (encrypt_request_test); RUN_TEST (kv_list_del_test); RUN_TEST (b64_test); RUN_TEST (b64_b64url_test); ran_tests |= all_aws_sig_v4_tests (aws_test_suite_dir, selector); RUN_TEST (kms_response_parser_test); RUN_TEST (kms_response_parser_files); RUN_TEST (kms_request_validate_test); RUN_TEST (kms_signature_test); RUN_TEST (kms_kmip_writer_test); RUN_TEST (kms_kmip_reader_test); RUN_TEST (kms_kmip_reader_negative_int_test); RUN_TEST (kms_kmip_reader_test); RUN_TEST (kms_kmip_reader_find_and_recurse_test); RUN_TEST (kms_kmip_reader_find_and_recurse_invalid_test); RUN_TEST (kms_kmip_reader_find_and_read_enum_test); RUN_TEST (kms_kmip_reader_find_and_read_bytes_test); RUN_TEST (kms_kmip_request_register_secretdata_test); RUN_TEST (kms_kmip_request_register_secretdata_invalid_test); RUN_TEST (kms_kmip_request_get_test); RUN_TEST (kms_kmip_request_activate_test); RUN_TEST (kms_request_kmip_prohibited_test); RUN_TEST (kms_kmip_response_parser_test); RUN_TEST (kms_kmip_response_get_unique_identifier_test); RUN_TEST (kms_kmip_response_get_secretdata_test); RUN_TEST (kms_kmip_response_get_secretdata_notfound_test); RUN_TEST (kms_kmip_response_parser_reuse_test); RUN_TEST (kms_kmip_response_parser_excess_test); RUN_TEST (kms_kmip_response_parser_notenough_test); RUN_TEST (kms_response_parser_response_too_big_test); RUN_TEST (test_request_newlines); RUN_TEST (test_kms_util); if (!ran_tests) { KMS_ASSERT (argc == 2); TEST_STDERR_PRINTF ("No such test: \"%s\"\n", argv[1]); abort (); } kms_message_cleanup (); return 0; } libmongocrypt-1.19.0/kms-message/test/test_kms_util.c000066400000000000000000000046171521103432300227210ustar00rootroot00000000000000/* * 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 "test_kms_util.h" #include "test_kms_assert.h" #include "hexlify.h" #include /* tolower */ char * copy_and_filter_hex (const char *unfiltered_hex) { size_t i, j; char *filtered = malloc (strlen (unfiltered_hex) + 1); j = 0; for (i = 0; i < strlen (unfiltered_hex); i++) { if (unfiltered_hex[i] != ' ' && unfiltered_hex[i] != '|') { filtered[j] = (char) tolower (unfiltered_hex[i]); j++; } } filtered[j] = '\0'; return filtered; } uint8_t * hex_to_data (const char *unfiltered_hex, size_t *outlen) { char *filtered_hex; uint8_t *bytes; size_t i; filtered_hex = copy_and_filter_hex (unfiltered_hex); *outlen = strlen (filtered_hex) / 2; bytes = malloc (*outlen); for (i = 0; i < *outlen; i++) { bytes[i] = unhexlify (filtered_hex + (i * 2), 2); } free (filtered_hex); return bytes; } char * data_to_hex (const uint8_t *buf, size_t len) { return hexlify (buf, len); } char * replace_all (const char *input, const char *match, const char *replacement) { ASSERT (input); ASSERT (match); ASSERT (replacement); kms_request_str_t *replaced = kms_request_str_new (); const char *start = input; const char *iter = strstr (input, match); while (iter != NULL) { kms_request_str_append_chars (replaced, start, (ssize_t) (iter - start)); kms_request_str_append_chars ( replaced, replacement, (ssize_t) strlen (replacement)); iter += strlen (match); start = iter; iter = strstr (iter, match); } // Append the remainder of input. kms_request_str_append_chars (replaced, start, -1); return kms_request_str_detach (replaced); } void test_kms_util (void) { char *got = replace_all ("foo bar baz", "bar", "baz"); ASSERT_CMPSTR (got, "foo baz baz"); free (got); } libmongocrypt-1.19.0/kms-message/test/test_kms_util.h000066400000000000000000000025771521103432300227310ustar00rootroot00000000000000/* * Copyright 2020-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. */ #ifndef TEST_KMS_UTIL_H #define TEST_KMS_UTIL_H #include #include "kms_request_str.h" /* copy_and_filter_hex returns a copy of @unfiltered_hex with the following * characters removed: ' ', '|' */ char * copy_and_filter_hex (const char *unfiltered_hex); /* hex_to_data calls copy_and_filter_hex on @unfiltered_hex, then converts it to * binary and returns a byte array. */ uint8_t * hex_to_data (const char *unfiltered_hex, size_t *outlen); char * data_to_hex (const uint8_t *data, size_t len); /* replace_all returns a copy of @input with all occurrences of @match replaced * with @replacement. */ char * replace_all (const char *input, const char *match, const char *replacement); /* test_kms_util tests utility functions. */ void test_kms_util (void); #endif /* TEST_KMS_UTIL_H */ libmongocrypt-1.19.0/kms-message/test/windows/000077500000000000000000000000001521103432300213515ustar00rootroot00000000000000libmongocrypt-1.19.0/kms-message/test/windows/dirent.h000066400000000000000000000717701521103432300230230ustar00rootroot00000000000000/* * Dirent interface for Microsoft Visual Studio * * Copyright (C) 2006-2012 Toni Ronkko * This file is part of dirent. Dirent may be freely distributed * under the MIT license. For all details and documentation, see * https://github.com/tronkko/dirent */ #ifndef DIRENT_H #define DIRENT_H /* * Include windows.h without Windows Sockets 1.1 to prevent conflicts with * Windows Sockets 2.0. */ #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include #include #include #include /* Indicates that d_type field is available in dirent structure */ #define _DIRENT_HAVE_D_TYPE /* Indicates that d_namlen field is available in dirent structure */ #define _DIRENT_HAVE_D_NAMLEN /* Entries missing from MSVC 6.0 */ #if !defined(FILE_ATTRIBUTE_DEVICE) # define FILE_ATTRIBUTE_DEVICE 0x40 #endif /* File type and permission flags for stat(), general mask */ #if !defined(S_IFMT) # define S_IFMT _S_IFMT #endif /* Directory bit */ #if !defined(S_IFDIR) # define S_IFDIR _S_IFDIR #endif /* Character device bit */ #if !defined(S_IFCHR) # define S_IFCHR _S_IFCHR #endif /* Pipe bit */ #if !defined(S_IFFIFO) # define S_IFFIFO _S_IFFIFO #endif /* Regular file bit */ #if !defined(S_IFREG) # define S_IFREG _S_IFREG #endif /* Read permission */ #if !defined(S_IREAD) # define S_IREAD _S_IREAD #endif /* Write permission */ #if !defined(S_IWRITE) # define S_IWRITE _S_IWRITE #endif /* Execute permission */ #if !defined(S_IEXEC) # define S_IEXEC _S_IEXEC #endif /* Pipe */ #if !defined(S_IFIFO) # define S_IFIFO _S_IFIFO #endif /* Block device */ #if !defined(S_IFBLK) # define S_IFBLK 0 #endif /* Link */ #if !defined(S_IFLNK) # define S_IFLNK 0 #endif /* Socket */ #if !defined(S_IFSOCK) # define S_IFSOCK 0 #endif /* Read user permission */ #if !defined(S_IRUSR) # define S_IRUSR S_IREAD #endif /* Write user permission */ #if !defined(S_IWUSR) # define S_IWUSR S_IWRITE #endif /* Execute user permission */ #if !defined(S_IXUSR) # define S_IXUSR 0 #endif /* Read group permission */ #if !defined(S_IRGRP) # define S_IRGRP 0 #endif /* Write group permission */ #if !defined(S_IWGRP) # define S_IWGRP 0 #endif /* Execute group permission */ #if !defined(S_IXGRP) # define S_IXGRP 0 #endif /* Read others permission */ #if !defined(S_IROTH) # define S_IROTH 0 #endif /* Write others permission */ #if !defined(S_IWOTH) # define S_IWOTH 0 #endif /* Execute others permission */ #if !defined(S_IXOTH) # define S_IXOTH 0 #endif /* Maximum length of file name */ #if !defined(PATH_MAX) # define PATH_MAX MAX_PATH #endif #if !defined(FILENAME_MAX) # define FILENAME_MAX MAX_PATH #endif #if !defined(NAME_MAX) # define NAME_MAX FILENAME_MAX #endif /* File type flags for d_type */ #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK #define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are * only defined for compatibility. These macros should always return false * on Windows. */ #if !defined(S_ISFIFO) # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) #endif #if !defined(S_ISDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #if !defined(S_ISREG) # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #if !defined(S_ISLNK) # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #if !defined(S_ISSOCK) # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif #if !defined(S_ISCHR) # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #endif #if !defined(S_ISBLK) # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) #endif /* Return the exact length of the file name without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return the maximum size of a file name */ #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { /* Always zero */ long d_ino; /* File position within stream */ long d_off; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ wchar_t d_name[PATH_MAX+1]; }; typedef struct _wdirent _wdirent; struct _WDIR { /* Current directory entry */ struct _wdirent ent; /* Private file data */ WIN32_FIND_DATAW data; /* True if data is valid */ int cached; /* Win32 search handle */ HANDLE handle; /* Initial directory name */ wchar_t *patt; }; typedef struct _WDIR _WDIR; /* Multi-byte character version */ struct dirent { /* Always zero */ long d_ino; /* File position within stream */ long d_off; /* Structure size */ unsigned short d_reclen; /* Length of name without \0 */ size_t d_namlen; /* File type */ int d_type; /* File name */ char d_name[PATH_MAX+1]; }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; typedef struct DIR DIR; /* Dirent functions */ static DIR *opendir (const char *dirname); static _WDIR *_wopendir (const wchar_t *dirname); static struct dirent *readdir (DIR *dirp); static struct _wdirent *_wreaddir (_WDIR *dirp); static int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result); static int _wreaddir_r( _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); static int closedir (DIR *dirp); static int _wclosedir (_WDIR *dirp); static void rewinddir (DIR* dirp); static void _wrewinddir (_WDIR* dirp); static int scandir (const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)); static int alphasort (const struct dirent **a, const struct dirent **b); static int versionsort (const struct dirent **a, const struct dirent **b); /* For compatibility with Symbian */ #define wdirent _wdirent #define WDIR _WDIR #define wopendir _wopendir #define wreaddir _wreaddir #define wclosedir _wclosedir #define wrewinddir _wrewinddir /* Internal utility functions */ static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count); static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, const wchar_t *wcstr, size_t count); static void dirent_set_errno (int error); /* * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ static _WDIR* _wopendir( const wchar_t *dirname) { _WDIR *dirp = NULL; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate new _WDIR structure */ dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { DWORD n; /* Reset _WDIR structure */ dirp->handle = INVALID_HANDLE_VALUE; dirp->patt = NULL; dirp->cached = 0; /* Compute the length of full path plus zero terminator * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) n = wcslen(dirname); # else n = GetFullPathNameW (dirname, 0, NULL, NULL); # endif /* Allocate room for absolute directory name and search pattern */ dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); if (dirp->patt) { /* * Convert relative directory name to an absolute one. This * allows rewinddir() to function correctly even when current * working directory is changed between opendir() and rewinddir(). * * Note that on WinRT there's no way to convert relative paths * into absolute paths, so just assume it is an absolute path. */ # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) wcsncpy_s(dirp->patt, n+1, dirname, n); # else n = GetFullPathNameW (dirname, n, dirp->patt, NULL); # endif if (n > 0) { wchar_t *p; /* Append search pattern \* to the directory name */ p = dirp->patt + n; if (dirp->patt < p) { switch (p[-1]) { case '\\': case '/': case ':': /* Directory ends in path separator, e.g. c:\temp\ */ /*NOP*/; break; default: /* Directory name doesn't end in path separator */ *p++ = '\\'; } } *p++ = '*'; *p = '\0'; /* Open directory stream and retrieve the first entry */ if (dirent_first (dirp)) { /* Directory stream opened successfully */ error = 0; } else { /* Cannot retrieve first entry */ error = 1; dirent_set_errno (ENOENT); } } else { /* Cannot retrieve full path name */ dirent_set_errno (ENOENT); error = 1; } } else { /* Cannot allocate memory for search pattern */ error = 1; } } else { /* Cannot allocate _WDIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { _wclosedir (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. * * Returns pointer to static directory entry which may be overwritten by * subsequent calls to _wreaddir(). */ static struct _wdirent* _wreaddir( _WDIR *dirp) { struct _wdirent *entry; /* * Read directory entry to buffer. We can safely ignore the return value * as entry will be set to NULL in case of error. */ (void) _wreaddir_r (dirp, &dirp->ent, &entry); /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry. * * Returns zero on success. If end of directory stream is reached, then sets * result to NULL and returns zero. */ static int _wreaddir_r( _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) { WIN32_FIND_DATAW *datap; /* Read next directory entry */ datap = dirent_next (dirp); if (datap) { size_t n; DWORD attr; /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n < PATH_MAX && datap->cFileName[n] != 0) { entry->d_name[n] = datap->cFileName[n]; n++; } entry->d_name[n] = 0; /* Length of file name excluding zero terminator */ entry->d_namlen = n; /* File type */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entry->d_type = DT_DIR; } else { entry->d_type = DT_REG; } /* Reset dummy fields */ entry->d_ino = 0; entry->d_off = 0; entry->d_reclen = sizeof (struct _wdirent); /* Set result address */ *result = entry; } else { /* Return NULL to indicate end of directory */ *result = NULL; } return /*OK*/0; } /* * Close directory stream opened by opendir() function. This invalidates the * DIR structure as well as any directory entry read previously by * _wreaddir(). */ static int _wclosedir( _WDIR *dirp) { int ok; if (dirp) { /* Release search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; } /* Release search pattern */ if (dirp->patt) { free (dirp->patt); dirp->patt = NULL; } /* Release directory structure */ free (dirp); ok = /*success*/0; } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream such that _wreaddir() returns the very first * file name again. */ static void _wrewinddir( _WDIR* dirp) { if (dirp) { /* Release existing search handle */ if (dirp->handle != INVALID_HANDLE_VALUE) { FindClose (dirp->handle); } /* Open new search handle */ dirent_first (dirp); } } /* Get first directory entry (internal) */ static WIN32_FIND_DATAW* dirent_first( _WDIR *dirp) { WIN32_FIND_DATAW *datap; /* Open directory and retrieve the first entry */ dirp->handle = FindFirstFileExW( dirp->patt, FindExInfoStandard, &dirp->data, FindExSearchNameMatch, NULL, 0); if (dirp->handle != INVALID_HANDLE_VALUE) { /* a directory entry is now waiting in memory */ datap = &dirp->data; dirp->cached = 1; } else { /* Failed to re-open directory: no directory entry in memory */ dirp->cached = 0; datap = NULL; } return datap; } /* * Get next directory entry (internal). * * Returns */ static WIN32_FIND_DATAW* dirent_next( _WDIR *dirp) { WIN32_FIND_DATAW *p; /* Get next directory entry */ if (dirp->cached != 0) { /* A valid directory entry already in memory */ p = &dirp->data; dirp->cached = 0; } else if (dirp->handle != INVALID_HANDLE_VALUE) { /* Get the next directory entry from stream */ if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { /* Got a file */ p = &dirp->data; } else { /* The very last entry has been processed or an error occurred */ FindClose (dirp->handle); dirp->handle = INVALID_HANDLE_VALUE; p = NULL; } } else { /* End of directory stream reached */ p = NULL; } return p; } /* * Open directory stream using plain old C-string. */ static DIR* opendir( const char *dirname) { struct DIR *dirp; int error; /* Must have directory name */ if (dirname == NULL || dirname[0] == '\0') { dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX + 1]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s( &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; } else { /* Failed to open directory stream */ error = 1; } } else { /* * Cannot convert file name to wide-character string. This * occurs if the string contains invalid multi-byte sequences or * the output buffer is too small to contain the resulting * string. */ error = 1; } } else { /* Cannot allocate DIR structure */ error = 1; } /* Clean up in case of error */ if (error && dirp) { free (dirp); dirp = NULL; } return dirp; } /* * Read next directory entry. */ static struct dirent* readdir( DIR *dirp) { struct dirent *entry; /* * Read directory entry to buffer. We can safely ignore the return value * as entry will be set to NULL in case of error. */ (void) readdir_r (dirp, &dirp->ent, &entry); /* Return pointer to statically allocated directory entry */ return entry; } /* * Read next directory entry into called-allocated buffer. * * Returns zero on success. If the end of directory stream is reached, then * sets result to NULL and returns zero. */ static int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result) { WIN32_FIND_DATAW *datap; /* Read next directory entry */ datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, entry->d_name, PATH_MAX + 1, datap->cAlternateFileName, PATH_MAX + 1); } if (!error) { DWORD attr; /* Length of file name excluding zero terminator */ entry->d_namlen = n - 1; /* File attributes */ attr = datap->dwFileAttributes; if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { entry->d_type = DT_CHR; } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { entry->d_type = DT_DIR; } else { entry->d_type = DT_REG; } /* Reset dummy fields */ entry->d_ino = 0; entry->d_off = 0; entry->d_reclen = sizeof (struct dirent); } else { /* * Cannot convert file name to multi-byte string so construct * an erroneous directory entry and return that. Note that * we cannot return NULL as that would stop the processing * of directory entries completely. */ entry->d_name[0] = '?'; entry->d_name[1] = '\0'; entry->d_namlen = 1; entry->d_type = DT_UNKNOWN; entry->d_ino = 0; entry->d_off = -1; entry->d_reclen = 0; } /* Return pointer to directory entry */ *result = entry; } else { /* No more directory entries */ *result = NULL; } return /*OK*/0; } /* * Close directory stream. */ static int closedir( DIR *dirp) { int ok; if (dirp) { /* Close wide-character directory stream */ ok = _wclosedir (dirp->wdirp); dirp->wdirp = NULL; /* Release multi-byte character version */ free (dirp); } else { /* Invalid directory stream */ dirent_set_errno (EBADF); ok = /*failure*/-1; } return ok; } /* * Rewind directory stream to beginning. */ static void rewinddir( DIR* dirp) { /* Rewind wide-character string directory stream */ _wrewinddir (dirp->wdirp); } /* * Scan directory for entries. */ static int scandir( const char *dirname, struct dirent ***namelist, int (*filter)(const struct dirent*), int (*compare)(const struct dirent**, const struct dirent**)) { struct dirent **files = NULL; size_t size = 0; size_t allocated = 0; const size_t init_size = 1; DIR *dir = NULL; struct dirent *entry; struct dirent *tmp = NULL; size_t i; int result = 0; /* Open directory stream */ dir = opendir (dirname); if (dir) { /* Read directory entries to memory */ while (1) { /* Enlarge pointer table to make room for another pointer */ if (size >= allocated) { void *p; size_t num_entries; /* Compute number of entries in the enlarged pointer table */ if (size < init_size) { /* Allocate initial pointer table */ num_entries = init_size; } else { /* Double the size */ num_entries = size * 2; } /* Allocate first pointer table or enlarge existing table */ p = realloc (files, sizeof (void*) * num_entries); if (p != NULL) { /* Got the memory */ files = (dirent**) p; allocated = num_entries; } else { /* Out of memory */ result = -1; break; } } /* Allocate room for temporary directory entry */ if (tmp == NULL) { tmp = (struct dirent*) malloc (sizeof (struct dirent)); if (tmp == NULL) { /* Cannot allocate temporary directory entry */ result = -1; break; } } /* Read directory entry to temporary area */ if (readdir_r (dir, tmp, &entry) == /*OK*/0) { /* Did we get an entry? */ if (entry != NULL) { int pass; /* Determine whether to include the entry in result */ if (filter) { /* Let the filter function decide */ pass = filter (tmp); } else { /* No filter function, include everything */ pass = 1; } if (pass) { /* Store the temporary entry to pointer table */ files[size++] = tmp; tmp = NULL; /* Keep up with the number of files */ result++; } } else { /* * End of directory stream reached => sort entries and * exit. */ qsort (files, size, sizeof (void*), (int (*) (const void*, const void*)) compare); break; } } else { /* Error reading directory entry */ result = /*Error*/ -1; break; } } } else { /* Cannot open directory */ result = /*Error*/ -1; } /* Release temporary directory entry */ if (tmp) { free (tmp); } /* Release allocated memory on error */ if (result < 0) { for (i = 0; i < size; i++) { free (files[i]); } free (files); files = NULL; } /* Close directory stream */ if (dir) { closedir (dir); } /* Pass pointer table to caller */ if (namelist) { *namelist = files; } return result; } /* Alphabetical sorting */ static int alphasort( const struct dirent **a, const struct dirent **b) { return strcoll ((*a)->d_name, (*b)->d_name); } /* Sort versions */ static int versionsort( const struct dirent **a, const struct dirent **b) { /* FIXME: implement strverscmp and use that */ return alphasort (a, b); } /* Convert multi-byte string to wide character string */ static int dirent_mbstowcs_s( size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, const char *mbstr, size_t count) { int error; int n; size_t len; UINT cp; DWORD flags; /* Determine code page for multi-byte string */ if (AreFileApisANSI ()) { /* Default ANSI code page */ cp = GetACP (); } else { /* Default OEM code page */ cp = GetOEMCP (); } /* * Determine flags based on the character set. For more information, * please see https://docs.microsoft.com/fi-fi/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar */ switch (cp) { case 42: case 50220: case 50221: case 50222: case 50225: case 50227: case 50229: case 57002: case 57003: case 57004: case 57005: case 57006: case 57007: case 57008: case 57009: case 57010: case 57011: case 65000: /* MultiByteToWideChar does not support MB_ERR_INVALID_CHARS */ flags = 0; break; default: /* * Ask MultiByteToWideChar to return an error if a multi-byte * character cannot be converted to a wide-character. */ flags = MB_ERR_INVALID_CHARS; } /* Compute the length of input string without zero-terminator */ len = 0; while (mbstr[len] != '\0' && len < count) { len++; } /* Convert to wide-character string */ n = MultiByteToWideChar( /* Source code page */ cp, /* Flags */ flags, /* Pointer to string to convert */ mbstr, /* Size of multi-byte string */ (int) len, /* Pointer to output buffer */ wcstr, /* Size of output buffer */ (int)sizeInWords - 1 ); /* Ensure that output buffer is zero-terminated */ wcstr[n] = '\0'; /* Return length of wide-character string with zero-terminator */ *pReturnValue = (size_t) (n + 1); /* Return zero if conversion succeeded */ if (n > 0) { error = 0; } else { error = 1; } return error; } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, /* max size of mbstr */ const wchar_t *wcstr, size_t count) { int n; int error; UINT cp; size_t len; BOOL flag = 0; LPBOOL pflag; /* Determine code page for multi-byte string */ if (AreFileApisANSI ()) { /* Default ANSI code page */ cp = GetACP (); } else { /* Default OEM code page */ cp = GetOEMCP (); } /* Compute the length of input string without zero-terminator */ len = 0; while (wcstr[len] != '\0' && len < count) { len++; } /* * Determine if we can ask WideCharToMultiByte to return information on * broken characters. For more information, please see * https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte */ switch (cp) { case CP_UTF7: case CP_UTF8: /* * WideCharToMultiByte fails if we request information on default * characters. This is just a nuisance but doesn't affect the * conversion: if Windows is configured to use UTF-8, then the default * character should not be needed anyway. */ pflag = NULL; break; default: /* * Request that WideCharToMultiByte sets the flag if it uses the * default character. */ pflag = &flag; } /* Convert wide-character string to multi-byte character string */ n = WideCharToMultiByte( /* Target code page */ cp, /* Flags */ 0, /* Pointer to unicode string */ wcstr, /* Length of unicode string */ (int) len, /* Pointer to output buffer */ mbstr, /* Size of output buffer */ (int)sizeInBytes - 1, /* Default character */ NULL, /* Whether default character was used or not */ pflag ); /* Ensure that output buffer is zero-terminated */ mbstr[n] = '\0'; /* Return length of multi-byte string with zero-terminator */ *pReturnValue = (size_t) (n + 1); /* Return zero if conversion succeeded without using default characters */ if (n > 0 && flag == 0) { error = 0; } else { error = 1; } return error; } /* Set errno variable */ static void dirent_set_errno( int error) { #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 and later */ _set_errno (error); #else /* Non-Microsoft compiler or older Microsoft compiler */ errno = error; #endif } #ifdef __cplusplus } #endif #endif /*DIRENT_H*/ libmongocrypt-1.19.0/src/000077500000000000000000000000001521103432300152535ustar00rootroot00000000000000libmongocrypt-1.19.0/src/crypto/000077500000000000000000000000001521103432300165735ustar00rootroot00000000000000libmongocrypt-1.19.0/src/crypto/cng.c000066400000000000000000000445501521103432300175160ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../mongocrypt-crypto-private.h" #include "../mongocrypt-private.h" #include #ifdef MONGOCRYPT_ENABLE_CRYPTO_CNG #include static BCRYPT_ALG_HANDLE _algo_sha512_hmac = 0; static BCRYPT_ALG_HANDLE _algo_sha256_hmac = 0; static BCRYPT_ALG_HANDLE _algo_aes256_cbc = 0; static BCRYPT_ALG_HANDLE _algo_aes256_ecb = 0; static DWORD _aes256_key_blob_length; static DWORD _aes256_block_length; static BCRYPT_ALG_HANDLE _random; #define STATUS_SUCCESS 0 bool _native_crypto_initialized = false; void _native_crypto_init(void) { DWORD cbOutput; NTSTATUS nt_status; /* Note, there is no mechanism for libmongocrypt to close these providers, * If we ever add such a mechanism, call BCryptCloseAlgorithmProvider. */ nt_status = BCryptOpenAlgorithmProvider(&_algo_sha512_hmac, BCRYPT_SHA512_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptOpenAlgorithmProvider(&_algo_sha256_hmac, BCRYPT_SHA256_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptOpenAlgorithmProvider(&_algo_aes256_cbc, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptSetProperty(_algo_aes256_cbc, BCRYPT_CHAINING_MODE, (PUCHAR)(BCRYPT_CHAIN_MODE_CBC), (ULONG)(sizeof(wchar_t) * wcslen(BCRYPT_CHAIN_MODE_CBC)), 0); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptOpenAlgorithmProvider(&_algo_aes256_ecb, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptSetProperty(_algo_aes256_ecb, BCRYPT_CHAINING_MODE, (PUCHAR)(BCRYPT_CHAIN_MODE_ECB), (ULONG)(sizeof(wchar_t) * wcslen(BCRYPT_CHAIN_MODE_ECB)), 0); if (nt_status != STATUS_SUCCESS) { return; } cbOutput = sizeof(_aes256_key_blob_length); nt_status = BCryptGetProperty(_algo_aes256_cbc, BCRYPT_OBJECT_LENGTH, (PUCHAR)(&_aes256_key_blob_length), cbOutput, &cbOutput, 0); if (nt_status != STATUS_SUCCESS) { return; } cbOutput = sizeof(_aes256_block_length); nt_status = BCryptGetProperty(_algo_aes256_cbc, BCRYPT_BLOCK_LENGTH, (PUCHAR)(&_aes256_block_length), cbOutput, &cbOutput, 0); if (nt_status != STATUS_SUCCESS) { return; } nt_status = BCryptOpenAlgorithmProvider(&_random, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (nt_status != STATUS_SUCCESS) { return; } _native_crypto_initialized = true; } typedef struct { unsigned char *key_object; uint32_t key_object_length; BCRYPT_KEY_HANDLE key_handle; unsigned char *iv; uint32_t iv_len; } cng_encrypt_state; static void _crypto_state_destroy(cng_encrypt_state *state); static cng_encrypt_state * _crypto_state_init(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *iv, mongocrypt_status_t *status) { cng_encrypt_state *state; uint32_t keyBlobLength; unsigned char *keyBlob; BCRYPT_KEY_DATA_BLOB_HEADER blobHeader; NTSTATUS nt_status; BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(iv); keyBlob = NULL; state = bson_malloc0(sizeof(*state)); BSON_ASSERT(state); state->key_handle = INVALID_HANDLE_VALUE; /* Initialize key storage buffer */ state->key_object = bson_malloc0(_aes256_key_blob_length); BSON_ASSERT(state->key_object); state->key_object_length = _aes256_key_blob_length; /* Allocate temporary buffer for key import */ BSON_ASSERT(sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->len <= UINT32_MAX); keyBlobLength = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->len; keyBlob = bson_malloc0(keyBlobLength); BSON_ASSERT(keyBlob); blobHeader.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; blobHeader.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; blobHeader.cbKeyData = key->len; memcpy(keyBlob, &blobHeader, sizeof(BCRYPT_KEY_DATA_BLOB_HEADER)); memcpy(keyBlob + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), key->data, key->len); nt_status = BCryptImportKey(_algo_aes256_cbc, NULL, BCRYPT_KEY_DATA_BLOB, &(state->key_handle), state->key_object, state->key_object_length, keyBlob, keyBlobLength, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("Import Key Failed: 0x%x", (int)nt_status); goto fail; } bson_free(keyBlob); state->iv = bson_malloc0(iv->len); BSON_ASSERT(state->iv); state->iv_len = iv->len; memcpy(state->iv, iv->data, iv->len); return state; fail: _crypto_state_destroy(state); bson_free(keyBlob); return NULL; } static void _crypto_state_destroy(cng_encrypt_state *state) { if (state) { /* Free the key handle before the key_object that contains it */ if (state->key_handle != INVALID_HANDLE_VALUE) { BCryptDestroyKey(state->key_handle); } bson_free(state->key_object); bson_free(state->iv); bson_free(state); } } bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) { BSON_ASSERT(args.in); BSON_ASSERT(args.out); bool ret = false; mongocrypt_status_t *status = args.status; cng_encrypt_state *state = _crypto_state_init(args.key, args.iv, status); BSON_ASSERT(state); NTSTATUS nt_status; ULONG bytes_written = args.bytes_written ? *args.bytes_written : 0u; nt_status = BCryptEncrypt(state->key_handle, (PUCHAR)(args.in->data), args.in->len, NULL, state->iv, state->iv_len, args.out->data, args.out->len, &bytes_written, 0); args.bytes_written ? (*args.bytes_written = bytes_written) : (void)0; if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error initializing cipher: 0x%x", (int)nt_status); goto done; } ret = true; done: _crypto_state_destroy(state); return ret; } bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) { BSON_ASSERT(args.in); BSON_ASSERT(args.out); bool ret = false; mongocrypt_status_t *status = args.status; cng_encrypt_state *state = _crypto_state_init(args.key, args.iv, status); BSON_ASSERT(state); NTSTATUS nt_status; ULONG bytes_written = args.bytes_written ? *args.bytes_written : 0u; nt_status = BCryptDecrypt(state->key_handle, (PUCHAR)(args.in->data), args.in->len, NULL, state->iv, state->iv_len, args.out->data, args.out->len, &bytes_written, 0); args.bytes_written ? (*args.bytes_written = bytes_written) : (void)0; if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error initializing cipher: 0x%x", (int)nt_status); goto done; } ret = true; done: _crypto_state_destroy(state); return ret; } /* _hmac_with_algorithm computes an HMAC of @in with the algorithm specified by * @hAlgorithm. * @key is the input key. * @out is the output. @out must be allocated by the caller with * the expected length @expect_out_len for the output. * Returns false and sets @status on error. @status is required. */ static bool _hmac_with_algorithm(BCRYPT_ALG_HANDLE hAlgorithm, const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, uint32_t expect_out_len, mongocrypt_status_t *status) { bool ret = false; BCRYPT_HASH_HANDLE hHash; NTSTATUS nt_status; BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); if (out->len != expect_out_len) { CLIENT_ERR("out does not contain " PRIu32 " bytes", expect_out_len); return false; } nt_status = BCryptCreateHash(hAlgorithm, &hHash, NULL, 0, (PUCHAR)key->data, (ULONG)key->len, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error initializing hmac: 0x%x", (int)nt_status); /* Only call BCryptDestroyHash if BCryptCreateHash succeeded. */ return false; } nt_status = BCryptHashData(hHash, (PUCHAR)in->data, (ULONG)in->len, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error hashing data: 0x%x", (int)nt_status); goto done; } nt_status = BCryptFinishHash(hHash, out->data, out->len, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error finishing hmac: 0x%x", (int)nt_status); goto done; } ret = true; done: (void)BCryptDestroyHash(hHash); return ret; } bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_algorithm(_algo_sha512_hmac, key, in, out, MONGOCRYPT_HMAC_SHA512_LEN, status); } bool _native_crypto_random(_mongocrypt_buffer_t *out, uint32_t count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); NTSTATUS nt_status = BCryptGenRandom(_random, out->data, count, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("BCryptGenRandom Failed: 0x%x", (int)nt_status); return false; } return true; } typedef struct { BCRYPT_KEY_HANDLE key_handle; unsigned char *input_block; uint32_t input_block_len; unsigned char *output_block; uint32_t output_block_len; uint32_t output_block_ptr; } cng_ctr_encrypt_state; static bool _cng_ctr_crypto_generate(cng_ctr_encrypt_state *state, mongocrypt_status_t *status) { BSON_ASSERT(state); ULONG bytesEncrypted = 0; NTSTATUS nt_status = BCryptEncrypt(state->key_handle, state->input_block, state->input_block_len, NULL, NULL, 0, state->output_block, state->output_block_len, &bytesEncrypted, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("error encrypting: 0x%x", (int)nt_status); return false; } BSON_ASSERT(bytesEncrypted); state->output_block_ptr = 0; return true; } static void _cng_ctr_crypto_advance(cng_ctr_encrypt_state *state) { BSON_ASSERT_PARAM(state); /* Assert rather than return false/NULL since this function's type is void */ BSON_ASSERT(sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) <= UINT32_MAX - state->input_block_len); uint32_t carry = 1; for (int i = (int)state->input_block_len - 1; i >= 0 && carry != 0; --i) { uint32_t bpp = (uint32_t)(state->input_block[i]) + carry; carry = bpp >> 8; state->input_block[i] = bpp & 0xFF; } } static bool _cng_ctr_crypto_next(cng_ctr_encrypt_state *state, mongocrypt_status_t *status, unsigned char *mask) { BSON_ASSERT_PARAM(state); BSON_ASSERT_PARAM(mask); if (state->output_block_ptr >= state->output_block_len) { _cng_ctr_crypto_advance(state); if (!_cng_ctr_crypto_generate(state, status)) { return false; }; } *mask = state->output_block[state->output_block_ptr]; ++state->output_block_ptr; return true; } static void _cng_ctr_crypto_state_destroy(cng_ctr_encrypt_state *state); static cng_ctr_encrypt_state *_cng_ctr_crypto_state_init(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *iv, mongocrypt_status_t *status) { cng_ctr_encrypt_state *state; uint32_t keyBlobLength; unsigned char *keyBlob; BCRYPT_KEY_DATA_BLOB_HEADER blobHeader; NTSTATUS nt_status; BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(iv); keyBlob = NULL; state = bson_malloc0(sizeof(*state)); BSON_ASSERT(state); if (UINT32_MAX - key->len < sizeof(BCRYPT_KEY_DATA_BLOB_HEADER)) { CLIENT_ERR("key is too long"); goto fail; } state->key_handle = INVALID_HANDLE_VALUE; /* Initialize input storage buffer */ state->input_block = bson_malloc0(_aes256_block_length); BSON_ASSERT(state->input_block); state->input_block_len = _aes256_block_length; BSON_ASSERT(iv->len == _aes256_block_length); memcpy(state->input_block, iv->data, iv->len); /* Initialize output storage buffer */ state->output_block = bson_malloc0(_aes256_block_length); BSON_ASSERT(state->output_block); state->output_block_len = _aes256_block_length; state->output_block_ptr = 0; /* Allocate temporary buffer for key import */ keyBlobLength = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + key->len; keyBlob = bson_malloc0(keyBlobLength); BSON_ASSERT(keyBlob); blobHeader.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; blobHeader.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; blobHeader.cbKeyData = key->len; memcpy(keyBlob, &blobHeader, sizeof(BCRYPT_KEY_DATA_BLOB_HEADER)); memcpy(keyBlob + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), key->data, key->len); nt_status = BCryptImportKey(_algo_aes256_ecb, NULL, BCRYPT_KEY_DATA_BLOB, &(state->key_handle), NULL, 0, keyBlob, keyBlobLength, 0); if (nt_status != STATUS_SUCCESS) { CLIENT_ERR("Import Key Failed: 0x%x", (int)nt_status); goto fail; } bson_free(keyBlob); if (!_cng_ctr_crypto_generate(state, status)) { goto fail; } return state; fail: _cng_ctr_crypto_state_destroy(state); bson_free(keyBlob); return NULL; } static void _cng_ctr_crypto_state_destroy(cng_ctr_encrypt_state *state) { if (state) { if (state->key_handle != INVALID_HANDLE_VALUE) { BCryptDestroyKey(state->key_handle); } bson_free(state->input_block); bson_free(state->output_block); bson_free(state); } } bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) { bool ret = false; cng_ctr_encrypt_state *state = NULL; mongocrypt_status_t *status = args.status; BSON_ASSERT(args.in && args.in->data); BSON_ASSERT(args.out && args.out->data); if (args.out->len < args.in->len) { CLIENT_ERR("Output buffer is too small"); goto fail; } state = _cng_ctr_crypto_state_init(args.key, args.iv, status); if (!state) { goto fail; } for (uint32_t i = 0; i < args.in->len; ++i) { unsigned char mask; if (!_cng_ctr_crypto_next(state, status, &mask)) { goto fail; } args.out->data[i] = args.in->data[i] ^ mask; } if (args.bytes_written) { *args.bytes_written = args.in->len; } ret = true; fail: _cng_ctr_crypto_state_destroy(state); return ret; } bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) { bool ret = false; cng_ctr_encrypt_state *state = NULL; mongocrypt_status_t *status = args.status; BSON_ASSERT(args.in && args.in->data); BSON_ASSERT(args.out && args.out->data); if (args.out->len < args.in->len) { CLIENT_ERR("Output buffer is too small"); goto fail; } state = _cng_ctr_crypto_state_init(args.key, args.iv, status); if (!state) { goto fail; } for (uint32_t i = 0; i < args.in->len; ++i) { unsigned char mask; if (!_cng_ctr_crypto_next(state, status, &mask)) { goto fail; } args.out->data[i] = args.in->data[i] ^ mask; } if (args.bytes_written) { *args.bytes_written = args.in->len; } ret = true; fail: _cng_ctr_crypto_state_destroy(state); return ret; } bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_algorithm(_algo_sha256_hmac, key, in, out, MONGOCRYPT_HMAC_SHA256_LEN, status); } #endif /* MONGOCRYPT_ENABLE_CRYPTO_CNG */ libmongocrypt-1.19.0/src/crypto/commoncrypto.c000066400000000000000000000234171521103432300214770ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "../mongocrypt-crypto-private.h" #include "../mongocrypt-private.h" #ifdef MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO #include #include #include static const char *CCCryptorStatus_to_string(CCCryptorStatus status) { switch (status) { case kCCSuccess: return "Success"; case kCCParamError: return "ParamError"; case kCCBufferTooSmall: return "BufferTooSmall"; case kCCMemoryFailure: return "MemoryFailure"; case kCCAlignmentError: return "AlignmentError"; case kCCDecodeError: return "DecodeError"; case kCCUnimplemented: return "Unimplemented"; case kCCOverflow: return "Overflow"; case kCCRNGFailure: return "RNGFailure"; case kCCUnspecifiedError: return "UnspecifiedError"; case kCCCallSequenceError: return "CallSequenceError"; case kCCKeySizeError: return "KeySizeError"; case kCCInvalidKey: return "InvalidKey"; default: return "Unknown"; } } bool _native_crypto_initialized = false; void _native_crypto_init(void) { _native_crypto_initialized = true; } static bool _native_crypto_aes_256_cbc_encrypt_with_mode(aes_256_args_t args, CCMode mode) { BSON_ASSERT(args.iv); BSON_ASSERT(args.key); BSON_ASSERT(args.in); BSON_ASSERT(args.out); bool ret = false; CCCryptorRef ctx = NULL; CCCryptorStatus cc_status; size_t intermediate_bytes_written; mongocrypt_status_t *status = args.status; cc_status = CCCryptorCreateWithMode(kCCEncrypt, mode, kCCAlgorithmAES, 0 /* defaults to CBC w/ no padding */, args.iv->data, args.key->data, kCCKeySizeAES256, NULL, 0, 0, 0, &ctx); if (cc_status != kCCSuccess) { if (cc_status == kCCUnimplemented && mode == kCCModeCTR) { CLIENT_ERR("error initializing cipher: %s (%d). CTR mode is only " "supported on macOS 10.15+", CCCryptorStatus_to_string(cc_status), (int)cc_status); } else { CLIENT_ERR("error initializing cipher: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); } goto done; } *args.bytes_written = 0; cc_status = CCCryptorUpdate(ctx, args.in->data, args.in->len, args.out->data, args.out->len, &intermediate_bytes_written); if (cc_status != kCCSuccess) { CLIENT_ERR("error encrypting: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); goto done; } BSON_ASSERT(intermediate_bytes_written <= UINT32_MAX); *args.bytes_written = (uint32_t)intermediate_bytes_written; BSON_ASSERT(args.out->len >= *args.bytes_written); cc_status = CCCryptorFinal(ctx, args.out->data + *args.bytes_written, args.out->len - *args.bytes_written, &intermediate_bytes_written); BSON_ASSERT(UINT32_MAX - *args.bytes_written >= intermediate_bytes_written); *args.bytes_written += intermediate_bytes_written; if (cc_status != kCCSuccess) { CLIENT_ERR("error finalizing: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); goto done; } ret = true; done: CCCryptorRelease(ctx); return ret; } bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) { return _native_crypto_aes_256_cbc_encrypt_with_mode(args, kCCModeCBC); } bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) { return _native_crypto_aes_256_cbc_encrypt_with_mode(args, kCCModeCTR); } /* Note, the decrypt function is almost exactly the same as the encrypt * functions except for the kCCDecrypt and the error message. */ static bool _native_crypto_aes_256_cbc_decrypt_with_mode(aes_256_args_t args, CCMode mode) { BSON_ASSERT(args.iv); BSON_ASSERT(args.key); BSON_ASSERT(args.in); BSON_ASSERT(args.out); bool ret = false; CCCryptorRef ctx = NULL; CCCryptorStatus cc_status; size_t intermediate_bytes_written; mongocrypt_status_t *status = args.status; cc_status = CCCryptorCreateWithMode(kCCDecrypt, mode, kCCAlgorithmAES, 0 /* defaults to CBC w/ no padding */, args.iv->data, args.key->data, kCCKeySizeAES256, NULL, 0, 0, 0, &ctx); if (cc_status != kCCSuccess) { if (cc_status == kCCUnimplemented && mode == kCCModeCTR) { CLIENT_ERR("error initializing cipher: %s (%d). CTR mode is only " "supported on macOS 10.15+", CCCryptorStatus_to_string(cc_status), (int)cc_status); } else { CLIENT_ERR("error initializing cipher: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); } goto done; } *args.bytes_written = 0; cc_status = CCCryptorUpdate(ctx, args.in->data, args.in->len, args.out->data, args.out->len, &intermediate_bytes_written); if (cc_status != kCCSuccess) { CLIENT_ERR("error decrypting: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); goto done; } BSON_ASSERT(intermediate_bytes_written <= UINT32_MAX); *args.bytes_written = (uint32_t)intermediate_bytes_written; BSON_ASSERT(args.out->len >= *args.bytes_written); cc_status = CCCryptorFinal(ctx, args.out->data + *args.bytes_written, args.out->len - *args.bytes_written, &intermediate_bytes_written); BSON_ASSERT(UINT32_MAX - *args.bytes_written >= intermediate_bytes_written); *args.bytes_written += intermediate_bytes_written; if (cc_status != kCCSuccess) { CLIENT_ERR("error finalizing: %s (%d)", CCCryptorStatus_to_string(cc_status), (int)cc_status); goto done; } ret = true; done: CCCryptorRelease(ctx); return ret; } bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) { return _native_crypto_aes_256_cbc_decrypt_with_mode(args, kCCModeCBC); } bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) { return _native_crypto_aes_256_cbc_decrypt_with_mode(args, kCCModeCTR); } /* _hmac_with_algorithm computes an HMAC of @in with the algorithm specified by * @algorithm. * @key is the input key. * @out is the output. @out must be allocated by the caller with * the expected length @expect_out_len for the output. * Returns false and sets @status on error. @status is required. */ static bool _hmac_with_algorithm(CCHmacAlgorithm algorithm, const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, uint32_t expect_out_len, mongocrypt_status_t *status) { CCHmacContext *ctx; BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); if (out->len != expect_out_len) { CLIENT_ERR("out does not contain %" PRIu32 " bytes", expect_out_len); return false; } ctx = bson_malloc0(sizeof(*ctx)); BSON_ASSERT(ctx); /* The ->len members are uint32_t and these functions take size_t */ CCHmacInit(ctx, algorithm, key->data, key->len); CCHmacUpdate(ctx, in->data, in->len); CCHmacFinal(ctx, out->data); bson_free(ctx); return true; } bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_algorithm(kCCHmacAlgSHA512, key, in, out, MONGOCRYPT_HMAC_SHA512_LEN, status); } bool _native_crypto_random(_mongocrypt_buffer_t *out, uint32_t count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); CCRNGStatus ret = CCRandomGenerateBytes(out->data, (size_t)count); if (ret != kCCSuccess) { CLIENT_ERR("failed to generate random iv: %d", (int)ret); return false; } return true; } bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_algorithm(kCCHmacAlgSHA256, key, in, out, MONGOCRYPT_HMAC_SHA256_LEN, status); } #endif /* MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO */ libmongocrypt-1.19.0/src/crypto/libcrypto.c000066400000000000000000000430441521103432300207530ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Comments in this implementation refer to: * [MCGREW] https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05 */ #include "../mongocrypt-crypto-private.h" #include "../mongocrypt-log-private.h" #include "../mongocrypt-private.h" #ifdef MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include #include #endif bool _native_crypto_initialized = false; /* _encrypt_with_cipher encrypts @in with the specified OpenSSL cipher. * @cipher is a usable EVP_CIPHER, or NULL if early initialization failed. * @cipher_description is a human-readable description used when reporting deferred errors from initialization, required * if @cipher might be NULL. * @key is the input key. @iv is the input IV. * @out is the output ciphertext. @out must be allocated by the caller with * enough room for the ciphertext. * @bytes_written is the number of bytes that were written to @out. * Returns false and sets @status on error. @status is required. */ static bool _encrypt_with_cipher(const EVP_CIPHER *cipher, const char *cipher_description, aes_256_args_t args) { BSON_ASSERT(args.key); BSON_ASSERT(args.in); BSON_ASSERT(args.out); BSON_ASSERT(args.in->len <= INT_MAX); mongocrypt_status_t *status = args.status; if (!cipher) { BSON_ASSERT(cipher_description); CLIENT_ERR("failed to initialize cipher %s", cipher_description); return false; } BSON_ASSERT(NULL == args.iv || (uint32_t)EVP_CIPHER_iv_length(cipher) == args.iv->len); BSON_ASSERT((uint32_t)EVP_CIPHER_key_length(cipher) == args.key->len); bool ret = false; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); BSON_ASSERT(ctx); if (!EVP_EncryptInit_ex(ctx, cipher, NULL /* engine */, args.key->data, NULL == args.iv ? NULL : args.iv->data)) { CLIENT_ERR("error in EVP_EncryptInit_ex: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } /* Disable the default OpenSSL padding. */ EVP_CIPHER_CTX_set_padding(ctx, 0); *args.bytes_written = 0; int intermediate_bytes_written = 0; if (!EVP_EncryptUpdate(ctx, args.out->data, &intermediate_bytes_written, args.in->data, (int)args.in->len)) { CLIENT_ERR("error in EVP_EncryptUpdate: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } BSON_ASSERT(intermediate_bytes_written >= 0 && (uint64_t)intermediate_bytes_written <= UINT32_MAX); /* intermediate_bytes_written cannot be negative, so int -> uint32_t is OK */ *args.bytes_written = (uint32_t)intermediate_bytes_written; if (!EVP_EncryptFinal_ex(ctx, args.out->data, &intermediate_bytes_written)) { CLIENT_ERR("error in EVP_EncryptFinal_ex: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } BSON_ASSERT(UINT32_MAX - *args.bytes_written >= (uint32_t)intermediate_bytes_written); *args.bytes_written += (uint32_t)intermediate_bytes_written; ret = true; done: EVP_CIPHER_CTX_free(ctx); return ret; } /* _decrypt_with_cipher decrypts @in with the specified OpenSSL cipher. * @cipher is a usable EVP_CIPHER, or NULL if early initialization failed. * @cipher_description is a human-readable description used when reporting deferred errors from initialization, required * if @cipher might be NULL. * @key is the input key. @iv is the input IV. * @out is the output plaintext. @out must be allocated by the caller with * enough room for the plaintext. * @bytes_written is the number of bytes that were written to @out. * Returns false and sets @status on error. @status is required. */ static bool _decrypt_with_cipher(const EVP_CIPHER *cipher, const char *cipher_description, aes_256_args_t args) { BSON_ASSERT(args.iv); BSON_ASSERT(args.key); BSON_ASSERT(args.in); BSON_ASSERT(args.out); BSON_ASSERT(args.in->len <= INT_MAX); mongocrypt_status_t *status = args.status; if (!cipher) { BSON_ASSERT_PARAM(cipher_description); CLIENT_ERR("failed to initialize cipher %s", cipher_description); return false; } BSON_ASSERT((uint32_t)EVP_CIPHER_iv_length(cipher) == args.iv->len); BSON_ASSERT((uint32_t)EVP_CIPHER_key_length(cipher) == args.key->len); bool ret = false; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); BSON_ASSERT(ctx); if (!EVP_DecryptInit_ex(ctx, cipher, NULL /* engine */, args.key->data, args.iv->data)) { CLIENT_ERR("error in EVP_DecryptInit_ex: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } /* Disable padding. */ EVP_CIPHER_CTX_set_padding(ctx, 0); *args.bytes_written = 0; int intermediate_bytes_written = 0; if (!EVP_DecryptUpdate(ctx, args.out->data, &intermediate_bytes_written, args.in->data, (int)args.in->len)) { CLIENT_ERR("error in EVP_DecryptUpdate: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } BSON_ASSERT(intermediate_bytes_written >= 0 && (uint64_t)intermediate_bytes_written <= UINT32_MAX); /* intermediate_bytes_written cannot be negative, so int -> uint32_t is OK */ *args.bytes_written = (uint32_t)intermediate_bytes_written; if (!EVP_DecryptFinal_ex(ctx, args.out->data, &intermediate_bytes_written)) { CLIENT_ERR("error in EVP_DecryptFinal_ex: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } BSON_ASSERT(UINT32_MAX - *args.bytes_written >= (uint32_t)intermediate_bytes_written); *args.bytes_written += (uint32_t)intermediate_bytes_written; ret = true; done: EVP_CIPHER_CTX_free(ctx); return ret; } bool _native_crypto_random(_mongocrypt_buffer_t *out, uint32_t count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); BSON_ASSERT(count <= INT_MAX); int ret = RAND_bytes(out->data, (int)count); /* From man page: "RAND_bytes() and RAND_priv_bytes() return 1 on success, -1 * if not supported by the current RAND method, or 0 on other failure. The * error code can be obtained by ERR_get_error(3)" */ if (ret == -1) { CLIENT_ERR("secure random IV not supported: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } else if (ret == 0) { CLIENT_ERR("failed to generate random IV: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } return true; } #if OPENSSL_VERSION_NUMBER >= 0x30000000L // Newest libcrypto support: requires EVP_MAC_CTX_dup and EVP_CIPHER_fetch added in OpenSSL 3.0.0 static struct { EVP_MAC_CTX *hmac_sha2_256; EVP_MAC_CTX *hmac_sha2_512; EVP_CIPHER *aes_256_cbc; EVP_CIPHER *aes_256_ctr; EVP_CIPHER *aes_256_ecb; // For testing only } _mongocrypt_libcrypto; EVP_MAC_CTX *_build_hmac_ctx_prototype(const char *digest_name) { EVP_MAC *hmac = EVP_MAC_fetch(NULL, OSSL_MAC_NAME_HMAC, NULL); if (!hmac) { return NULL; } EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(hmac); EVP_MAC_free(hmac); if (!ctx) { return NULL; } OSSL_PARAM params[] = {OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, (char *)digest_name, 0), OSSL_PARAM_construct_end()}; if (EVP_MAC_CTX_set_params(ctx, params)) { return ctx; } else { EVP_MAC_CTX_free(ctx); return NULL; } } /* _hmac_with_ctx_prototype computes an HMAC of @in using an OpenSSL context duplicated from @ctx_prototype. * @ctx_description is a human-readable description used when reporting deferred errors from initialization, required * if @ctx_prototype might be NULL. * @key is the input key. * @out is the output. @out must be allocated by the caller with * the exact length for the output. E.g. for HMAC 256, @out->len must be 32. * Returns false and sets @status on error. @status is required. */ static bool _hmac_with_ctx_prototype(const EVP_MAC_CTX *ctx_prototype, const char *ctx_description, const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); BSON_ASSERT(key->len <= INT_MAX); if (!ctx_prototype) { BSON_ASSERT_PARAM(ctx_description); CLIENT_ERR("failed to initialize algorithm %s", ctx_description); return false; } EVP_MAC_CTX *ctx = EVP_MAC_CTX_dup(ctx_prototype); if (ctx) { bool ok = EVP_MAC_init(ctx, key->data, key->len, NULL) && EVP_MAC_update(ctx, in->data, in->len) && EVP_MAC_final(ctx, out->data, NULL, out->len); EVP_MAC_CTX_free(ctx); if (ok) { return true; } } CLIENT_ERR("HMAC error: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } void _native_crypto_init(void) { // Early lookup of digest and cipher algorithms avoids both the lookup overhead itself and the overhead of lock // contention in the default OSSL_LIB_CTX. // // Failures now will store NULL, reporting a client error later. // // On HMAC fetching: // // Note that libcrypto sets an additional trap for us regarding MAC algorithms. An early fetch of the HMAC itself // won't actually pre-fetch the subalgorithm. The name of the inner digest gets stored as a string, and re-fetched // when setting up MAC context parameters. To fetch both the outer and inner algorithms ahead of time, we construct // a prototype EVP_MAC_CTX that can be duplicated before each use. // // On thread safety: // // This creates objects that are intended to be immutable shared data after initialization. To understand whether // this is safe we could consult the OpenSSL documentation but currently it's lacking in specifics about the // individual API functions and types. It offers some general guidelines: "Objects are thread-safe as long as the // API's being invoked don't modify the object; in this case the parameter is usually marked in the API as C. // Not all parameters are marked this way." By inspection, we can see that pre-fetched ciphers and MACs are designed // with atomic reference counting support and appear to be intended for safe immutable use. Contexts are normally // not safe to share, but these used only as a source for EVP_MAC_CTX_dup() can be treated as immutable. // // TODO: This could be refactored to live in mongocrypt_t rather than in global data. Currently there's no way to // avoid leaking this set of one-time allocations. // // TODO: Higher performance yet could be achieved by re-using thread local EVP_MAC_CTX, but this requires careful // lifecycle management to avoid leaking data. Alternatively, the libmongocrypt API could be modified to include // some non-shared but long-lived context suitable for keeping these crypto objects. Alternatively still, it may be // worth using a self contained SHA2 HMAC with favorable performance and portability characteristics. _mongocrypt_libcrypto.aes_256_cbc = EVP_CIPHER_fetch(NULL, "AES-256-CBC", NULL); _mongocrypt_libcrypto.aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL); _mongocrypt_libcrypto.aes_256_ecb = EVP_CIPHER_fetch(NULL, "AES-256-ECB", NULL); _mongocrypt_libcrypto.hmac_sha2_256 = _build_hmac_ctx_prototype(OSSL_DIGEST_NAME_SHA2_256); _mongocrypt_libcrypto.hmac_sha2_512 = _build_hmac_ctx_prototype(OSSL_DIGEST_NAME_SHA2_512); _native_crypto_initialized = true; } bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(_mongocrypt_libcrypto.aes_256_cbc, "AES-256-CBC", args); } bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) { return _decrypt_with_cipher(_mongocrypt_libcrypto.aes_256_cbc, "AES-256-CBC", args); } bool _native_crypto_aes_256_ecb_encrypt(aes_256_args_t args); // -Wmissing-prototypes: for testing only. bool _native_crypto_aes_256_ecb_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(_mongocrypt_libcrypto.aes_256_ecb, "AES-256-ECB", args); } bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(_mongocrypt_libcrypto.aes_256_ctr, "AES-256-CTR", args); } bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) { return _decrypt_with_cipher(_mongocrypt_libcrypto.aes_256_ctr, "AES-256-CTR", args); } bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_ctx_prototype(_mongocrypt_libcrypto.hmac_sha2_256, "HMAC-SHA2-256", key, in, out, status); } bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_ctx_prototype(_mongocrypt_libcrypto.hmac_sha2_512, "HMAC-SHA2-512", key, in, out, status); } #else /* OPENSSL_VERSION_NUMBER < 0x30000000L */ // Support for previous libcrypto versions, without early fetch optimization. #if OPENSSL_VERSION_NUMBER < 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) static HMAC_CTX *HMAC_CTX_new(void) { return bson_malloc0(sizeof(HMAC_CTX)); } static void HMAC_CTX_free(HMAC_CTX *ctx) { HMAC_CTX_cleanup(ctx); bson_free(ctx); } #endif void _native_crypto_init(void) { _native_crypto_initialized = true; } /* _hmac_with_hash computes an HMAC of @in with the OpenSSL hash specified by * @hash. * @key is the input key. * @out is the output. @out must be allocated by the caller with * the exact length for the output. E.g. for HMAC 256, @out->len must be 32. * Returns false and sets @status on error. @status is required. */ static bool _hmac_with_hash(const EVP_MD *hash, const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(hash); BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); BSON_ASSERT(key->len <= INT_MAX); #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (!HMAC(hash, key->data, (int)key->len, in->data, in->len, out->data, NULL /* unused out len */)) { CLIENT_ERR("error initializing HMAC: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } return true; #else HMAC_CTX *ctx; bool ret = false; ctx = HMAC_CTX_new(); if (out->len != (uint32_t)EVP_MD_size(hash)) { CLIENT_ERR("out does not contain %d bytes", EVP_MD_size(hash)); goto done; } if (!HMAC_Init_ex(ctx, key->data, (int)key->len, hash, NULL /* engine */)) { CLIENT_ERR("error initializing HMAC: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } if (!HMAC_Update(ctx, in->data, in->len)) { CLIENT_ERR("error updating HMAC: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } if (!HMAC_Final(ctx, out->data, NULL /* unused out len */)) { CLIENT_ERR("error finalizing: %s", ERR_error_string(ERR_get_error(), NULL)); goto done; } ret = true; done: HMAC_CTX_free(ctx); return ret; #endif } bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(EVP_aes_256_cbc(), NULL, args); } bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) { return _decrypt_with_cipher(EVP_aes_256_cbc(), NULL, args); } bool _native_crypto_aes_256_ecb_encrypt(aes_256_args_t args); // -Wmissing-prototypes: for testing only. bool _native_crypto_aes_256_ecb_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(EVP_aes_256_ecb(), NULL, args); } bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) { return _encrypt_with_cipher(EVP_aes_256_ctr(), NULL, args); } bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) { return _decrypt_with_cipher(EVP_aes_256_ctr(), NULL, args); } bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_hash(EVP_sha256(), key, in, out, status); } bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _hmac_with_hash(EVP_sha512(), key, in, out, status); } #endif /* OPENSSL_VERSION_NUMBER */ #endif /* MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO */ libmongocrypt-1.19.0/src/crypto/none.c000066400000000000000000000047141521103432300177040ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* everything is a no-op */ #include "../mongocrypt-crypto-private.h" #include "../mongocrypt-private.h" #ifndef MONGOCRYPT_ENABLE_CRYPTO bool _native_crypto_initialized = false; void _native_crypto_init(void) { _native_crypto_initialized = true; } bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) { mongocrypt_status_t *status = args.status; CLIENT_ERR("hook not set for aes_256_cbc_encrypt"); return false; } bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) { mongocrypt_status_t *status = args.status; CLIENT_ERR("hook not set for aes_256_cbc_decrypt"); return false; } bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { CLIENT_ERR("hook not set for hmac_sha_512"); return false; } bool _native_crypto_random(_mongocrypt_buffer_t *out, uint32_t count, mongocrypt_status_t *status) { CLIENT_ERR("hook not set for random"); return false; } bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) { mongocrypt_status_t *status = args.status; CLIENT_ERR("hook not set for _native_crypto_aes_256_ctr_encrypt"); return false; } bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) { mongocrypt_status_t *status = args.status; CLIENT_ERR("hook not set for _native_crypto_aes_256_ctr_decrypt"); return false; } bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key, const _mongocrypt_buffer_t *in, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { CLIENT_ERR("hook not set for _native_crypto_hmac_sha_256"); return false; } #endif /* MONGOCRYPT_ENABLE_CRYPTO */ libmongocrypt-1.19.0/src/csfle-markup.cpp000066400000000000000000000165061521103432300203600ustar00rootroot00000000000000#ifdef NDEBUG #undef NDEBUG #endif #include extern "C" { #include "./mongocrypt-dll-private.h" } #include #include #include #include #include #include namespace { void print_usage() { std::cout << "Usage:\n" << " csfle-markup [doc-namespace]\n" << "\n" << "Write a JSON document to stdin, and the resulting marked-up\n" << "document will be written to stdout.\n\n" << "Hint: Pipe through \"jq .\" for prettier output.\n"; } template struct deferred_fn { Fn fn; ~deferred_fn() { fn(); } }; template deferred_fn make_deferred(F fn) { return deferred_fn{fn}; } #define DEFER(...) make_deferred([&] __VA_ARGS__) void bson_child(bson_t *out, bson_iter_t *it) { uint32_t len; const uint8_t *data; if (BSON_ITER_HOLDS_DOCUMENT(it)) { bson_iter_document(it, &len, &data); } if (BSON_ITER_HOLDS_ARRAY(it)) { bson_iter_array(it, &len, &data); } bool okay = bson_init_static(out, data, len); assert(okay); } void print_field_info(bson_t *bson, std::string path) { bson_iter_t iter; for (bson_iter_init(&iter, bson); bson_iter_next(&iter);) { auto key = bson_iter_key(&iter); auto child_path = path + "/" + key; if (BSON_ITER_HOLDS_ARRAY(&iter) || BSON_ITER_HOLDS_DOCUMENT(&iter)) { bson_t child; bson_child(&child, &iter); print_field_info(&child, child_path); } if (BSON_ITER_HOLDS_BINARY(&iter)) { bson_subtype_t subtype; uint32_t len; const uint8_t *data; bson_iter_binary(&iter, &subtype, &len, &data); if (subtype == BSON_SUBTYPE_ENCRYPTED && len) { bson_t encoded; uint8_t subsubtype = data[0]; if (subsubtype == 0) { // Intent-to-encrypt BSON_ASSERT(len > 0); bson_init_static(&encoded, data + 1, len - 1); auto s = bson_as_canonical_extended_json(&encoded, nullptr); fprintf(stderr, "BSON binary at [%s] has encoded intent-to-encrypt " "content: %s\n", child_path.data(), s); bson_free(s); } } } } } int do_main(int argc, const char *const *argv) { if (argc < 2 || argc > 3) { print_usage(); return 2; } if (argv[1] == std::string("--help")) { print_usage(); return 0; } const auto csfle_path = argv[1]; const auto doc_ns = argc > 2 ? argv[2] : ""; mcr_dll csfle = mcr_dll_open(argv[1]); auto close_csfle = DEFER({ mcr_dll_close(csfle); }); if (csfle.error_string.raw.data) { std::cerr << "Failed to open [" << argv[1] << "] as a dynamic library: " << csfle.error_string.raw.data << '\n'; return 3; } #define LOAD_SYM(Name) \ MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE \ auto Name = reinterpret_cast(mcr_dll_sym(csfle, #Name)); \ MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE \ if (!Name) { \ fprintf(stderr, "Failed to load required symbol [%s] from the given csfle library", #Name); \ return 4; \ } \ static_assert(true, "") LOAD_SYM(mongo_crypt_v1_status_create); LOAD_SYM(mongo_crypt_v1_status_destroy); LOAD_SYM(mongo_crypt_v1_status_get_explanation); LOAD_SYM(mongo_crypt_v1_lib_create); LOAD_SYM(mongo_crypt_v1_lib_destroy); LOAD_SYM(mongo_crypt_v1_get_version_str); LOAD_SYM(mongo_crypt_v1_query_analyzer_create); LOAD_SYM(mongo_crypt_v1_query_analyzer_destroy); LOAD_SYM(mongo_crypt_v1_analyze_query); LOAD_SYM(mongo_crypt_v1_bson_free); fprintf(stderr, "Loaded csfle library [%s]: %s\n", csfle_path, mongo_crypt_v1_get_version_str()); // Read from stdin std::stringstream strm; strm << std::cin.rdbuf(); std::string input_json = strm.str(); bson_error_t error; bson_t *input = bson_new_from_json(reinterpret_cast(input_json.c_str()), static_cast(input_json.size()), &error); if (!input) { std::cerr << "Failed to read JSON data: " << error.message << '\n'; return 5; } auto del_bson = DEFER({ bson_destroy(input); }); bson_iter_t find_db; if (!bson_iter_init_find(&find_db, input, "$db")) { fputs("Note: Added a {\"$db\": \"unspecified\"} field to the document " "root. This field is required for csfle.\n", stderr); BSON_APPEND_UTF8(input, "$db", "unspecified"); } auto status = mongo_crypt_v1_status_create(); auto del_status = DEFER({ mongo_crypt_v1_status_destroy(status); }); auto lib = mongo_crypt_v1_lib_create(status); if (!lib) { fprintf(stderr, "Failed to lib_create for csfle: %s\n", mongo_crypt_v1_status_get_explanation(status)); return 6; } auto del_lib = DEFER({ mongo_crypt_v1_lib_destroy(lib, nullptr); }); auto qa = mongo_crypt_v1_query_analyzer_create(lib, status); if (!qa) { fprintf(stderr, "Failed to create a query analyzer for csfle: %s\n", mongo_crypt_v1_status_get_explanation(status)); return 7; } auto del_qa = DEFER({ mongo_crypt_v1_query_analyzer_destroy(qa); }); uint32_t data_len = input->len; uint8_t *data_ptr = mongo_crypt_v1_analyze_query(qa, bson_get_data(input), doc_ns, static_cast(strlen(doc_ns)), &data_len, status); if (!data_ptr) { fprintf(stderr, "Failed to analyze the given query: %s\n", mongo_crypt_v1_status_get_explanation(status)); return 8; } auto del_data = DEFER({ mongo_crypt_v1_bson_free(data_ptr); }); bson_t *output = bson_new_from_data(data_ptr, data_len); assert(output); auto del_output = DEFER({ bson_destroy(output); }); auto s = bson_as_canonical_extended_json(output, nullptr); fputs(s, stdout); bson_free(s); print_field_info(output, ""); return 0; } } // namespace int main(int argc, const char **argv) { try { return do_main(argc, argv); } catch (const std::exception &e) { std::cerr << "Unhandled exception: " << e.what() << '\n'; return 2; } } libmongocrypt-1.19.0/src/mc-array-private.h000066400000000000000000000026031521103432300206100ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_ARRAY_PRIVATE_H #define MC_ARRAY_PRIVATE_H /* This file is copied from mongoc-array-private.h in libmongoc version 1.17.7 * at commit 200a01bb208633fe3cf395d81acc1e19492d9de4 */ #include typedef struct _mc_array_t mc_array_t; struct _mc_array_t { size_t len; size_t element_size; size_t allocated; void *data; }; #define _mc_array_append_val(a, v) _mc_array_append_vals(a, &v, 1) #define _mc_array_index(a, t, i) (((t *)(a)->data)[i]) #define _mc_array_clear(a) (a)->len = 0 void _mc_array_init(mc_array_t *array, size_t element_size); void _mc_array_copy(mc_array_t *dst, const mc_array_t *src); void _mc_array_append_vals(mc_array_t *array, const void *data, uint32_t n_elements); void _mc_array_destroy(mc_array_t *array); #endif /* MC_ARRAY_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-array.c000066400000000000000000000051441521103432300171360ustar00rootroot00000000000000/* * Copyright 2022-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. */ /* This file is copied from mongoc-array.c in libmongoc version 1.17.7 at commit * 200a01bb208633fe3cf395d81acc1e19492d9de4 */ #include "mc-array-private.h" void _mc_array_init(mc_array_t *array, size_t element_size) { BSON_ASSERT_PARAM(array); BSON_ASSERT(element_size); array->len = 0; array->element_size = element_size; array->allocated = 128; array->data = (void *)bson_malloc0(array->allocated); } /* *-------------------------------------------------------------------------- * * _mc_array_copy -- * * Destroy dst and copy src into it. Both arrays must be initialized. * * Returns: * None. * * Side effects: * None. * *-------------------------------------------------------------------------- */ void _mc_array_copy(mc_array_t *dst, const mc_array_t *src) { BSON_ASSERT_PARAM(dst); BSON_ASSERT_PARAM(src); _mc_array_destroy(dst); dst->len = src->len; dst->element_size = src->element_size; dst->allocated = src->allocated; dst->data = (void *)bson_malloc(dst->allocated); memcpy(dst->data, src->data, dst->allocated); } void _mc_array_destroy(mc_array_t *array) { if (array && array->data) { bson_free(array->data); } } void _mc_array_append_vals(mc_array_t *array, const void *data, uint32_t n_elements) { size_t len; size_t off; size_t next_size; BSON_ASSERT_PARAM(array); BSON_ASSERT_PARAM(data); BSON_ASSERT(array->len <= SIZE_MAX / array->element_size); off = array->element_size * array->len; BSON_ASSERT(n_elements <= SIZE_MAX / array->element_size); len = (size_t)n_elements * array->element_size; BSON_ASSERT(len <= SIZE_MAX - off); if ((off + len) > array->allocated) { next_size = bson_next_power_of_two(off + len); array->data = (void *)bson_realloc(array->data, next_size); array->allocated = next_size; } memcpy((uint8_t *)array->data + off, data, len); BSON_ASSERT(array->len <= SIZE_MAX - n_elements); array->len += n_elements; } libmongocrypt-1.19.0/src/mc-cmp-private.h000066400000000000000000000160671521103432300202620ustar00rootroot00000000000000/* * Copyright 2018-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. */ // `cmp.h` is a modified copy of `bson-cmp.h` from libbson 1.28.0. #ifndef MC_CMP_H #define MC_CMP_H #include /* ssize_t + BSON_CONCAT */ #include #include #include /* Sanity check: ensure ssize_t limits are as expected relative to size_t. */ BSON_STATIC_ASSERT2(ssize_t_size_min_check, SSIZE_MIN + 1 == -SSIZE_MAX); BSON_STATIC_ASSERT2(ssize_t_size_max_check, (size_t)SSIZE_MAX <= SIZE_MAX); /* Based on the "Safe Integral Comparisons" proposal merged in C++20: * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0586r2.html * * Due to lack of type deduction in C, relational comparison functions (e.g. * `cmp_less`) are defined in sets of four "functions" according to the * signedness of each value argument, e.g.: * - mc_cmp_less_ss (signed-value, signed-value) * - mc_cmp_less_uu (unsigned-value, unsigned-value) * - mc_cmp_less_su (signed-value, unsigned-value) * - mc_cmp_less_us (unsigned-value, signed-value) * * Similarly, the `in_range` function is defined as a set of two "functions" * according to the signedness of the value argument: * - mc_in_range_signed (Type, signed-value) * - mc_in_range_unsigned (Type, unsigned-value) * * The user must take care to use the correct signedness for the provided * argument(s). Enabling compiler warnings for implicit sign conversions is * recommended. */ #define MC_CMP_SET(op, ss, uu, su, us) \ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _ss)(int64_t t, int64_t u) { return (ss); } \ \ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _uu)(uint64_t t, uint64_t u) { return (uu); } \ \ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _su)(int64_t t, uint64_t u) { return (su); } \ \ static BSON_INLINE bool BSON_CONCAT3(mc_cmp_, op, _us)(uint64_t t, int64_t u) { return (us); } MC_CMP_SET(equal, t == u, t == u, t < 0 ? false : (uint64_t)(t) == u, u < 0 ? false : t == (uint64_t)(u)) MC_CMP_SET(not_equal, !mc_cmp_equal_ss(t, u), !mc_cmp_equal_uu(t, u), !mc_cmp_equal_su(t, u), !mc_cmp_equal_us(t, u)) MC_CMP_SET(less, t < u, t < u, t < 0 ? true : (uint64_t)(t) < u, u < 0 ? false : t < (uint64_t)(u)) MC_CMP_SET(greater, mc_cmp_less_ss(u, t), mc_cmp_less_uu(u, t), mc_cmp_less_us(u, t), mc_cmp_less_su(u, t)) MC_CMP_SET(less_equal, !mc_cmp_greater_ss(t, u), !mc_cmp_greater_uu(t, u), !mc_cmp_greater_su(t, u), !mc_cmp_greater_us(t, u)) MC_CMP_SET(greater_equal, !mc_cmp_less_ss(t, u), !mc_cmp_less_uu(t, u), !mc_cmp_less_su(t, u), !mc_cmp_less_us(t, u)) #undef MC_CMP_SET /* Return true if the given value is within the range of the corresponding * signed type. The suffix must match the signedness of the given value. */ #define MC_IN_RANGE_SET_SIGNED(Type, min, max) \ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _signed)(int64_t value) { \ return mc_cmp_greater_equal_ss(value, min) && mc_cmp_less_equal_ss(value, max); \ } \ \ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(uint64_t value) { \ return mc_cmp_greater_equal_us(value, min) && mc_cmp_less_equal_us(value, max); \ } /* Return true if the given value is within the range of the corresponding * unsigned type. The suffix must match the signedness of the given value. */ #define MC_IN_RANGE_SET_UNSIGNED(Type, max) \ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _signed)(int64_t value) { \ return mc_cmp_greater_equal_su(value, 0u) && mc_cmp_less_equal_su(value, max); \ } \ \ static BSON_INLINE bool BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(uint64_t value) { \ return mc_cmp_less_equal_uu(value, max); \ } MC_IN_RANGE_SET_SIGNED(signed_char, SCHAR_MIN, SCHAR_MAX) MC_IN_RANGE_SET_SIGNED(short, SHRT_MIN, SHRT_MAX) MC_IN_RANGE_SET_SIGNED(int, INT_MIN, INT_MAX) MC_IN_RANGE_SET_SIGNED(long, LONG_MIN, LONG_MAX) MC_IN_RANGE_SET_SIGNED(long_long, LLONG_MIN, LLONG_MAX) MC_IN_RANGE_SET_UNSIGNED(unsigned_char, UCHAR_MAX) MC_IN_RANGE_SET_UNSIGNED(unsigned_short, USHRT_MAX) MC_IN_RANGE_SET_UNSIGNED(unsigned_int, UINT_MAX) MC_IN_RANGE_SET_UNSIGNED(unsigned_long, ULONG_MAX) MC_IN_RANGE_SET_UNSIGNED(unsigned_long_long, ULLONG_MAX) MC_IN_RANGE_SET_SIGNED(int8_t, INT8_MIN, INT8_MAX) MC_IN_RANGE_SET_SIGNED(int16_t, INT16_MIN, INT16_MAX) MC_IN_RANGE_SET_SIGNED(int32_t, INT32_MIN, INT32_MAX) MC_IN_RANGE_SET_SIGNED(int64_t, INT64_MIN, INT64_MAX) MC_IN_RANGE_SET_UNSIGNED(uint8_t, UINT8_MAX) MC_IN_RANGE_SET_UNSIGNED(uint16_t, UINT16_MAX) MC_IN_RANGE_SET_UNSIGNED(uint32_t, UINT32_MAX) MC_IN_RANGE_SET_UNSIGNED(uint64_t, UINT64_MAX) MC_IN_RANGE_SET_SIGNED(ssize_t, SSIZE_MIN, SSIZE_MAX) MC_IN_RANGE_SET_UNSIGNED(size_t, SIZE_MAX) #undef MC_IN_RANGE_SET_SIGNED #undef MC_IN_RANGE_SET_UNSIGNED /* Return true if the value with *signed* type is in the representable range of * Type and false otherwise. */ #define mc_in_range_signed(Type, value) BSON_CONCAT3(mc_in_range, _##Type, _signed)(value) /* Return true if the value with *unsigned* type is in the representable range * of Type and false otherwise. */ #define mc_in_range_unsigned(Type, value) BSON_CONCAT3(mc_in_range, _##Type, _unsigned)(value) #endif /* MC_CMP_H */ libmongocrypt-1.19.0/src/mc-dec128.h000066400000000000000000000573731521103432300170260ustar00rootroot00000000000000#ifndef MC_DEC128_H_INCLUDED #define MC_DEC128_H_INCLUDED #include #include #include #include // Conditional preprocessor definition set by the usage of an intel_dfp from // the ImportDFP.cmake script: #ifndef MONGOCRYPT_INTELDFP // Notify includers that Decimal128 is not available: #define MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() false #else // With IntelDFP: // Tell includers that Decimal128 is okay: #define MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() true // Include the header that declares the DFP functions, which may be macros that // expand to renamed symbols: #include #include #include #include #include #include MLIB_C_LINKAGE_BEGIN /// Rounding controls for Decimal128 operations typedef enum mc_dec128_rounding_mode { MC_DEC128_ROUND_NEAREST_EVEN = 0, MC_DEC128_ROUND_DOWNWARD = 1, MC_DEC128_ROUND_UPWARD = 2, MC_DEC128_ROUND_TOWARD_ZERO = 3, MC_DEC128_ROUND_NEAREST_AWAY = 4, MC_DEC128_ROUND_DEFAULT = MC_DEC128_ROUND_NEAREST_EVEN, } mc_dec128_rounding_mode; typedef struct mc_dec128_flagset { _IDEC_flags bits; } mc_dec128_flagset; // This alignment conditional is the same conditions used in Intel's DFP // library, ensuring we match the ABI of the library without pulling the header #if defined _MSC_VER #if defined _M_IX86 && !defined __INTEL_COMPILER #define _mcDec128Align(n) #else #define _mcDec128Align(n) __declspec(align(n)) #endif #else #if !defined HPUX_OS #define _mcDec128Align(n) __attribute__((aligned(n))) #else #define _mcDec128Align(n) #endif #endif typedef union _mcDec128Align(16) { uint64_t _words[2]; #if !defined(__INTELLISENSE__) && defined(__GNUC__) && defined(__amd64) && !defined(__APPLE__) && !defined(__clang__) // If supported by the compiler, emit a field that can be used to visualize // the value in a debugger. float value_ __attribute__((mode(TD))); #endif } mc_dec128; #undef _mcDec128Align /// Expands to a dec128 constant value. #ifdef __cplusplus #define MC_DEC128_C(N) mc_dec128 _mcDec128Const(((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0)) #else #define MC_DEC128_C(N) _mcDec128Const(((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0)) #endif #define MC_DEC128(N) MLIB_INIT(mc_dec128) MC_DEC128_C(N) #define _mcDec128Combination(Bits) ((uint64_t)(Bits) << (47)) #define _mcDec128ZeroExpCombo _mcDec128Combination(1 << 7 | 1 << 13 | 1 << 14) #define _mcDec128Const(N, Negate) _mcDec128ConstFromParts(N, (_mcDec128ZeroExpCombo | ((uint64_t)(Negate) << 63))) #define _mcDec128ConstFromParts(CoeffLow, HighWord) \ { \ { \ MLIB_IS_LITTLE_ENDIAN ? (uint64_t)(CoeffLow) : (uint64_t)(HighWord), \ MLIB_IS_LITTLE_ENDIAN ? (uint64_t)(HighWord) : (uint64_t)(CoeffLow), \ }, \ } static const mc_dec128 MC_DEC128_ZERO = MC_DEC128_C(0); static const mc_dec128 MC_DEC128_ONE = MC_DEC128_C(1); static const mc_dec128 MC_DEC128_MINUSONE = MC_DEC128_C(-1); /// The greatest-magnitude finite negative value representable in a Decimal128 #define MC_DEC128_LARGEST_NEGATIVE mc_dec128_from_string("-9999999999999999999999999999999999E6111") /// The least-magnitude non-zero negative value representable in a Decimal128 #define MC_DEC128_SMALLEST_NEGATIVE mc_dec128_from_string("-1E-6176") /// The greatest-magnitude finite positive value representable in a Decimal128 #define MC_DEC128_LARGEST_POSITIVE mc_dec128_from_string("9999999999999999999999999999999999E6111") /// The least-magnitude non-zero positive value representable in a Decimal128 #define MC_DEC128_SMALLEST_POSITIVE mc_dec128_from_string("1E-6176") /// The normalized zero of Decimal128 #define MC_DEC128_NORMALIZED_ZERO MC_DEC128_C(0) /// A zero of Decimal128 with the least exponent #define MC_DEC128_NEGATIVE_EXPONENT_ZERO mc_dec128_from_string("0E-6176") #define _mcDec128InfCombo _mcDec128Combination(1 << 15 | 1 << 14 | 1 << 13 | 1 << 12) #define _mcDec128QuietNaNCombo _mcDec128Combination(1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | 1 << 11) /// Positive infinity of Decimal128 #define MC_DEC128_POSITIVE_INFINITY _mcDec128ConstFromParts(0, _mcDec128InfCombo) /// Negative infinity of Decimal128 #define MC_DEC128_NEGATIVE_INFINITY _mcDec128ConstFromParts(0, _mcDec128InfCombo | 1ull << 63) /// Positve quiet NaN of Decimal128 #define MC_DEC128_POSITIVE_NAN _mcDec128ConstFromParts(0, _mcDec128QuietNaNCombo) /// Negative quiet NaN of Decimal128 #define MC_DEC128_NEGATIVE_NAN _mcDec128ConstFromParts(0, _mcDec128QuietNaNCombo | 1ull << 63) /// Convert an mc_dec128 value to a DFP 128-bit object static inline BID_UINT128 _mc_to_bid128(mc_dec128 d) { BID_UINT128 r; memcpy(&r, &d, sizeof d); return r; } /// Convert a DFP 128-bit object to a mc_dec128 value static inline mc_dec128 _bid128_to_mc(BID_UINT128 d) { mc_dec128 r; memcpy(&r, &d, sizeof d); return r; } /** * @brief Convert a double-precision binary floating point value into the * nearest Decimal128 value * * @param d The number to conver * @param rnd The rounding mode in case the value is not exactly representable * @param flags Out param for exception/error flags (Optional) */ static inline mc_dec128 mc_dec128_from_double_ex(double d, mc_dec128_rounding_mode rnd, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; return _bid128_to_mc(binary64_to_bid128(d, rnd, flags ? &flags->bits : &zero_flags.bits)); } /** * @brief Convert a double-precision binary floating point value into the * nearest Decimal128 value */ static inline mc_dec128 mc_dec128_from_double(double d) { return mc_dec128_from_double_ex(d, MC_DEC128_ROUND_DEFAULT, NULL); } /** * @brief Convert a string representation of a number into the nearest * Decimal128 value * * @param s The string to parse. MUST be null-terminated * @param rnd The rounding mode to use if the result is not representable * @param flags Out param for exception/error flags (Optional) */ static inline mc_dec128 mc_dec128_from_string_ex(const char *s, mc_dec128_rounding_mode rnd, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; return _bid128_to_mc(bid128_from_string((char *)s, rnd, flags ? &flags->bits : &zero_flags.bits)); } /** * @brief Convert a string representation of a number into the nearest * Decimal128 value */ static inline mc_dec128 mc_dec128_from_string(const char *s) { return mc_dec128_from_string_ex(s, MC_DEC128_ROUND_DEFAULT, NULL); } /** * @brief A type capable of holding a string rendering of a Decimal128 in * engineering notation. */ typedef struct mc_dec128_string { /// The character array of the rendered value. Null-terminated char str[48]; } mc_dec128_string; /** * @brief Render a Decimal128 value as a string (in engineering notation) * * @param d The number to represent * @param flags Output parameter for exception/error flags (optional) */ static inline mc_dec128_string mc_dec128_to_string_ex(mc_dec128 d, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; mc_dec128_string out = {{0}}; bid128_to_string(out.str, _mc_to_bid128(d), flags ? &flags->bits : &zero_flags.bits); return out; } /** * @brief Render a Decimal128 value as a string (in engineering notation) */ static inline mc_dec128_string mc_dec128_to_string(mc_dec128 d) { return mc_dec128_to_string_ex(d, NULL); } /// Compare two dec128 numbers #define DECL_IDF_COMPARE_1(Oper) \ static inline bool mc_dec128_##Oper##_ex(mc_dec128 left, mc_dec128 right, mc_dec128_flagset *flags) { \ mc_dec128_flagset zero_flags = {0}; \ return 0 \ != bid128_quiet_##Oper(_mc_to_bid128(left), \ _mc_to_bid128(right), \ flags ? &flags->bits : &zero_flags.bits); \ } \ \ static inline bool mc_dec128_##Oper(mc_dec128 left, mc_dec128 right) { \ return mc_dec128_##Oper##_ex(left, right, NULL); \ } #define DECL_IDF_COMPARE(Op) DECL_IDF_COMPARE_1(Op) DECL_IDF_COMPARE(equal) DECL_IDF_COMPARE(not_equal) DECL_IDF_COMPARE(greater) DECL_IDF_COMPARE(greater_equal) DECL_IDF_COMPARE(less) DECL_IDF_COMPARE(less_equal) #undef DECL_IDF_COMPARE #undef DECL_IDF_COMPARE_1 /// Test properties of Decimal128 numbers #define DECL_PREDICATE(Name, BIDName) \ static inline bool mc_dec128_##Name(mc_dec128 d) { return 0 != bid128_##BIDName(_mc_to_bid128(d)); } DECL_PREDICATE(is_zero, isZero) DECL_PREDICATE(is_negative, isSigned) DECL_PREDICATE(is_inf, isInf) DECL_PREDICATE(is_finite, isFinite) DECL_PREDICATE(is_nan, isNaN) #undef DECL_PREDICATE /// Binary arithmetic operations on two Decimal128 numbers #define DECL_IDF_BINOP_WRAPPER(Oper) \ static inline mc_dec128 mc_dec128_##Oper##_ex(mc_dec128 left, \ mc_dec128 right, \ mc_dec128_rounding_mode mode, \ mc_dec128_flagset *flags) { \ mc_dec128_flagset zero_flags = {0}; \ return _bid128_to_mc( \ bid128_##Oper(_mc_to_bid128(left), _mc_to_bid128(right), mode, flags ? &flags->bits : &zero_flags.bits)); \ } \ \ static inline mc_dec128 mc_dec128_##Oper(mc_dec128 left, mc_dec128 right) { \ return mc_dec128_##Oper##_ex(left, right, MC_DEC128_ROUND_DEFAULT, NULL); \ } DECL_IDF_BINOP_WRAPPER(add) DECL_IDF_BINOP_WRAPPER(mul) DECL_IDF_BINOP_WRAPPER(div) DECL_IDF_BINOP_WRAPPER(sub) DECL_IDF_BINOP_WRAPPER(pow) #undef DECL_IDF_BINOP_WRAPPER /// Unary arithmetic operations on two Decimal128 numbers #define DECL_IDF_UNOP_WRAPPER(Oper) \ static inline mc_dec128 mc_dec128_##Oper##_ex(mc_dec128 operand, mc_dec128_flagset *flags) { \ mc_dec128_flagset zero_flags = {0}; \ return _bid128_to_mc( \ bid128_##Oper(_mc_to_bid128(operand), MC_DEC128_ROUND_DEFAULT, flags ? &flags->bits : &zero_flags.bits)); \ } \ \ static inline mc_dec128 mc_dec128_##Oper(mc_dec128 operand) { return mc_dec128_##Oper##_ex(operand, NULL); } DECL_IDF_UNOP_WRAPPER(log2) DECL_IDF_UNOP_WRAPPER(log10) #undef DECL_IDF_UNOP_WRAPPER static inline mc_dec128 mc_dec128_round_integral_ex(mc_dec128 value, mc_dec128_rounding_mode direction, mc_dec128_flagset *flags) { BID_UINT128 bid = _mc_to_bid128(value); mc_dec128_flagset zero_flags = {0}; _IDEC_flags *fl = flags ? &flags->bits : &zero_flags.bits; switch (direction) { case MC_DEC128_ROUND_TOWARD_ZERO: return _bid128_to_mc(bid128_round_integral_zero(bid, fl)); case MC_DEC128_ROUND_NEAREST_AWAY: return _bid128_to_mc(bid128_round_integral_nearest_away(bid, fl)); case MC_DEC128_ROUND_NEAREST_EVEN: return _bid128_to_mc(bid128_round_integral_nearest_even(bid, fl)); case MC_DEC128_ROUND_DOWNWARD: return _bid128_to_mc(bid128_round_integral_negative(bid, fl)); case MC_DEC128_ROUND_UPWARD: return _bid128_to_mc(bid128_round_integral_positive(bid, fl)); default: abort(); } } static inline mc_dec128 mc_dec128_negate(mc_dec128 operand) { return _bid128_to_mc(bid128_negate(_mc_to_bid128(operand))); } static inline mc_dec128 mc_dec128_abs(mc_dec128 operand) { return _bid128_to_mc(bid128_abs(_mc_to_bid128(operand))); } /** * @brief Scale the given Decimal128 by a power of ten * * @param fac The value being scaled * @param exp The exponent: 10^exp is the scale factor * @param rounding Rounding behavior * @param flags Control/result flags * @return mc_dec128 The product */ static inline mc_dec128 mc_dec128_scale_ex(mc_dec128 fac, long int exp, mc_dec128_rounding_mode rounding, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; return _bid128_to_mc(bid128_scalbln(_mc_to_bid128(fac), exp, rounding, flags ? &flags->bits : &zero_flags.bits)); } /** * @brief Scale the given Decimal128 by a power of ten * * @param fac The value being scaled * @param exp The exponent: 10^exp is the scale factor * @return mc_dec128 The product */ static inline mc_dec128 mc_dec128_scale(mc_dec128 fac, long int exp) { return mc_dec128_scale_ex(fac, exp, MC_DEC128_ROUND_DEFAULT, NULL); } /// The result of a dec_128 modf operation typedef struct mc_dec128_modf_result { /// The whole part of the result mc_dec128 whole; /// The fractional part of the result mc_dec128 frac; } mc_dec128_modf_result; /** * @brief Split a Decimal128 into its whole and fractional parts. * * The "whole" value is the integral value of the Decimal128 rounded towards * zero. The "frac" part is the remainder after removing the whole. * * @param d The value to inspect * @param flags Results status flags */ static inline mc_dec128_modf_result mc_dec128_modf_ex(mc_dec128 d, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; mc_dec128_modf_result res; BID_UINT128 whole; res.frac = _bid128_to_mc(bid128_modf(_mc_to_bid128(d), &whole, flags ? &flags->bits : &zero_flags.bits)); res.whole = _bid128_to_mc(whole); return res; } /** * @brief Split a Decimal128 into its whole and fractional parts. * * The "whole" value is the integral value of the Decimal128 rounded towards * zero. The "frac" part is the remainder after removing the whole. * * @param d The value to inspect */ static inline mc_dec128_modf_result mc_dec128_modf(mc_dec128 d) { return mc_dec128_modf_ex(d, NULL); } /** * @brief Compute the "fmod", the remainder after decimal division rounding * towards zero. * * @param numer The dividend of the modulus * @param denom The divisor of the modulus * @param flags Control/status flags */ static inline mc_dec128 mc_dec128_fmod_ex(mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; return _bid128_to_mc( bid128_fmod(_mc_to_bid128(numer), _mc_to_bid128(denom), flags ? &flags->bits : &zero_flags.bits)); } /** * @brief Compute the "fmod", the remainder after decimal division rounding * towards zero. * * @param numer The dividend of the modulus * @param denom The divisor of the modulus */ static inline mc_dec128 mc_dec128_fmod(mc_dec128 numer, mc_dec128 denom) { return mc_dec128_fmod_ex(numer, denom, NULL); } /** * @brief Obtain the a 64-bit binary integer value for the given Decimal128 * value, nearest rounding toward zero. * * @param d The value to inspect * @param flags Control/status flags */ static inline int64_t mc_dec128_to_int64_ex(mc_dec128 d, mc_dec128_flagset *flags) { mc_dec128_flagset zero_flags = {0}; return bid128_to_int64_int(_mc_to_bid128(d), flags ? &flags->bits : &zero_flags.bits); } /** * @brief Obtain the a 64-bit binary integer value for the given Decimal128 * value, nearest rounding toward zero. * * @param d The value to inspect */ static inline int64_t mc_dec128_to_int64(mc_dec128 d) { return mc_dec128_to_int64_ex(d, NULL); } /// Constants for inspection/deconstruction of Decimal128 enum { /// Least non-canonical combination bits value MC_DEC128_COMBO_NONCANONICAL = 3 << 15, /// Least combination value indicating infinity MC_DEC128_COMBO_INFINITY = 0x1e << 12, /// The greatest Decimal128 biased exponent MC_DEC128_MAX_BIASED_EXPONENT = 6143 + 6144, /// The exponent bias of Decimal128 MC_DEC128_EXPONENT_BIAS = 6143 + 33, // +33 to include the 34 decimal digits /// Minimum exponent value (without bias) MC_DEC_MIN_EXPONENT = -6143, /// Maximum exponent value (without bias) MC_DEC_MAX_EXPONENT = 6144, }; /// Obtain the value of the combination bits of a decimal128 static inline uint32_t mc_dec128_combination(mc_dec128 d) { // Grab the high 64 bits: uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0]; // Sign is the 64th bit: int signpos = 64 - 1; // Combo is the next 16 bits: int fieldpos = signpos - 17; int fieldmask = (1 << 17) - 1; return (uint32_t)((hi >> fieldpos) & (uint32_t)fieldmask); } /** * @brief Obtain the value of the high 49 bits of the Decimal128 coefficient */ static inline uint64_t mc_dec128_coeff_high(mc_dec128 d) { uint64_t hi_field_mask = (1ull << 49) - 1; uint32_t combo = mc_dec128_combination(d); if (combo < MC_DEC128_COMBO_NONCANONICAL) { uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0]; return hi & hi_field_mask; } else { return 0; } } /** * @brief Obtain the value of the low 64 bits of the Decimal128 coefficient */ static inline uint64_t mc_dec128_coeff_low(mc_dec128 d) { uint32_t combo = mc_dec128_combination(d); if (combo < MC_DEC128_COMBO_NONCANONICAL) { uint64_t lo = d._words[MLIB_IS_LITTLE_ENDIAN ? 0 : 1]; return lo; } else { return 0; } } /** * @brief Obtain the full coefficient of the given Decimal128 number. Requires a * 128-bit integer. */ static inline mlib_int128 mc_dec128_coeff(mc_dec128 d) { // Hi bits uint64_t hi = mc_dec128_coeff_high(d); // Lo bits uint64_t lo = mc_dec128_coeff_low(d); // Shift and add mlib_int128 hi_128 = mlib_int128_lshift(MLIB_INT128_CAST(hi), 64); return mlib_int128_add(hi_128, MLIB_INT128_CAST(lo)); } /** * @brief Obtain the biased value of the Decimal128 exponent. * * The value is "biased" in that its binary value not actually zero for 10^0. It * is offset by half the exponent range (the "bias") so it can encode the full * positive and negative exponent range. The bias value is defined as * MC_DEC128_EXPONENT_BIAS. */ static inline uint32_t mc_dec128_get_biased_exp(mc_dec128 d) { uint32_t combo = mc_dec128_combination(d); if (combo < MC_DEC128_COMBO_NONCANONICAL) { return combo >> 3; } if (combo >= MC_DEC128_COMBO_INFINITY) { return MC_DEC128_MAX_BIASED_EXPONENT + 1; } else { return (combo >> 1) & ((1 << 14) - 1); } } /// Create a decimal string from a dec128 number. The result must be freed. static inline char *mc_dec128_to_new_decimal_string(mc_dec128 d) { if (mc_dec128_is_zero(d)) { // Just return "0" char *s = (char *)calloc(2, 1); if (s) { s[0] = '0'; } return s; } if (mc_dec128_is_negative(d)) { // Negate the result, return a string with a '-' prefix d = mc_dec128_negate(d); char *s = mc_dec128_to_new_decimal_string(d); if (!s) { return NULL; } char *s1 = (char *)calloc(strlen(s) + 2, 1); if (s1) { s1[0] = '-'; strcpy(s1 + 1, s); } free(s); return s1; } if (mc_dec128_is_inf(d) || mc_dec128_is_nan(d)) { const char *r = mc_dec128_is_inf(d) ? "Infinity" : "NaN"; char *c = (char *)calloc(strlen(r) + 1, 1); if (c) { strcpy(c, r); } return c; } const char DIGITS[] = "0123456789"; const mc_dec128 TEN = MC_DEC128_C(10); // Format the whole and fractional part separately. mc_dec128_modf_result modf = mc_dec128_modf(d); if (mc_dec128_is_zero(modf.frac)) { // This is a non-zero integer // Allocate enough digits: mc_dec128 log10 = mc_dec128_modf(mc_dec128_log10(d)).whole; int64_t ndigits = mc_dec128_to_int64(log10) + 1; // +1 for null char *strbuf = (char *)calloc((size_t)(ndigits + 1), 1); if (strbuf) { // Write the string backwards: char *optr = strbuf + ndigits - 1; while (!mc_dec128_is_zero(modf.whole)) { mc_dec128 rem = mc_dec128_fmod(modf.whole, TEN); int64_t remi = mc_dec128_to_int64(rem); *optr-- = DIGITS[remi]; // Divide ten modf = mc_dec128_modf(mc_dec128_div(modf.whole, TEN)); } } return strbuf; } else if (mc_dec128_is_zero(modf.whole)) { // This is only a fraction (less than one, but more than zero) while (!mc_dec128_is_zero(mc_dec128_modf(d).frac)) { d = mc_dec128_mul(d, TEN); } // 'd' is now a whole number char *part = mc_dec128_to_new_decimal_string(d); if (!part) { return NULL; } char *buf = (char *)calloc(strlen(part) + 3, 1); if (buf) { buf[0] = '0'; buf[1] = '.'; strcpy(buf + 2, part); } free(part); return buf; } else { // We have both a whole part and a fractional part char *whole = mc_dec128_to_new_decimal_string(modf.whole); if (!whole) { return NULL; } char *frac = mc_dec128_to_new_decimal_string(modf.frac); if (!frac) { free(whole); return NULL; } char *ret = (char *)calloc(strlen(whole) + strlen(frac) + 1, 1); if (ret) { char *out = ret; strcpy(out, whole); out += strlen(whole); // "frac" contains a leading zero, which we don't want strcpy(out, frac + 1); } free(whole); free(frac); return ret; } } static inline mc_dec128 mc_dec128_from_bson_iter(const bson_iter_t *it) { bson_decimal128_t b; if (!bson_iter_decimal128(it, &b)) { mc_dec128 nan = MC_DEC128_POSITIVE_NAN; return nan; } mc_dec128 ret; memcpy(&ret, &b, sizeof b); return ret; } static inline bson_decimal128_t mc_dec128_to_bson_decimal128(mc_dec128 v) { bson_decimal128_t ret; memcpy(&ret, &v, sizeof ret); return ret; } MLIB_C_LINKAGE_END #endif /// defined MONGOCRYPT_INTELDFP #endif // MC_DEC128_H_INCLUDED libmongocrypt-1.19.0/src/mc-dec128.test.cpp000066400000000000000000000036171521103432300203270ustar00rootroot00000000000000#include #include #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() #include #include #define CHECK MLIB_CHECK inline std::ostream &operator<<(std::ostream &out, mc_dec128 d) noexcept { auto s = mc_dec128_to_new_decimal_string(d); out << s; free(s); return out; } #define OPER(Op, Fn) \ inline auto operator Op(mc_dec128 a, mc_dec128 b) noexcept /**/ \ ->decltype(mc_dec128_##Fn(a, b)) { \ return mc_dec128_##Fn(a, b); \ } OPER(+, add) OPER(-, sub) OPER(*, mul) OPER(/, div) OPER(==, equal) OPER(>, greater) OPER(<, less) int main() { mc_dec128 a = MC_DEC128_ZERO; CHECK(mc_dec128_is_zero(a)); mc_dec128 b = MC_DEC128_ZERO; mc_dec128 c = a * b; CHECK(c == MC_DEC128_ZERO); CHECK(mc_dec128_is_zero(c)); b = MC_DEC128_C(1); // 0 + 1 = 1 c = a + b; CHECK(c == MC_DEC128_C(1)); // 1 + 1 = 2 c = b + b; CHECK(c == MC_DEC128_C(2)); // 2 * 2 = 4 c = c * c; CHECK(c == MC_DEC128_C(4)); // (4 + 1) / 2 = 2.5 c = (c + MC_DEC128_C(1)) / MC_DEC128_C(2); CHECK(c == mc_dec128_from_string("2.5")); mc_dec128_string s = mc_dec128_to_string(c); CHECK(std::string(s.str) == "+25E-1"); char *str = mc_dec128_to_new_decimal_string(c); CHECK(std::string(str) == "2.5"); free(str); mc_dec128 infin = MC_DEC128_POSITIVE_INFINITY; CHECK(mc_dec128_is_inf(infin)); mc_dec128 nan = MC_DEC128_POSITIVE_NAN; CHECK(mc_dec128_is_nan(nan)); } #else int main() { std::puts("@@ctest-skip@@\n Decimal128 support is not enabled\n"); } #endif libmongocrypt-1.19.0/src/mc-efc-private.h000066400000000000000000000047361521103432300202400ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_EFC_PRIVATE_H #define MC_EFC_PRIVATE_H #include "mongocrypt-buffer-private.h" #include typedef enum _supported_query_type_flags { // No queries supported SUPPORTS_NO_QUERIES = 0, // Equality query supported SUPPORTS_EQUALITY_QUERIES = 1 << 0, // Range query supported SUPPORTS_RANGE_QUERIES = 1 << 1, // Range preview query supported SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES = 1 << 2, // Text search preview query supported SUPPORTS_SUBSTRING_PREVIEW_QUERIES = 1 << 3, SUPPORTS_SUFFIX_QUERIES = 1 << 4, SUPPORTS_PREFIX_QUERIES = 1 << 5, // prefixPreview and suffixPreview are dropped. Setting this results in an error. SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES = 1 << 6, SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES = 1 << 7, } supported_query_type_flags; typedef struct _mc_EncryptedField_t { supported_query_type_flags supported_queries; _mongocrypt_buffer_t keyId; const char *keyAltName; const char *path; struct _mc_EncryptedField_t *next; } mc_EncryptedField_t; /* See * https://github.com/mongodb/mongo/blob/591f49a64e96cea68bf3501320de31c51c31f412/src/mongo/crypto/encryption_fields.idl#L48-L112 * for the server IDL definition of EncryptedFieldConfig. */ typedef struct { mc_EncryptedField_t *fields; uint8_t str_encode_version; } mc_EncryptedFieldConfig_t; /* mc_EncryptedFieldConfig_parse parses a subset of the fields from @efc_bson * into @efc. Fields are copied from @efc_bson. It is OK to free efc_bson after * this call. Fields are appended in reverse order to @efc->fields. Extra * unrecognized fields are not considered an error for forward compatibility. */ bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc, const bson_t *efc_bson, mongocrypt_status_t *status); void mc_EncryptedFieldConfig_cleanup(mc_EncryptedFieldConfig_t *efc); #endif /* MC_EFC_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-efc.c000066400000000000000000000267011521103432300165570ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-efc-private.h" #include "mc-mlib/str.h" #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" // mc_iter_document_as_bson #include static bool _parse_query_type_string(const char *queryType, supported_query_type_flags *out) { BSON_ASSERT_PARAM(queryType); BSON_ASSERT_PARAM(out); mstr_view qtv = mstrv_view_cstr(queryType); if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR), qtv)) { *out = SUPPORTS_EQUALITY_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGE_STR), qtv)) { *out = SUPPORTS_RANGE_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR), qtv)) { *out = SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR), qtv)) { *out = SUPPORTS_SUBSTRING_PREVIEW_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIX_STR), qtv)) { *out = SUPPORTS_SUFFIX_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIX_STR), qtv)) { *out = SUPPORTS_PREFIX_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR), qtv)) { *out = SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR), qtv)) { *out = SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES; } else { return false; } return true; } static bool _parse_supported_query_types(bson_iter_t *iter, supported_query_type_flags *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iter); BSON_ASSERT_PARAM(out); if (!BSON_ITER_HOLDS_DOCUMENT(iter)) { CLIENT_ERR("When parsing supported query types: Expected type document, got: %d", (int)bson_iter_type(iter)); return false; } bson_t query_doc; if (!mc_iter_document_as_bson(iter, &query_doc, status)) { return false; } bson_iter_t query_type_iter; if (!bson_iter_init_find(&query_type_iter, &query_doc, "queryType")) { CLIENT_ERR("When parsing supported query types: Unable to find 'queryType' in query document"); return false; } if (!BSON_ITER_HOLDS_UTF8(&query_type_iter)) { CLIENT_ERR("When parsing supported query types: Expected 'queryType' to be type UTF-8, got: %d", (int)bson_iter_type(&query_type_iter)); return false; } const char *queryType = bson_iter_utf8(&query_type_iter, NULL /* length */); if (!_parse_query_type_string(queryType, out)) { CLIENT_ERR("When parsing supported query types: Did not recognize query type '%s'", queryType); return false; } return true; } /* _parse_field parses and prepends one field document to efc->fields. */ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocrypt_status_t *status) { supported_query_type_flags query_types = SUPPORTS_NO_QUERIES; bson_iter_t field_iter, keyid_iter, keyaltname_iter; BSON_ASSERT_PARAM(efc); BSON_ASSERT_PARAM(field); bool has_keyid = false; bool has_keyaltname = false; if (bson_iter_init_find(&keyid_iter, field, "keyId")) { has_keyid = true; } if (bson_iter_init_find(&keyaltname_iter, field, "keyAltName")) { has_keyaltname = true; } if (!(has_keyid || has_keyaltname)) { CLIENT_ERR("unable to find 'keyId' or 'keyAltName' in 'field' document"); return false; } if (has_keyid && has_keyaltname) { CLIENT_ERR("only one of 'keyId' or 'keyAltName may be in 'field' document"); return false; } _mongocrypt_buffer_t field_keyid; if (has_keyid) { if (!BSON_ITER_HOLDS_BINARY(&keyid_iter)) { CLIENT_ERR("expected 'fields.keyId' to be type binary, got: %s", mc_bson_type_to_string(bson_iter_type(&keyid_iter))); return false; } if (!_mongocrypt_buffer_from_uuid_iter(&field_keyid, &keyid_iter)) { CLIENT_ERR("unable to parse uuid key from 'fields.keyId'"); return false; } } const char *keyAltName = ""; if (has_keyaltname) { if (!BSON_ITER_HOLDS_UTF8(&keyaltname_iter)) { CLIENT_ERR("expected 'fields.keyAltName' to be type UTF-8, got: %d", (int)bson_iter_type(&keyaltname_iter)); return false; } keyAltName = bson_iter_utf8(&keyaltname_iter, NULL); } const char *field_path; if (!bson_iter_init_find(&field_iter, field, "path")) { CLIENT_ERR("unable to find 'path' in 'field' document"); return false; } if (!BSON_ITER_HOLDS_UTF8(&field_iter)) { CLIENT_ERR("expected 'fields.path' to be type UTF-8, got: %d", (int)bson_iter_type(&field_iter)); return false; } field_path = bson_iter_utf8(&field_iter, NULL /* length */); if (bson_iter_init_find(&field_iter, field, "queries")) { if (BSON_ITER_HOLDS_ARRAY(&field_iter)) { // Multiple queries, iterate through and grab all query types. uint32_t queries_buf_len; const uint8_t *queries_buf; bson_t queries_arr; bson_iter_array(&field_iter, &queries_buf_len, &queries_buf); if (!bson_init_static(&queries_arr, queries_buf, queries_buf_len)) { CLIENT_ERR("Failed to parse 'queries' field"); return false; } bson_iter_t queries_iter; bson_iter_init(&queries_iter, &queries_arr); while (bson_iter_next(&queries_iter)) { supported_query_type_flags flag; if (!_parse_supported_query_types(&queries_iter, &flag, status)) { return false; } query_types |= flag; } } else { supported_query_type_flags flag; if (!_parse_supported_query_types(&field_iter, &flag, status)) { return false; } query_types |= flag; } } if (query_types & SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES) { // Error if the removed "rangePreview" is included. // This check is intended to give an easier-to-understand earlier error. CLIENT_ERR("Cannot use field '%s' with 'rangePreview' queries. 'rangePreview' is unsupported. Use 'range' " "instead. 'range' is not compatible with 'rangePreview' and requires recreating the collection.", field_path); return false; } if (query_types & SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES) { CLIENT_ERR("Cannot use field '%s' with 'prefixPreview' queries. 'prefixPreview' is unsupported. Use 'prefix' " "instead.", field_path); return false; } if (query_types & SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES) { CLIENT_ERR("Cannot use field '%s' with 'suffixPreview' queries. 'suffixPreview' is unsupported. Use 'suffix' " "instead.", field_path); return false; } /* Prepend a new mc_EncryptedField_t */ mc_EncryptedField_t *ef = bson_malloc0(sizeof(mc_EncryptedField_t)); if (has_keyid) { _mongocrypt_buffer_copy_to(&field_keyid, &ef->keyId); } if (has_keyaltname) { ef->keyAltName = bson_strdup(keyAltName); } ef->path = bson_strdup(field_path); ef->next = efc->fields; ef->supported_queries = query_types; efc->fields = ef; return true; } bool mc_EncryptedFieldConfig_parse(mc_EncryptedFieldConfig_t *efc, const bson_t *efc_bson, mongocrypt_status_t *status) { bson_iter_t iter; BSON_ASSERT_PARAM(efc); BSON_ASSERT_PARAM(efc_bson); memset(efc, 0, sizeof(*efc)); if (!bson_iter_init_find(&iter, efc_bson, "fields")) { CLIENT_ERR("unable to find 'fields' in encrypted_field_config"); return false; } if (!BSON_ITER_HOLDS_ARRAY(&iter)) { CLIENT_ERR("expected 'fields' to be type array, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } if (!bson_iter_recurse(&iter, &iter)) { CLIENT_ERR("unable to recurse into encrypted_field_config 'fields'"); return false; } supported_query_type_flags all_supported_queries = SUPPORTS_NO_QUERIES; while (bson_iter_next(&iter)) { bson_t field; if (!mc_iter_document_as_bson(&iter, &field, status)) { return false; } if (!_parse_field(efc, &field, status)) { return false; } // The first element of efc->fields contains the newly parsed field. all_supported_queries |= efc->fields->supported_queries; } // Check for duplicate keyAltName values for (mc_EncryptedField_t *field1 = efc->fields; field1 != NULL; field1 = field1->next) { if (field1->keyAltName) { for (mc_EncryptedField_t *field2 = field1->next; field2 != NULL; field2 = field2->next) { if (field2->keyAltName) { if (strcmp(field1->keyAltName, field2->keyAltName) == 0) { CLIENT_ERR("duplicate keyAltName '%s' found in encrypted field config", field1->keyAltName); return false; } } } } } if (!bson_iter_init_find(&iter, efc_bson, "strEncodeVersion")) { if (all_supported_queries & (SUPPORTS_SUBSTRING_PREVIEW_QUERIES | SUPPORTS_SUFFIX_QUERIES | SUPPORTS_PREFIX_QUERIES)) { // Has at least one text search query type, set to latest by default. efc->str_encode_version = LATEST_STR_ENCODE_VERSION; } else { // Set to 0 to indicate no text search, and thus no strEncodeVersion needed. efc->str_encode_version = 0; } } else { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR("expected 'strEncodeVersion' to be type int32, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } int32_t version = bson_iter_int32(&iter); if (version > LATEST_STR_ENCODE_VERSION || version < MIN_STR_ENCODE_VERSION) { CLIENT_ERR("'strEncodeVersion' of %" PRId32 " is not supported", version); return false; } efc->str_encode_version = (uint8_t)version; } return true; } void mc_EncryptedFieldConfig_cleanup(mc_EncryptedFieldConfig_t *efc) { if (!efc) { return; } mc_EncryptedField_t *ptr = efc->fields; while (ptr != NULL) { mc_EncryptedField_t *ptr_next = ptr->next; _mongocrypt_buffer_cleanup(&ptr->keyId); bson_free((char *)ptr->path); bson_free((char *)ptr->keyAltName); bson_free(ptr); ptr = ptr_next; } } libmongocrypt-1.19.0/src/mc-fle-blob-subtype-private.h000066400000000000000000000036511521103432300226510ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE_BLOB_SUBTYPE_PRIVATE_H #define MC_FLE_BLOB_SUBTYPE_PRIVATE_H /* FLE Blob Subtype is the first byte of a BSON Binary Subtype 6. * These are defined by the EncryptedBinDataType enum in: * https://github.com/mongodb/mongo/blob/0d51c5cb6571a14e690c4774bb069d1990fd35b6/src/mongo/crypto/fle_field_schema.idl */ typedef enum { MC_SUBTYPE_FLE1EncryptionPlaceholder = 0, MC_SUBTYPE_FLE1DeterministicEncryptedValue = 1, MC_SUBTYPE_FLE1RandomEncryptedValue = 2, MC_SUBTYPE_FLE2EncryptionPlaceholder = 3, MC_SUBTYPE_FLE2InsertUpdatePayload = 4, MC_SUBTYPE_FLE2FindEqualityPayload = 5, MC_SUBTYPE_FLE2UnindexedEncryptedValue = 6, MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue = 7, MC_SUBTYPE_FLE2IndexedRangeEncryptedValue = 9, MC_SUBTYPE_FLE2FindRangePayload = 10, /* Queryable Encryption Version 2 Subtypes */ MC_SUBTYPE_FLE2InsertUpdatePayloadV2 = 11, MC_SUBTYPE_FLE2FindEqualityPayloadV2 = 12, MC_SUBTYPE_FLE2FindRangePayloadV2 = 13, MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2 = 14, MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2 = 15, MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 = 16, /* Text Search Subtypes */ MC_SUBTYPE_FLE2IndexedTextEncryptedValue = 17, MC_SUBTYPE_FLE2FindTextPayload = 18, } mc_fle_blob_subtype_t; #endif /* MC_FLE_BLOB_SUBTYPE_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-encryption-placeholder-private.h000066400000000000000000000252201521103432300247720ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_ENCRYPTION_PLACEHOLDER_PRIVATE_H #define MC_FLE2_ENCRYPTION_PLACEHOLDER_PRIVATE_H #include #include "mc-fle2-find-range-payload-private.h" #include "mc-optional-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" /** FLE2RangeFindSpecEdgesInfo represents the information needed to generate * edges for a range find query. It is encoded inside an FLE2RangeFindSpec. See * https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl * for the representation in the MongoDB server. * * Bounds on range queries are referred to as lowerBound or lb, and upperBound * or ub. Bounds on an underlying range index are referred to as min and max. */ typedef struct { // lowerBound is the lower bound for an encrypted range query. bson_iter_t lowerBound; // lbIncluded indicates if the lower bound should be included in the range. bool lbIncluded; // upperBound is the upperBound for an encrypted range query. bson_iter_t upperBound; // ubIncluded indicates if the upper bound should be included in the range. bool ubIncluded; // indexMin is the minimum value for the encrypted index that this query is // using. bson_iter_t indexMin; // indexMax is the maximum value for the encrypted index that this query is // using. bson_iter_t indexMax; // precision determines the number of digits after the decimal point for // floating point values. mc_optional_int32_t precision; // trimFactor determines how many root levels of the hypergraph to trim. mc_optional_int32_t trimFactor; } mc_FLE2RangeFindSpecEdgesInfo_t; // `mc_FLE2RangeFindSpecEdgesInfo_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindSpecEdgesInfo_t, BSON_ALIGNOF(mc_FLE2RangeFindSpecEdgesInfo_t) >= BSON_ALIGNOF(bson_iter_t)); /** FLE2RangeFindSpec represents the range find specification that is encoded * inside of a FLE2EncryptionPlaceholder. See * https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl * for the representation in the MongoDB server. */ typedef struct { // edgesInfo is the information about the edges in an FLE2 find payload. struct { mc_FLE2RangeFindSpecEdgesInfo_t value; bool set; } edgesInfo; // payloadId Id of payload - must be paired with another payload. int32_t payloadId; // firstOperator represents the first query operator for which this payload // was generated. mc_FLE2RangeOperator_t firstOperator; // secondOperator represents the second query operator for which this payload // was generated. Only populated for two-sided ranges. It is 0 if unset. mc_FLE2RangeOperator_t secondOperator; } mc_FLE2RangeFindSpec_t; // `mc_FLE2RangeFindSpec_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindSpec_t, BSON_ALIGNOF(mc_FLE2RangeFindSpec_t) >= BSON_ALIGNOF(mc_FLE2RangeFindSpecEdgesInfo_t)); bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); /** mc_FLE2RangeInsertSpec_t represents the range insert specification that is * encoded inside of a FLE2EncryptionPlaceholder. See * https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl#L364 * for the representation in the MongoDB server. */ typedef struct { // v is the value to encrypt. bson_iter_t v; // min is the Queryable Encryption min bound for range. bson_iter_t min; // max is the Queryable Encryption max bound for range. bson_iter_t max; // precision determines the number of digits after the decimal point for // floating point values. mc_optional_int32_t precision; // trimFactor determines how many root levels of the hypergraph to trim. mc_optional_int32_t trimFactor; } mc_FLE2RangeInsertSpec_t; // `mc_FLE2RangeInsertSpec_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeInsertSpec_t, BSON_ALIGNOF(mc_FLE2RangeInsertSpec_t) >= BSON_ALIGNOF(bson_iter_t)); bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); // Note: For the substring/suffix/prefix insert specs, all lengths are in terms of number of UTF-8 codepoints, not // number of bytes. /* mc_FLE2SubstringInsertSpec_t holds the parameters used to encode for substring search. */ typedef struct { // mlen is the max string length that can be indexed. uint32_t mlen; // lb is the lower bound on the length of substrings to be indexed. uint32_t lb; // ub is the upper bound on the length of substrings to be indexed. uint32_t ub; } mc_FLE2SubstringInsertSpec_t; bool mc_FLE2SubstringInsertSpec_parse(mc_FLE2SubstringInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); /* mc_FLE2SuffixInsertSpec_t holds the parameters used to encode for suffix search. */ typedef struct { // lb is the lower bound on the length of suffixes to be indexed. uint32_t lb; // ub is the upper bound on the length of suffixes to be indexed. uint32_t ub; } mc_FLE2SuffixInsertSpec_t; bool mc_FLE2SuffixInsertSpec_parse(mc_FLE2SuffixInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); /* mc_FLE2PrefixInsertSpec_t holds the parameters used to encode for prefix search. */ typedef struct { // lb is the lower bound on the length of prefixes to be indexed. uint32_t lb; // ub is the upper bound on the length of prefixes to be indexed. uint32_t ub; } mc_FLE2PrefixInsertSpec_t; bool mc_FLE2PrefixInsertSpec_parse(mc_FLE2PrefixInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); /** mc_FLE2TextSearchInsertSpec_t represents the text search insert specification that is * encoded inside of a FLE2EncryptionPlaceholder. See * https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl * for the representation in the MongoDB server. */ typedef struct { // v_iter points to the value to encrypt. bson_iter_t v_iter; // v is the value to encrypt, pointing to the value at v_iter. const char *v; // len is the byte length of v. uint32_t len; // substr is the spec for substring indexing. struct { mc_FLE2SubstringInsertSpec_t value; bool set; } substr; // suffix is the spec for suffix indexing. struct { mc_FLE2SuffixInsertSpec_t value; bool set; } suffix; // prefix is the spec for prefix indexing. struct { mc_FLE2PrefixInsertSpec_t value; bool set; } prefix; // casef indicates if case folding is enabled. bool casef; // diacf indicates if diacritic folding is enabled. bool diacf; } mc_FLE2TextSearchInsertSpec_t; // `mc_FLE2TextSearchInsertSpec_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2TextSearchInsertSpec_t, BSON_ALIGNOF(mc_FLE2TextSearchInsertSpec_t) >= BSON_ALIGNOF(bson_iter_t)); /** mc_FLE2TextSearchInsertSpec_parse parses a BSON document into a mc_FLE2TextSearchInsertSpec_t. * @in must point to a BSON document. * @out must outlive the BSON object @in is iterating on. * - Returns false on error. * - No cleanup needed for @out. */ bool mc_FLE2TextSearchInsertSpec_parse(mc_FLE2TextSearchInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status); /** FLE2EncryptionPlaceholder implements Encryption BinData (subtype 6) * sub-subtype 0, the intent-to-encrypt mapping. Contains a value to encrypt and * a description of how it should be encrypted. * * For automatic encryption, FLE2EncryptionPlaceholder is created by query * analysis (mongocryptd or mongo_crypt shared library). For explicit * encryption, FLE2EncryptionPlaceholder is created by libmongocrypt. * * FLE2EncryptionPlaceholder is processed by libmongocrypt into a payload * suitable to send to the MongoDB server (mongod/mongos). * * See * https://github.com/mongodb/mongo/blob/d870dda33fb75983f628636ff8f849c7f1c90b09/src/mongo/crypto/fle_field_schema.idl#L133 * for the representation in the MongoDB server. */ typedef struct { mongocrypt_fle2_placeholder_type_t type; mongocrypt_fle2_encryption_algorithm_t algorithm; bson_iter_t v_iter; _mongocrypt_buffer_t index_key_id; _mongocrypt_buffer_t user_key_id; int64_t maxContentionFactor; // sparsity is the Queryable Encryption range hypergraph sparsity factor int64_t sparsity; } mc_FLE2EncryptionPlaceholder_t; // `mc_FLE2EncryptionPlaceholder_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2EncryptionPlaceholder_t, BSON_ALIGNOF(mc_FLE2EncryptionPlaceholder_t) >= BSON_ALIGNOF(bson_iter_t)); void mc_FLE2EncryptionPlaceholder_init(mc_FLE2EncryptionPlaceholder_t *placeholder); bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out, const bson_t *in, mongocrypt_status_t *status); void mc_FLE2EncryptionPlaceholder_cleanup(mc_FLE2EncryptionPlaceholder_t *placeholder); /* mc_validate_contention is used to check that contention is a valid * value. contention may come from the 'cm' field in FLE2EncryptionPlaceholder * or from mongocrypt_ctx_setopt_contention_factor. */ bool mc_validate_contention(int64_t contention, mongocrypt_status_t *status); /* mc_validate_sparsity is used to check that sparsity is a valid * value. */ bool mc_validate_sparsity(int64_t sparsity, mongocrypt_status_t *status); #endif /* MC_FLE2_ENCRYPTION_PLACEHOLDER_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-encryption-placeholder.c000066400000000000000000000563571521103432300233340ustar00rootroot00000000000000/* * Copyright 2022-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 #include // SIZE_MAX #include "mc-fle2-encryption-placeholder-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" // mc_bson_type_to_string #include "mongocrypt.h" // Common logic for testing field name, tracking duplication, and presence. #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR(ERROR_PREFIX "Duplicate field '" #Name "' in placeholder bson"); \ goto fail; \ } \ has_##Name = true; \ ((void)0) #define END_IF_FIELD \ continue; \ } \ else((void)0) #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR(ERROR_PREFIX "Missing field '" #Name "' in placeholder"); \ goto fail; \ } // Common logic for parsing int32 greater than zero #define IF_FIELD_INT32_GT0_PARSE(Name, Dest, Iter) \ IF_FIELD(Name); \ { \ if (!BSON_ITER_HOLDS_INT32(&Iter)) { \ CLIENT_ERR(ERROR_PREFIX "'" #Name "' must be an int32"); \ goto fail; \ } \ int32_t val = bson_iter_int32(&Iter); \ if (val <= 0) { \ CLIENT_ERR(ERROR_PREFIX "'" #Name "' must be greater than zero"); \ goto fail; \ } \ Dest = (uint32_t)val; \ } \ END_IF_FIELD void mc_FLE2EncryptionPlaceholder_init(mc_FLE2EncryptionPlaceholder_t *placeholder) { memset(placeholder, 0, sizeof(mc_FLE2EncryptionPlaceholder_t)); } #define ERROR_PREFIX "Error parsing FLE2EncryptionPlaceholder: " bool mc_FLE2EncryptionPlaceholder_parse(mc_FLE2EncryptionPlaceholder_t *out, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter = {0}; bool has_t = false, has_a = false, has_v = false, has_cm = false; bool has_ki = false, has_ku = false; bool has_s = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); mc_FLE2EncryptionPlaceholder_init(out); if (!bson_validate(in, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, in)) { CLIENT_ERR(ERROR_PREFIX "invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(t); { int32_t type; if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "invalid marking, 't' must be an int32"); goto fail; } type = bson_iter_int32(&iter); if ((type != MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT) && (type != MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND)) { CLIENT_ERR(ERROR_PREFIX "invalid placeholder type value: %d", type); goto fail; } out->type = (mongocrypt_fle2_placeholder_type_t)type; } END_IF_FIELD; IF_FIELD(a); { int32_t algorithm; if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "invalid marking, 'a' must be an int32"); goto fail; } algorithm = bson_iter_int32(&iter); if (algorithm != MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED && algorithm != MONGOCRYPT_FLE2_ALGORITHM_EQUALITY && algorithm != MONGOCRYPT_FLE2_ALGORITHM_RANGE && algorithm != MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH) { CLIENT_ERR(ERROR_PREFIX "invalid algorithm value: %d", algorithm); goto fail; } out->algorithm = (mongocrypt_fle2_encryption_algorithm_t)algorithm; } END_IF_FIELD; IF_FIELD(ki); { if (!_mongocrypt_buffer_from_uuid_iter(&out->index_key_id, &iter)) { CLIENT_ERR(ERROR_PREFIX "index key id must be a UUID"); goto fail; } } END_IF_FIELD; IF_FIELD(ku); { if (!_mongocrypt_buffer_from_uuid_iter(&out->user_key_id, &iter)) { CLIENT_ERR(ERROR_PREFIX "user key id must be a UUID"); goto fail; } } END_IF_FIELD; IF_FIELD(v); memcpy(&out->v_iter, &iter, sizeof(bson_iter_t)); END_IF_FIELD; IF_FIELD(cm); { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR(ERROR_PREFIX "invalid marking, 'cm' must be an int64"); goto fail; } out->maxContentionFactor = bson_iter_int64(&iter); if (!mc_validate_contention(out->maxContentionFactor, status)) { goto fail; } } END_IF_FIELD; IF_FIELD(s); { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR(ERROR_PREFIX "invalid marking, 's' must be an int64"); goto fail; } out->sparsity = bson_iter_int64(&iter); if (!mc_validate_sparsity(out->sparsity, status)) { goto fail; } } END_IF_FIELD; } CHECK_HAS(t) CHECK_HAS(a) CHECK_HAS(ki) CHECK_HAS(ku) CHECK_HAS(v) CHECK_HAS(cm) // Do not error if sparsity (s) is not present. // 's' was added in version 6.1 of query analysis (mongocryptd or mongo_crypt // shared library). 's' is not present in query analysis 6.0. return true; fail: return false; } void mc_FLE2EncryptionPlaceholder_cleanup(mc_FLE2EncryptionPlaceholder_t *placeholder) { BSON_ASSERT_PARAM(placeholder); _mongocrypt_buffer_cleanup(&placeholder->index_key_id); _mongocrypt_buffer_cleanup(&placeholder->user_key_id); mc_FLE2EncryptionPlaceholder_init(placeholder); } #undef ERROR_PREFIX #define ERROR_PREFIX "Error validating contention: " bool mc_validate_contention(int64_t contention, mongocrypt_status_t *status) { if (contention < 0) { CLIENT_ERR(ERROR_PREFIX "contention must be non-negative, got: %" PRId64, contention); return false; } if (contention == INT64_MAX) { CLIENT_ERR(ERROR_PREFIX "contention must be < INT64_MAX, got: %" PRId64, contention); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error validating sparsity: " bool mc_validate_sparsity(int64_t sparsity, mongocrypt_status_t *status) { if (sparsity < 0) { CLIENT_ERR(ERROR_PREFIX "sparsity must be non-negative, got: %" PRId64, sparsity); return false; } // mc_getEdgesInt expects a size_t sparsity. if ((uint64_t)sparsity >= SIZE_MAX) { CLIENT_ERR(ERROR_PREFIX "sparsity must be < %zu, got: %" PRId64, SIZE_MAX, sparsity); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2RangeFindSpecEdgesInfo: " static bool mc_FLE2RangeFindSpecEdgesInfo_parse(mc_FLE2RangeFindSpecEdgesInfo_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_lowerBound = false, has_lbIncluded = false, has_upperBound = false, has_ubIncluded = false, has_indexMin = false, has_indexMax = false, has_precision = false, has_trimFactor = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(lowerBound); out->lowerBound = iter; END_IF_FIELD; IF_FIELD(lbIncluded); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "'lbIncluded' must be a bool"); goto fail; } out->lbIncluded = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(upperBound); out->upperBound = iter; END_IF_FIELD; IF_FIELD(ubIncluded); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "'ubIncluded' must be a bool"); goto fail; } out->ubIncluded = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(indexMin); out->indexMin = iter; END_IF_FIELD; IF_FIELD(indexMax); out->indexMax = iter; END_IF_FIELD; IF_FIELD(precision); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'precision' must be an int32"); goto fail; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'precision' must be non-negative"); goto fail; } out->precision = OPT_I32(val); } END_IF_FIELD; IF_FIELD(trimFactor); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'trimFactor' must be an int32"); goto fail; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'trimFactor' must be non-negative"); goto fail; } out->trimFactor = OPT_I32(val); } END_IF_FIELD; } CHECK_HAS(lowerBound) CHECK_HAS(lbIncluded) CHECK_HAS(upperBound) CHECK_HAS(ubIncluded) CHECK_HAS(indexMin) CHECK_HAS(indexMax) // Do not error if precision is not present. Precision optional and only // applies to double/decimal128. return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2RangeFindSpec: " bool mc_FLE2RangeFindSpec_parse(mc_FLE2RangeFindSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); bson_iter_t iter = *in; bool has_edgesInfo = false, has_payloadId = false, has_firstOperator = false, has_secondOperator = false; *out = (mc_FLE2RangeFindSpec_t){{{{0}}}}; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(edgesInfo); { if (!mc_FLE2RangeFindSpecEdgesInfo_parse(&out->edgesInfo.value, &iter, status)) { goto fail; } out->edgesInfo.set = true; } END_IF_FIELD; IF_FIELD(payloadId); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'payloadId' must be an int32"); goto fail; } out->payloadId = bson_iter_int32(&iter); } END_IF_FIELD; IF_FIELD(firstOperator); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'firstOperator' must be an int32"); goto fail; } const int32_t first_op = bson_iter_int32(&iter); if (first_op < FLE2RangeOperator_min_val || first_op > FLE2RangeOperator_max_val) { CLIENT_ERR(ERROR_PREFIX "'firstOperator' must be between %d and %d", FLE2RangeOperator_min_val, FLE2RangeOperator_max_val); goto fail; } out->firstOperator = (mc_FLE2RangeOperator_t)first_op; } END_IF_FIELD; IF_FIELD(secondOperator); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'secondOperator' must be an int32"); goto fail; } const int32_t second_op = bson_iter_int32(&iter); if (second_op < FLE2RangeOperator_min_val || second_op > FLE2RangeOperator_max_val) { CLIENT_ERR(ERROR_PREFIX "'secondOperator' must be between %d and %d", FLE2RangeOperator_min_val, FLE2RangeOperator_max_val); goto fail; } out->secondOperator = (mc_FLE2RangeOperator_t)second_op; } END_IF_FIELD; } // edgesInfo is optional. Do not require it. CHECK_HAS(payloadId) CHECK_HAS(firstOperator) // secondOperator is optional. Do not require it. return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2RangeInsertSpec: " bool mc_FLE2RangeInsertSpec_parse(mc_FLE2RangeInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); *out = (mc_FLE2RangeInsertSpec_t){{0}}; bson_iter_t iter = *in; bool has_v = false, has_min = false, has_max = false, has_precision = false, has_trimFactor = false; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(v); out->v = iter; END_IF_FIELD; IF_FIELD(min); out->min = iter; END_IF_FIELD; IF_FIELD(max); out->max = iter; END_IF_FIELD; IF_FIELD(precision); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'precision' must be an int32"); goto fail; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'precision' must be non-negative"); goto fail; } out->precision = OPT_I32(val); } END_IF_FIELD; IF_FIELD(trimFactor); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'trimFactor' must be an int32"); goto fail; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'trimFactor' must be non-negative"); goto fail; } out->trimFactor = OPT_I32(val); } END_IF_FIELD; } CHECK_HAS(v) CHECK_HAS(min) CHECK_HAS(max) // Do not error if precision is not present. Precision optional and only // applies to double/decimal128. return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2SubstringInsertSpec: " bool mc_FLE2SubstringInsertSpec_parse(mc_FLE2SubstringInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_mlen = false, has_ub = false, has_lb = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD_INT32_GT0_PARSE(mlen, out->mlen, iter); IF_FIELD_INT32_GT0_PARSE(ub, out->ub, iter); IF_FIELD_INT32_GT0_PARSE(lb, out->lb, iter); } CHECK_HAS(mlen) CHECK_HAS(ub) CHECK_HAS(lb) if (out->ub < out->lb) { CLIENT_ERR(ERROR_PREFIX "upper bound cannot be less than the lower bound"); goto fail; } if (out->mlen < out->ub) { CLIENT_ERR(ERROR_PREFIX "maximum indexed length cannot be less than the upper bound"); goto fail; } return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2SuffixInsertSpec: " bool mc_FLE2SuffixInsertSpec_parse(mc_FLE2SuffixInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_ub = false, has_lb = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD_INT32_GT0_PARSE(ub, out->ub, iter); IF_FIELD_INT32_GT0_PARSE(lb, out->lb, iter); } CHECK_HAS(ub) CHECK_HAS(lb) if (out->ub < out->lb) { CLIENT_ERR(ERROR_PREFIX "upper bound cannot be less than the lower bound"); goto fail; } return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2PrefixInsertSpec: " bool mc_FLE2PrefixInsertSpec_parse(mc_FLE2PrefixInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_ub = false, has_lb = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD_INT32_GT0_PARSE(ub, out->ub, iter); IF_FIELD_INT32_GT0_PARSE(lb, out->lb, iter); } CHECK_HAS(ub) CHECK_HAS(lb) if (out->ub < out->lb) { CLIENT_ERR(ERROR_PREFIX "upper bound cannot be less than the lower bound"); goto fail; } return true; fail: return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error parsing FLE2TextSearchInsertSpec: " bool mc_FLE2TextSearchInsertSpec_parse(mc_FLE2TextSearchInsertSpec_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); *out = (mc_FLE2TextSearchInsertSpec_t){{0}}; bson_iter_t iter = *in; bool has_v = false, has_casef = false, has_diacf = false; bool has_substr = false, has_suffix = false, has_prefix = false; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR(ERROR_PREFIX "must be an iterator to a document"); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(v); { out->v = bson_iter_utf8(&iter, &out->len); if (!out->v) { CLIENT_ERR(ERROR_PREFIX "unsupported BSON type: %s for text search", mc_bson_type_to_string(bson_iter_type(&iter))); goto fail; } out->v_iter = iter; } END_IF_FIELD; IF_FIELD(casef); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "'casef' must be a bool"); goto fail; } out->casef = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(diacf); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "'diacf' must be a bool"); goto fail; } out->diacf = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(substr); { if (!mc_FLE2SubstringInsertSpec_parse(&out->substr.value, &iter, status)) { goto fail; } out->substr.set = true; } END_IF_FIELD; IF_FIELD(suffix); { if (!mc_FLE2SuffixInsertSpec_parse(&out->suffix.value, &iter, status)) { goto fail; } out->suffix.set = true; } END_IF_FIELD; IF_FIELD(prefix); { if (!mc_FLE2PrefixInsertSpec_parse(&out->prefix.value, &iter, status)) { goto fail; } out->prefix.set = true; } END_IF_FIELD; } CHECK_HAS(v) CHECK_HAS(casef) CHECK_HAS(diacf) return true; fail: return false; } #undef ERROR_PREFIX libmongocrypt-1.19.0/src/mc-fle2-find-equality-payload-private-v2.h000066400000000000000000000031271521103432300250510ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_V2_H #define MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_V2_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t serverDerivedFromDataToken; // l int64_t maxContentionFactor; // cm } mc_FLE2FindEqualityPayloadV2_t; void mc_FLE2FindEqualityPayloadV2_init(mc_FLE2FindEqualityPayloadV2_t *payload); bool mc_FLE2FindEqualityPayloadV2_parse(mc_FLE2FindEqualityPayloadV2_t *out, const bson_t *in, mongocrypt_status_t *status); bool mc_FLE2FindEqualityPayloadV2_serialize(const mc_FLE2FindEqualityPayloadV2_t *payload, bson_t *out); void mc_FLE2FindEqualityPayloadV2_cleanup(mc_FLE2FindEqualityPayloadV2_t *payload); #endif /* MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_V2_H */ libmongocrypt-1.19.0/src/mc-fle2-find-equality-payload-private.h000066400000000000000000000030151521103432300245200ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_H #define MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t eccDerivedToken; // c _mongocrypt_buffer_t serverEncryptionToken; // e int64_t maxContentionFactor; // cm } mc_FLE2FindEqualityPayload_t; void mc_FLE2FindEqualityPayload_init(mc_FLE2FindEqualityPayload_t *payload); bool mc_FLE2FindEqualityPayload_parse(mc_FLE2FindEqualityPayload_t *out, const bson_t *in, mongocrypt_status_t *status); bool mc_FLE2FindEqualityPayload_serialize(const mc_FLE2FindEqualityPayload_t *payload, bson_t *out); void mc_FLE2FindEqualityPayload_cleanup(mc_FLE2FindEqualityPayload_t *payload); #endif /* MC_FLE2_FIND_EQUALITY_PAYLOAD_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-find-equality-payload-v2.c000066400000000000000000000134471521103432300234020ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-encryption-placeholder-private.h" #include "mc-fle2-find-equality-payload-private-v2.h" #include "mc-parse-utils-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" void mc_FLE2FindEqualityPayloadV2_init(mc_FLE2FindEqualityPayloadV2_t *payload) { memset(payload, 0, sizeof(mc_FLE2FindEqualityPayloadV2_t)); } void mc_FLE2FindEqualityPayloadV2_cleanup(mc_FLE2FindEqualityPayloadV2_t *payload) { BSON_ASSERT_PARAM(payload); _mongocrypt_buffer_cleanup(&payload->edcDerivedToken); _mongocrypt_buffer_cleanup(&payload->escDerivedToken); _mongocrypt_buffer_cleanup(&payload->serverDerivedFromDataToken); } #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR("Duplicate field '" #Name "' in payload bson"); \ goto fail; \ } \ has_##Name = true; #define END_IF_FIELD \ continue; \ } #define PARSE_BINARY(Name, Dest) \ IF_FIELD(Name) { \ if (!parse_bindata(BSON_SUBTYPE_BINARY, &iter, &out->Dest, status)) { \ goto fail; \ } \ } \ END_IF_FIELD #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR("Missing field '" #Name "' in payload"); \ goto fail; \ } else \ ((void)0) bool mc_FLE2FindEqualityPayloadV2_parse(mc_FLE2FindEqualityPayloadV2_t *out, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_d = false, has_s = false, has_l = false, has_cm = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); mc_FLE2FindEqualityPayloadV2_init(out); if (!bson_validate(in, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, in)) { CLIENT_ERR("invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); PARSE_BINARY(d, edcDerivedToken) PARSE_BINARY(s, escDerivedToken) PARSE_BINARY(l, serverDerivedFromDataToken) IF_FIELD(cm) { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR("Field 'cm' expected to hold an int64"); goto fail; } out->maxContentionFactor = bson_iter_int64(&iter); if (!mc_validate_contention(out->maxContentionFactor, status)) { goto fail; } } END_IF_FIELD } CHECK_HAS(d); CHECK_HAS(s); CHECK_HAS(l); CHECK_HAS(cm); return true; fail: return false; } #define APPEND_BINDATA(name, value) \ if (!_mongocrypt_buffer_append(&(value), out, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2FindEqualityPayloadV2_serialize(const mc_FLE2FindEqualityPayloadV2_t *payload, bson_t *out) { BSON_ASSERT_PARAM(payload); APPEND_BINDATA("d", payload->edcDerivedToken); APPEND_BINDATA("s", payload->escDerivedToken); APPEND_BINDATA("l", payload->serverDerivedFromDataToken); if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionFactor)) { return false; } return true; } #undef APPEND_BINDATA libmongocrypt-1.19.0/src/mc-fle2-find-equality-payload.c000066400000000000000000000137351521103432300230550ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-encryption-placeholder-private.h" #include "mc-fle2-find-equality-payload-private.h" #include "mc-parse-utils-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" void mc_FLE2FindEqualityPayload_init(mc_FLE2FindEqualityPayload_t *payload) { memset(payload, 0, sizeof(mc_FLE2FindEqualityPayload_t)); } void mc_FLE2FindEqualityPayload_cleanup(mc_FLE2FindEqualityPayload_t *payload) { BSON_ASSERT_PARAM(payload); _mongocrypt_buffer_cleanup(&payload->edcDerivedToken); _mongocrypt_buffer_cleanup(&payload->escDerivedToken); _mongocrypt_buffer_cleanup(&payload->eccDerivedToken); _mongocrypt_buffer_cleanup(&payload->serverEncryptionToken); } #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR("Duplicate field '" #Name "' in payload bson"); \ goto fail; \ } \ has_##Name = true; #define END_IF_FIELD \ continue; \ } #define PARSE_BINARY(Name, Dest) \ IF_FIELD(Name) { \ if (!parse_bindata(BSON_SUBTYPE_BINARY, &iter, &out->Dest, status)) { \ goto fail; \ } \ } \ END_IF_FIELD #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR("Missing field '" #Name "' in payload"); \ goto fail; \ } else \ ((void)0) bool mc_FLE2FindEqualityPayload_parse(mc_FLE2FindEqualityPayload_t *out, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_d = false, has_s = false, has_c = false, has_e = false, has_cm = false; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); mc_FLE2FindEqualityPayload_init(out); if (!bson_validate(in, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, in)) { CLIENT_ERR("invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); PARSE_BINARY(d, edcDerivedToken) PARSE_BINARY(s, escDerivedToken) PARSE_BINARY(c, eccDerivedToken) PARSE_BINARY(e, serverEncryptionToken) IF_FIELD(cm) { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR("Field 'cm' expected to hold an int64"); goto fail; } out->maxContentionFactor = bson_iter_int64(&iter); if (!mc_validate_contention(out->maxContentionFactor, status)) { goto fail; } } END_IF_FIELD } CHECK_HAS(d); CHECK_HAS(s); CHECK_HAS(c); CHECK_HAS(e); CHECK_HAS(cm); return true; fail: return false; } #define PAYLOAD_APPEND_BINDATA(name, value) \ if (!_mongocrypt_buffer_append(&(value), out, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2FindEqualityPayload_serialize(const mc_FLE2FindEqualityPayload_t *payload, bson_t *out) { BSON_ASSERT_PARAM(payload); PAYLOAD_APPEND_BINDATA("d", payload->edcDerivedToken); PAYLOAD_APPEND_BINDATA("s", payload->escDerivedToken); PAYLOAD_APPEND_BINDATA("c", payload->eccDerivedToken); PAYLOAD_APPEND_BINDATA("e", payload->serverEncryptionToken); if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionFactor)) { return false; } return true; } #undef PAYLOAD_APPEND_BINDATA libmongocrypt-1.19.0/src/mc-fle2-find-range-payload-private-v2.h000066400000000000000000000074771521103432300243240ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_V2_H #define MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_V2_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" #include "mc-array-private.h" #include "mc-fle2-range-operator-private.h" #include "mc-optional-private.h" /** FLE2FindRangePayloadEdgesInfoV2 represents the token information for a range * find query. It is encoded inside an FLE2FindRangePayloadV2. */ typedef struct { mc_array_t edgeFindTokenSetArray; // g int64_t maxContentionFactor; // cm } mc_FLE2FindRangePayloadEdgesInfoV2_t; /** * FLE2FindRangePayloadV2 represents an FLE2 payload of a range indexed field to * query. It is created client side. * * FLE2FindRangePayloadV2 has the following data layout: * * struct { * uint8_t fle_blob_subtype = 13; * uint8_t bson[]; * } FLE2FindRangePayloadV2; * * bson is a BSON document of this form: * payload: * g: array // Array of Edges * cm: // Queryable Encryption max counter * payloadId: // Payload ID. * firstOperator: * secondOperator: * sp: optional // Sparsity. * pn: optional // Precision. * tf: optional // Trim Factor. * mn: optional // Index Min. * mx: optional // Index Max. */ typedef struct { struct { mc_FLE2FindRangePayloadEdgesInfoV2_t value; bool set; } payload; // payloadId Id of payload - must be paired with another payload. int32_t payloadId; // firstOperator represents the first query operator for which this payload // was generated. mc_FLE2RangeOperator_t firstOperator; // secondOperator represents the second query operator for which this payload // was generated. Only populated for two-sided ranges. It is 0 if unset. mc_FLE2RangeOperator_t secondOperator; mc_optional_int64_t sparsity; // sp mc_optional_int32_t precision; // pn mc_optional_int32_t trimFactor; // tf bson_value_t indexMin; // mn bson_value_t indexMax; // mx } mc_FLE2FindRangePayloadV2_t; // `mc_FLE2FindRangePayloadV2_t` inherits extended alignment from libbson. To dynamically allocate, use aligned // allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2FindRangePayloadV2_t, BSON_ALIGNOF(mc_FLE2FindRangePayloadV2_t) >= BSON_ALIGNOF(bson_value_t)); /** * EdgeFindTokenSetV2 is the following BSON document: * d: // EDCDerivedFromDataToken * s: // ESCDerivedFromDataToken * l: // ServerDerivedFromDataToken * * Instances of mc_EdgeFindTokenSetV2_t are expected to be owned by * mc_FLE2FindRangePayloadV2_t and are freed in * mc_FLE2FindRangePayloadV2_cleanup. */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t serverDerivedFromDataToken; // l } mc_EdgeFindTokenSetV2_t; void mc_FLE2FindRangePayloadV2_init(mc_FLE2FindRangePayloadV2_t *payload); bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out); void mc_FLE2FindRangePayloadV2_cleanup(mc_FLE2FindRangePayloadV2_t *payload); #endif /* MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_V2_H */ libmongocrypt-1.19.0/src/mc-fle2-find-range-payload-private.h000066400000000000000000000063461521103432300237710ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_H #define MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" #include "mc-array-private.h" #include "mc-fle2-range-operator-private.h" /** FLE2FindRangePayloadEdgesInfo represents the token information for a range * find query. It is encoded inside an FLE2FindRangePayload. See * https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_field_schema.idl * for the representation in the MongoDB server. */ typedef struct { mc_array_t edgeFindTokenSetArray; // g _mongocrypt_buffer_t serverEncryptionToken; // e int64_t maxContentionFactor; // cm } mc_FLE2FindRangePayloadEdgesInfo_t; /** * FLE2FindRangePayload represents an FLE2 payload of a range indexed field to * query. It is created client side. * * FLE2FindRangePayload has the following data layout: * * struct { * uint8_t fle_blob_subtype = 10; * uint8_t bson[]; * } FLE2FindRangePayload; * * bson is a BSON document of this form: * g: array // Array of Edges * e: // ServerDataEncryptionLevel1Token * cm: // Queryable Encryption max contentionFactor */ typedef struct { struct { mc_FLE2FindRangePayloadEdgesInfo_t value; bool set; } payload; // payloadId Id of payload - must be paired with another payload. int32_t payloadId; // firstOperator represents the first query operator for which this payload // was generated. mc_FLE2RangeOperator_t firstOperator; // secondOperator represents the second query operator for which this payload // was generated. Only populated for two-sided ranges. It is 0 if unset. mc_FLE2RangeOperator_t secondOperator; } mc_FLE2FindRangePayload_t; /** * EdgeFindTokenSet is the following BSON document: * d: // EDCDerivedFromDataTokenAndContentionFactor * s: // ESCDerivedFromDataTokenAndContentionFactor * c: // ECCDerivedFromDataTokenAndContentionFactor * * Instances of mc_EdgeFindTokenSet_t are expected to be owned by * mc_FLE2FindRangePayload_t and are freed in * mc_FLE2FindRangePayload_cleanup. */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t eccDerivedToken; // c } mc_EdgeFindTokenSet_t; void mc_FLE2FindRangePayload_init(mc_FLE2FindRangePayload_t *payload); bool mc_FLE2FindRangePayload_serialize(const mc_FLE2FindRangePayload_t *payload, bson_t *out); void mc_FLE2FindRangePayload_cleanup(mc_FLE2FindRangePayload_t *payload); #endif /* MC_FLE2_FIND_RANGE_PAYLOAD_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-find-range-payload-v2.c000066400000000000000000000136361521103432300226410ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-find-range-payload-private-v2.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" void mc_FLE2FindRangePayloadV2_init(mc_FLE2FindRangePayloadV2_t *payload) { BSON_ASSERT_PARAM(payload); *payload = (mc_FLE2FindRangePayloadV2_t){{{{0}}}}; _mc_array_init(&payload->payload.value.edgeFindTokenSetArray, sizeof(mc_EdgeFindTokenSetV2_t)); } static void mc_EdgeFindTokenSetV2_cleanup(mc_EdgeFindTokenSetV2_t *etc) { if (!etc) { return; } _mongocrypt_buffer_cleanup(&etc->edcDerivedToken); _mongocrypt_buffer_cleanup(&etc->escDerivedToken); _mongocrypt_buffer_cleanup(&etc->serverDerivedFromDataToken); } void mc_FLE2FindRangePayloadV2_cleanup(mc_FLE2FindRangePayloadV2_t *payload) { if (!payload) { return; } // Free all EdgeFindTokenSet entries. for (size_t i = 0; i < payload->payload.value.edgeFindTokenSetArray.len; i++) { mc_EdgeFindTokenSetV2_t entry = _mc_array_index(&payload->payload.value.edgeFindTokenSetArray, mc_EdgeFindTokenSetV2_t, i); mc_EdgeFindTokenSetV2_cleanup(&entry); } _mc_array_destroy(&payload->payload.value.edgeFindTokenSetArray); } #define APPEND_BINDATA(out, name, value) \ if (!_mongocrypt_buffer_append(&(value), out, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2FindRangePayloadV2_serialize(const mc_FLE2FindRangePayloadV2_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); // Append "payload" if this is not a stub. if (payload->payload.set) { bson_t payload_bson; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "payload", &payload_bson)) { return false; } // Append "payload.g" array of EdgeTokenSets. bson_t g_bson; if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(&payload_bson, "g", &g_bson)) { return false; } uint32_t g_index = 0; for (size_t i = 0; i < payload->payload.value.edgeFindTokenSetArray.len; i++) { mc_EdgeFindTokenSetV2_t etc = _mc_array_index(&payload->payload.value.edgeFindTokenSetArray, mc_EdgeFindTokenSetV2_t, i); bson_t etc_bson; const char *g_index_string; char storage[16]; bson_uint32_to_string(g_index, &g_index_string, storage, sizeof(storage)); if (!BSON_APPEND_DOCUMENT_BEGIN(&g_bson, g_index_string, &etc_bson)) { return false; } etc.edcDerivedToken.subtype = BSON_SUBTYPE_BINARY; etc.escDerivedToken.subtype = BSON_SUBTYPE_BINARY; etc.serverDerivedFromDataToken.subtype = BSON_SUBTYPE_BINARY; APPEND_BINDATA(&etc_bson, "d", etc.edcDerivedToken); APPEND_BINDATA(&etc_bson, "s", etc.escDerivedToken); APPEND_BINDATA(&etc_bson, "l", etc.serverDerivedFromDataToken); if (!bson_append_document_end(&g_bson, &etc_bson)) { return false; } if (g_index == UINT32_MAX) { break; } g_index++; } if (!bson_append_array_end(&payload_bson, &g_bson)) { return false; } // Append "payload.cm". if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionFactor)) { return false; } if (!bson_append_document_end(out, &payload_bson)) { return false; } } // Append "payloadId" if (!BSON_APPEND_INT32(out, "payloadId", payload->payloadId)) { return false; } // Append "firstOperator". if (!BSON_APPEND_INT32(out, "firstOperator", payload->firstOperator)) { return false; } // Append "secondOperator" if present. if (payload->secondOperator != FLE2RangeOperator_kNone && !BSON_APPEND_INT32(out, "secondOperator", payload->secondOperator)) { return false; } // Encode parameters that were used to generate the mincover. // The crypto parameters are all optionally set. Find payloads may come in pairs (a lower and upper bound). // One of the pair includes the mincover. The other payload was not generated with crypto parameters. if (payload->sparsity.set) { if (!BSON_APPEND_INT64(out, "sp", payload->sparsity.value)) { return false; } } if (payload->precision.set) { if (!BSON_APPEND_INT32(out, "pn", payload->precision.value)) { return false; } } if (payload->trimFactor.set) { if (!BSON_APPEND_INT32(out, "tf", payload->trimFactor.value)) { return false; } } if (payload->indexMin.value_type != BSON_TYPE_EOD) { if (!BSON_APPEND_VALUE(out, "mn", &payload->indexMin)) { return false; } } if (payload->indexMax.value_type != BSON_TYPE_EOD) { if (!BSON_APPEND_VALUE(out, "mx", &payload->indexMax)) { return false; } } return true; } #undef APPEND_BINDATA libmongocrypt-1.19.0/src/mc-fle2-find-range-payload.c000066400000000000000000000117621521103432300223120ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-find-range-payload-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" void mc_FLE2FindRangePayload_init(mc_FLE2FindRangePayload_t *payload) { BSON_ASSERT_PARAM(payload); *payload = (mc_FLE2FindRangePayload_t){{{{0}}}}; _mc_array_init(&payload->payload.value.edgeFindTokenSetArray, sizeof(mc_EdgeFindTokenSet_t)); } static void mc_EdgeFindTokenSet_cleanup(mc_EdgeFindTokenSet_t *etc) { if (!etc) { return; } _mongocrypt_buffer_cleanup(&etc->edcDerivedToken); _mongocrypt_buffer_cleanup(&etc->escDerivedToken); _mongocrypt_buffer_cleanup(&etc->eccDerivedToken); } void mc_FLE2FindRangePayload_cleanup(mc_FLE2FindRangePayload_t *payload) { if (!payload) { return; } _mongocrypt_buffer_cleanup(&payload->payload.value.serverEncryptionToken); // Free all EdgeFindTokenSet entries. for (size_t i = 0; i < payload->payload.value.edgeFindTokenSetArray.len; i++) { mc_EdgeFindTokenSet_t entry = _mc_array_index(&payload->payload.value.edgeFindTokenSetArray, mc_EdgeFindTokenSet_t, i); mc_EdgeFindTokenSet_cleanup(&entry); } _mc_array_destroy(&payload->payload.value.edgeFindTokenSetArray); } #define APPEND_BINDATA(out, name, value) \ if (!_mongocrypt_buffer_append(&(value), out, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2FindRangePayload_serialize(const mc_FLE2FindRangePayload_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); // Append "payload" if this is not a stub. if (payload->payload.set) { bson_t payload_bson; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "payload", &payload_bson)) { return false; } // Append "payload.g" array of EdgeTokenSets. bson_t g_bson; if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(&payload_bson, "g", &g_bson)) { return false; } uint32_t g_index = 0; for (size_t i = 0; i < payload->payload.value.edgeFindTokenSetArray.len; i++) { mc_EdgeFindTokenSet_t etc = _mc_array_index(&payload->payload.value.edgeFindTokenSetArray, mc_EdgeFindTokenSet_t, i); bson_t etc_bson; const char *g_index_string; char storage[16]; bson_uint32_to_string(g_index, &g_index_string, storage, sizeof(storage)); if (!BSON_APPEND_DOCUMENT_BEGIN(&g_bson, g_index_string, &etc_bson)) { return false; } etc.edcDerivedToken.subtype = BSON_SUBTYPE_BINARY; etc.escDerivedToken.subtype = BSON_SUBTYPE_BINARY; etc.eccDerivedToken.subtype = BSON_SUBTYPE_BINARY; APPEND_BINDATA(&etc_bson, "d", etc.edcDerivedToken); APPEND_BINDATA(&etc_bson, "s", etc.escDerivedToken); APPEND_BINDATA(&etc_bson, "c", etc.eccDerivedToken); if (!bson_append_document_end(&g_bson, &etc_bson)) { return false; } if (g_index == UINT32_MAX) { break; } g_index++; } if (!bson_append_array_end(&payload_bson, &g_bson)) { return false; } // Append "payload.e" and "payload.cm". APPEND_BINDATA(&payload_bson, "e", payload->payload.value.serverEncryptionToken); if (!BSON_APPEND_INT64(&payload_bson, "cm", payload->payload.value.maxContentionFactor)) { return false; } if (!bson_append_document_end(out, &payload_bson)) { return false; } } // Append "payloadId" if (!BSON_APPEND_INT32(out, "payloadId", payload->payloadId)) { return false; } // Append "firstOperator". if (!BSON_APPEND_INT32(out, "firstOperator", payload->firstOperator)) { return false; } // Append "secondOperator" if present. if (payload->secondOperator != FLE2RangeOperator_kNone && !BSON_APPEND_INT32(out, "secondOperator", payload->secondOperator)) { return false; } return true; } #undef APPEND_BINDATA libmongocrypt-1.19.0/src/mc-fle2-find-text-payload-private.h000066400000000000000000000110461521103432300236520ustar00rootroot00000000000000/* * Copyright 2025-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. */ #ifndef MC_FLE2_FIND_TEXT_PAYLOAD_PRIVATE_H #define MC_FLE2_FIND_TEXT_PAYLOAD_PRIVATE_H #include "mc-fle2-encryption-placeholder-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" #define DEF_TEXT_SEARCH_FIND_TOKEN_SET(Type) \ typedef struct { \ _mongocrypt_buffer_t edcDerivedToken; \ _mongocrypt_buffer_t escDerivedToken; \ _mongocrypt_buffer_t serverDerivedFromDataToken; \ } mc_Text##Type##FindTokenSet_t; DEF_TEXT_SEARCH_FIND_TOKEN_SET(Exact); DEF_TEXT_SEARCH_FIND_TOKEN_SET(Substring); DEF_TEXT_SEARCH_FIND_TOKEN_SET(Suffix); DEF_TEXT_SEARCH_FIND_TOKEN_SET(Prefix); typedef struct { struct { mc_TextExactFindTokenSet_t value; bool set; } exact; // e struct { mc_TextSubstringFindTokenSet_t value; bool set; } substring; // s struct { mc_TextSuffixFindTokenSet_t value; bool set; } suffix; // u struct { mc_TextPrefixFindTokenSet_t value; bool set; } prefix; // p } mc_TextSearchFindTokenSets_t; /** * FLE2FindTextPayload represents an FLE2 payload of a substring/suffix/prefix indexed field to * query. It is created client side. * * FLE2FindTextPayload has the following data layout: * * struct { * uint8_t fle_blob_subtype = 18; * uint8_t bson[]; * } FLE2FindTextPayload; * * bson is a BSON document of this form: * { * ts: { * e: optional // required tokens if doing full string match * s: optional // required tokens if doing substring match * u: optional // required tokens if doing suffix match * p: optional // required tokens if doing prefix match * } * cm: // Queryable Encryption max contentionFactor * cf: // case folding parameter * df: // diacritic folding parameter * ss: optional // substring-indexing parameters (if applicable) * fs: optional // suffix-indexing parameters (if applicable) * ps: optional // prefix-indexing parameters (if applicable) * } * where for each of T in [Exact, Substring, Suffix, Prefix], TextFindTokenSet is a document of form: * { * d: // EDCTextDerivedFromDataToken * s: // ESCTextDerivedFromDataToken * l: // ServerTextDerivedFromDataToken * } */ typedef struct { mc_TextSearchFindTokenSets_t tokenSets; // ts int64_t maxContentionFactor; // cm bool caseFold; // cf bool diacriticFold; // df struct { mc_FLE2SubstringInsertSpec_t value; bool set; } substringSpec; // ss struct { mc_FLE2SuffixInsertSpec_t value; bool set; } suffixSpec; // fs struct { mc_FLE2PrefixInsertSpec_t value; bool set; } prefixSpec; // ps } mc_FLE2FindTextPayload_t; void mc_FLE2FindTextPayload_init(mc_FLE2FindTextPayload_t *payload); bool mc_FLE2FindTextPayload_parse(mc_FLE2FindTextPayload_t *out, const bson_t *in, mongocrypt_status_t *status); bool mc_FLE2FindTextPayload_serialize(const mc_FLE2FindTextPayload_t *payload, bson_t *out); void mc_FLE2FindTextPayload_cleanup(mc_FLE2FindTextPayload_t *payload); #endif /* MC_FLE2_FIND_TEXT_PAYLOAD_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-find-text-payload.c000066400000000000000000000446641521103432300222110ustar00rootroot00000000000000/* * Copyright 2025-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 #include #include "mc-fle2-encryption-placeholder-private.h" #include "mc-fle2-find-text-payload-private.h" #include "mc-parse-utils-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-util-private.h" #include "mongocrypt.h" #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR("Error parsing %s: Duplicate field '" #Name "'", class_name); \ goto fail; \ } \ has_##Name = true; #define END_IF_FIELD \ continue; \ } #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR("Error parsing %s: Missing required field '" #Name "'", class_name); \ goto fail; \ } #define PARSE_BINARY(Name, Dest) \ IF_FIELD(Name) { \ if (!parse_bindata(BSON_SUBTYPE_BINARY, &iter, Dest, status)) { \ goto fail; \ } \ } \ END_IF_FIELD typedef struct { _mongocrypt_buffer_t *edcDerivedToken; _mongocrypt_buffer_t *escDerivedToken; _mongocrypt_buffer_t *serverDerivedToken; } mc_TextFindTokenSetIndirection_t; typedef struct { const _mongocrypt_buffer_t *edcDerivedToken; const _mongocrypt_buffer_t *escDerivedToken; const _mongocrypt_buffer_t *serverDerivedToken; } mc_TextFindTokenSetIndirectionConst_t; /* Cleanup code common to all mc_TextFindTokenSet_t types. */ static void mc_TextFindTokenSetIndirection_cleanup(mc_TextFindTokenSetIndirection_t ts) { _mongocrypt_buffer_cleanup(ts.edcDerivedToken); _mongocrypt_buffer_cleanup(ts.escDerivedToken); _mongocrypt_buffer_cleanup(ts.serverDerivedToken); } /* Serialization code common to all mc_TextFindTokenSet_t types. */ static bool mc_TextFindTokenSetIndirection_serialize(mc_TextFindTokenSetIndirectionConst_t ts, bson_t *parent, const char *field_name) { BSON_ASSERT_PARAM(ts.edcDerivedToken); BSON_ASSERT_PARAM(ts.escDerivedToken); BSON_ASSERT_PARAM(ts.serverDerivedToken); BSON_ASSERT_PARAM(parent); BSON_ASSERT_PARAM(field_name); bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(parent, field_name, &child)) { return false; } if (!_mongocrypt_buffer_append(ts.edcDerivedToken, &child, "d", -1)) { return false; } if (!_mongocrypt_buffer_append(ts.escDerivedToken, &child, "s", -1)) { return false; } if (!_mongocrypt_buffer_append(ts.serverDerivedToken, &child, "l", -1)) { return false; } if (!bson_append_document_end(parent, &child)) { return false; } return true; } /* Parsing code common to all mc_TextFindTokenSet_t types. */ static bool mc_TextFindTokenSetIndirection_parse(mc_TextFindTokenSetIndirection_t out, const char *class_name, const bson_iter_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out.edcDerivedToken); BSON_ASSERT_PARAM(out.escDerivedToken); BSON_ASSERT_PARAM(out.serverDerivedToken); BSON_ASSERT_PARAM(in); bson_iter_t iter; bool has_d = false, has_s = false, has_l = false; iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR("Error parsing %s: field expected to be a document, but got %s", class_name, mc_bson_type_to_string(bson_iter_type(&iter))); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); PARSE_BINARY(d, out.edcDerivedToken); PARSE_BINARY(s, out.escDerivedToken); PARSE_BINARY(l, out.serverDerivedToken); CLIENT_ERR("Error parsing %s: Unrecognized field '%s'", class_name, field); goto fail; } CHECK_HAS(d); CHECK_HAS(s); CHECK_HAS(l); return true; fail: return false; } #define INDIRECT(type, ts) \ (type) { \ .edcDerivedToken = &(ts).edcDerivedToken, .escDerivedToken = &(ts).escDerivedToken, \ .serverDerivedToken = &(ts).serverDerivedFromDataToken \ } #define INDIRECT_TOKENSET(ts) INDIRECT(mc_TextFindTokenSetIndirection_t, ts) #define INDIRECT_TOKENSET_CONST(ts) INDIRECT(mc_TextFindTokenSetIndirectionConst_t, ts) #define DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP(Type) \ static void mc_Text##Type##FindTokenSet_cleanup(mc_Text##Type##FindTokenSet_t *fts) { \ if (fts) { \ mc_TextFindTokenSetIndirection_cleanup(INDIRECT_TOKENSET(*fts)); \ } \ } DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP(Exact); DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP(Substring); DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP(Suffix); DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP(Prefix); #define DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE(Type) \ static bool mc_Text##Type##FindTokenSet_serialize(bson_t *parent, \ const char *field_name, \ const mc_Text##Type##FindTokenSet_t *ts) { \ BSON_ASSERT_PARAM(ts); \ return mc_TextFindTokenSetIndirection_serialize(INDIRECT_TOKENSET_CONST(*ts), parent, field_name); \ } DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE(Exact) DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE(Substring) DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE(Suffix) DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE(Prefix) #define DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE(Type) \ static bool mc_Text##Type##FindTokenSet_parse(mc_Text##Type##FindTokenSet_t *out, \ bson_iter_t *in, \ mongocrypt_status_t *status) { \ BSON_ASSERT_PARAM(out); \ return mc_TextFindTokenSetIndirection_parse(INDIRECT_TOKENSET(*out), "Text" #Type "FindTokenSet", in, status); \ } DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE(Exact) DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE(Substring) DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE(Suffix) DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE(Prefix) #undef DEF_TEXT_SEARCH_FIND_TOKEN_SET_CLEANUP #undef DEF_TEXT_SEARCH_FIND_TOKEN_SET_SERIALIZE #undef DEF_TEXT_SEARCH_FIND_TOKEN_SET_PARSE #undef INDIRECT_TOKENSET_CONST #undef INDIRECT_TOKENSET #undef INDIRECT void mc_FLE2FindTextPayload_init(mc_FLE2FindTextPayload_t *payload) { BSON_ASSERT_PARAM(payload); memset(payload, 0, sizeof(*payload)); } void mc_FLE2FindTextPayload_cleanup(mc_FLE2FindTextPayload_t *payload) { if (!payload) { return; } mc_TextExactFindTokenSet_cleanup(&payload->tokenSets.exact.value); mc_TextSubstringFindTokenSet_cleanup(&payload->tokenSets.substring.value); mc_TextSuffixFindTokenSet_cleanup(&payload->tokenSets.suffix.value); mc_TextPrefixFindTokenSet_cleanup(&payload->tokenSets.prefix.value); } static bool mc_TextSearchFindTokenSets_parse(mc_TextSearchFindTokenSets_t *out, const bson_iter_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); bson_iter_t iter; bool has_e = false, has_s = false, has_u = false, has_p = false; uint8_t field_count = 0; const char *class_name = "TextSearchFindTokenSets"; iter = *in; if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR("Error parsing %s: field expected to be a document, but got %s", class_name, mc_bson_type_to_string(bson_iter_type(&iter))); return false; } bson_iter_recurse(&iter, &iter); while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(e) { if (!mc_TextExactFindTokenSet_parse(&out->exact.value, &iter, status)) { goto fail; } out->exact.set = true; field_count++; } END_IF_FIELD IF_FIELD(s) { if (!mc_TextSubstringFindTokenSet_parse(&out->substring.value, &iter, status)) { goto fail; } out->substring.set = true; field_count++; } END_IF_FIELD IF_FIELD(u) { if (!mc_TextSuffixFindTokenSet_parse(&out->suffix.value, &iter, status)) { goto fail; } out->suffix.set = true; field_count++; } END_IF_FIELD IF_FIELD(p) { if (!mc_TextPrefixFindTokenSet_parse(&out->prefix.value, &iter, status)) { goto fail; } out->prefix.set = true; field_count++; } END_IF_FIELD CLIENT_ERR("Error parsing %s: Unrecognized field '%s'", class_name, field); goto fail; } if (!field_count) { CLIENT_ERR("Error parsing %s: exactly one optional field is required", class_name); goto fail; } else if (field_count > 1) { CLIENT_ERR("Error parsing %s: cannot have multiple optional fields present", class_name); goto fail; } return true; fail: return false; } bool mc_FLE2FindTextPayload_parse(mc_FLE2FindTextPayload_t *out, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter; const char *class_name = "FLE2FindTextPayload"; bool has_ts = false, has_cm = false, has_cf = false, has_df = false; // required fields bool has_ss = false, has_fs = false, has_ps = false; // optional fields BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); mc_FLE2FindTextPayload_init(out); if (!bson_validate(in, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, in)) { CLIENT_ERR("invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); const char *typestr = mc_bson_type_to_string(bson_iter_type(&iter)); BSON_ASSERT(field); IF_FIELD(ts) { if (!mc_TextSearchFindTokenSets_parse(&out->tokenSets, &iter, status)) { return false; } } END_IF_FIELD IF_FIELD(cm) { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR("Error parsing %s: Field 'cm' expected to be int64, but got %s", class_name, typestr); goto fail; } out->maxContentionFactor = bson_iter_int64(&iter); if (!mc_validate_contention(out->maxContentionFactor, status)) { goto fail; } } END_IF_FIELD IF_FIELD(cf) { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR("Error parsing %s: Field 'cf' expected to be boolean, but got %s", class_name, typestr); goto fail; } out->caseFold = bson_iter_bool(&iter); } END_IF_FIELD IF_FIELD(df) { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR("Error parsing %s: Field 'df' expected to be boolean, but got %s", class_name, typestr); goto fail; } out->diacriticFold = bson_iter_bool(&iter); } END_IF_FIELD IF_FIELD(ss) { if (!mc_FLE2SubstringInsertSpec_parse(&out->substringSpec.value, &iter, status)) { goto fail; } out->substringSpec.set = true; } END_IF_FIELD IF_FIELD(fs) { if (!mc_FLE2SuffixInsertSpec_parse(&out->suffixSpec.value, &iter, status)) { goto fail; } out->suffixSpec.set = true; } END_IF_FIELD IF_FIELD(ps) { if (!mc_FLE2PrefixInsertSpec_parse(&out->prefixSpec.value, &iter, status)) { goto fail; } out->prefixSpec.set = true; } END_IF_FIELD CLIENT_ERR("Error parsing %s: Unrecognized field '%s'", class_name, field); goto fail; } CHECK_HAS(ts); CHECK_HAS(cm); CHECK_HAS(cf); CHECK_HAS(df); return true; fail: return false; } bool mc_FLE2FindTextPayload_serialize(const mc_FLE2FindTextPayload_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); // Append token sets "ts" { bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "ts", &child)) { return false; } // Append "e" if present if (payload->tokenSets.exact.set) { mc_TextExactFindTokenSet_serialize(&child, "e", &payload->tokenSets.exact.value); } // Append "s" if present if (payload->tokenSets.substring.set) { mc_TextSubstringFindTokenSet_serialize(&child, "s", &payload->tokenSets.substring.value); } // Append "u" if present if (payload->tokenSets.suffix.set) { mc_TextSuffixFindTokenSet_serialize(&child, "u", &payload->tokenSets.suffix.value); } // Append "p" if present if (payload->tokenSets.prefix.set) { mc_TextPrefixFindTokenSet_serialize(&child, "p", &payload->tokenSets.prefix.value); } if (!bson_append_document_end(out, &child)) { return false; } } // Append "cm". if (!BSON_APPEND_INT64(out, "cm", payload->maxContentionFactor)) { return false; } // Append "cf". if (!BSON_APPEND_BOOL(out, "cf", payload->caseFold)) { return false; } // Append "df". if (!BSON_APPEND_BOOL(out, "df", payload->diacriticFold)) { return false; } // Append "ss" if present. if (payload->substringSpec.set) { bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "ss", &child)) { return false; } if (!BSON_APPEND_INT32(&child, "mlen", (int32_t)payload->substringSpec.value.mlen)) { return false; } if (!BSON_APPEND_INT32(&child, "ub", (int32_t)payload->substringSpec.value.ub)) { return false; } if (!BSON_APPEND_INT32(&child, "lb", (int32_t)payload->substringSpec.value.lb)) { return false; } if (!bson_append_document_end(out, &child)) { return false; } } // Append "fs" if present. if (payload->suffixSpec.set) { bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "fs", &child)) { return false; } if (!BSON_APPEND_INT32(&child, "ub", (int32_t)payload->suffixSpec.value.ub)) { return false; } if (!BSON_APPEND_INT32(&child, "lb", (int32_t)payload->suffixSpec.value.lb)) { return false; } if (!bson_append_document_end(out, &child)) { return false; } } // Append "ps" if present. if (payload->prefixSpec.set) { bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "ps", &child)) { return false; } if (!BSON_APPEND_INT32(&child, "ub", (int32_t)payload->prefixSpec.value.ub)) { return false; } if (!BSON_APPEND_INT32(&child, "lb", (int32_t)payload->prefixSpec.value.lb)) { return false; } if (!bson_append_document_end(out, &child)) { return false; } } return true; } libmongocrypt-1.19.0/src/mc-fle2-insert-update-payload-private-v2.h000066400000000000000000000200021521103432300250510ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_V2_H #define MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_V2_H #include #include "mc-array-private.h" #include "mc-optional-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" /* * The *TokenSet types corresponds to the following BSON document: * d: // EDC derived from data & contention factor token * s: // ESC derived from data & contention factor token * l: // server derived from data token * p: // encrypted token for compaction */ #define DEF_TEXT_SEARCH_TOKEN_SET(Type) \ typedef struct { \ _mongocrypt_buffer_t edcDerivedToken; /* d */ \ _mongocrypt_buffer_t escDerivedToken; /* s */ \ _mongocrypt_buffer_t serverDerivedFromDataToken; /* l */ \ _mongocrypt_buffer_t encryptedTokens; /* p */ \ } mc_Text##Type##TokenSet_t; \ void mc_Text##Type##TokenSet_init(mc_Text##Type##TokenSet_t *); \ void mc_Text##Type##TokenSet_cleanup(mc_Text##Type##TokenSet_t *); \ void mc_Text##Type##TokenSet_shallow_copy(const mc_Text##Type##TokenSet_t *src, mc_Text##Type##TokenSet_t *dest) DEF_TEXT_SEARCH_TOKEN_SET(Exact); DEF_TEXT_SEARCH_TOKEN_SET(Substring); DEF_TEXT_SEARCH_TOKEN_SET(Suffix); DEF_TEXT_SEARCH_TOKEN_SET(Prefix); /** * TextSearchTokenSets corresponds to following BSON document: * * e: // Holds tokens for exact string search * s: array // Holds tokens for substring search * u: array // Holds tokens for suffix search * p: array // Holds tokens for prefix search */ typedef struct { mc_TextExactTokenSet_t exact; // e mc_array_t substringArray; // s mc_array_t suffixArray; // u mc_array_t prefixArray; // p } mc_TextSearchTokenSets_t; void mc_TextSearchTokenSets_init(mc_TextSearchTokenSets_t *); void mc_TextSearchTokenSets_cleanup(mc_TextSearchTokenSets_t *); /** * FLE2InsertUpdatePayloadV2 represents an FLE2 payload of an indexed field to * insert or update. It is created client side. * * FLE2InsertUpdatePayloadV2 has the following data layout: * * struct { * uint8_t fle_blob_subtype = 11; * uint8_t bson[]; * } FLE2InsertUpdatePayloadV2; * * bson is a BSON document of this form: * d: // EDCDerivedFromDataTokenAndContentionFactor * s: // ESCDerivedFromDataTokenAndContentionFactor * p: // Encrypted Tokens * u: // Index KeyId * t: // Encrypted type * v: // Encrypted value * e: // ServerDataEncryptionLevel1Token * l: // ServerDerivedFromDataToken * k: // Randomly sampled contention factor value * g: array // Array of Edges. Only included for range payloads. * sp: optional // Sparsity. Only included for range payloads. * pn: optional // Precision. Only included for range payloads. * tf: optional // Trim Factor. Only included for range payloads. * mn: optional // Index Min. Only included for range payloads. * mx: optional // Index Max. Only included for range payloads. * b: optional // Only included for text payloads. * * p is the result of: * Encrypt( * key=ECOCToken, * plaintext=( * ESCDerivedFromDataTokenAndContentionFactor) * ) * * v is the result of: * UserKeyId || EncryptAEAD( * key=UserKey, * plaintext=value * associated_data=UserKeyId) */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t encryptedTokens; // p _mongocrypt_buffer_t indexKeyId; // u bson_type_t valueType; // t _mongocrypt_buffer_t value; // v _mongocrypt_buffer_t serverEncryptionToken; // e _mongocrypt_buffer_t serverDerivedFromDataToken; // l int64_t contentionFactor; // k mc_array_t edgeTokenSetArray; // g mc_optional_int64_t sparsity; // sp mc_optional_int32_t precision; // pn mc_optional_int32_t trimFactor; // tf bson_value_t indexMin; // mn bson_value_t indexMax; // mx struct { mc_TextSearchTokenSets_t tsts; bool set; } textSearchTokenSets; // b _mongocrypt_buffer_t plaintext; _mongocrypt_buffer_t userKeyId; } mc_FLE2InsertUpdatePayloadV2_t; // `mc_FLE2InsertUpdatePayloadV2_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2InsertUpdatePayloadV2_t, BSON_ALIGNOF(mc_FLE2InsertUpdatePayloadV2_t) >= BSON_ALIGNOF(bson_value_t)); /** * EdgeTokenSetV2 is the following BSON document: * d: // EDCDerivedFromDataTokenAndContentionFactor * s: // ESCDerivedFromDataTokenAndContentionFactor * l: // ServerDerivedFromDataToken * p: // Encrypted Tokens * * Instances of mc_EdgeTokenSetV2_t are expected to be owned by * mc_FLE2InsertUpdatePayloadV2_t and are freed in * mc_FLE2InsertUpdatePayloadV2_cleanup. */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t serverDerivedFromDataToken; // l _mongocrypt_buffer_t encryptedTokens; // p } mc_EdgeTokenSetV2_t; void mc_FLE2InsertUpdatePayloadV2_init(mc_FLE2InsertUpdatePayloadV2_t *payload); bool mc_FLE2InsertUpdatePayloadV2_parse(mc_FLE2InsertUpdatePayloadV2_t *out, const _mongocrypt_buffer_t *in, mongocrypt_status_t *status); /* mc_FLE2InsertUpdatePayloadV2_decrypt decrypts ciphertext. * Returns NULL and sets @status on error. It is an error to call before * mc_FLE2InsertUpdatePayloadV2_parse. */ const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayloadV2_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2InsertUpdatePayloadV2_t *iup, const _mongocrypt_buffer_t *user_key, mongocrypt_status_t *status); bool mc_FLE2InsertUpdatePayloadV2_serialize(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out); bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out); bool mc_FLE2InsertUpdatePayloadV2_serializeForTextSearch(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out); void mc_FLE2InsertUpdatePayloadV2_cleanup(mc_FLE2InsertUpdatePayloadV2_t *payload); #endif /* MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_V2_H */ libmongocrypt-1.19.0/src/mc-fle2-insert-update-payload-private.h000066400000000000000000000104021521103432300245270ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_H #define MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_H #include #include "mc-array-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mongocrypt.h" /** * FLE2InsertUpdatePayload represents an FLE2 payload of an indexed field to * insert or update. It is created client side. * * FLE2InsertUpdatePayload has the following data layout: * * struct { * uint8_t fle_blob_subtype = 4; * uint8_t bson[]; * } FLE2InsertUpdatePayload; * * bson is a BSON document of this form: * d: // EDCDerivedFromDataTokenAndContentionFactor * s: // ESCDerivedFromDataTokenAndContentionFactor * c: // ECCDerivedFromDataTokenAndContentionFactor * p: // Encrypted Tokens * u: // Index KeyId * t: // Encrypted type * v: // Encrypted value * e: // ServerDataEncryptionLevel1Token * g: array // Array of Edges * * p is the result of: * Encrypt( * key=ECOCToken, * plaintext=( * ESCDerivedFromDataTokenAndContentionFactor || * ECCDerivedFromDataTokenAndContentionFactor) * ) * * v is the result of: * UserKeyId || EncryptAEAD( * key=UserKey, * plaintext=value * associated_data=UserKeyId) */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t eccDerivedToken; // c _mongocrypt_buffer_t encryptedTokens; // p _mongocrypt_buffer_t indexKeyId; // u bson_type_t valueType; // t _mongocrypt_buffer_t value; // v _mongocrypt_buffer_t serverEncryptionToken; // e mc_array_t edgeTokenSetArray; // g _mongocrypt_buffer_t plaintext; _mongocrypt_buffer_t userKeyId; } mc_FLE2InsertUpdatePayload_t; /** * EdgeTokenSet is the following BSON document: * d: // EDCDerivedFromDataTokenAndContentionFactor * s: // ESCDerivedFromDataTokenAndContentionFactor * c: // ECCDerivedFromDataTokenAndContentionFactor * p: // Encrypted Tokens * * Instances of mc_EdgeTokenSet_t are expected to be owned by * mc_FLE2InsertUpdatePayload_t and are freed in * mc_FLE2InsertUpdatePayload_cleanup. */ typedef struct { _mongocrypt_buffer_t edcDerivedToken; // d _mongocrypt_buffer_t escDerivedToken; // s _mongocrypt_buffer_t eccDerivedToken; // c _mongocrypt_buffer_t encryptedTokens; // p } mc_EdgeTokenSet_t; void mc_FLE2InsertUpdatePayload_init(mc_FLE2InsertUpdatePayload_t *payload); bool mc_FLE2InsertUpdatePayload_parse(mc_FLE2InsertUpdatePayload_t *out, const _mongocrypt_buffer_t *in, mongocrypt_status_t *status); /* mc_FLE2InsertUpdatePayload_decrypt decrypts ciphertext. * Returns NULL and sets @status on error. It is an error to call before * mc_FLE2InsertUpdatePayload_parse. */ const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayload_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2InsertUpdatePayload_t *iup, const _mongocrypt_buffer_t *user_key, mongocrypt_status_t *status); bool mc_FLE2InsertUpdatePayload_serialize(const mc_FLE2InsertUpdatePayload_t *payload, bson_t *out); bool mc_FLE2InsertUpdatePayload_serializeForRange(const mc_FLE2InsertUpdatePayload_t *payload, bson_t *out); void mc_FLE2InsertUpdatePayload_cleanup(mc_FLE2InsertUpdatePayload_t *payload); #endif /* MC_FLE2_INSERT_UPDATE_PAYLOAD_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-insert-update-payload-v2.c000066400000000000000000000635261521103432300234160ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-encryption-placeholder-private.h" #include "mc-fle2-insert-update-payload-private-v2.h" #include "mc-parse-utils-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-util-private.h" // mc_bson_type_to_string #include "mongocrypt.h" #define DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP(Type) \ void mc_Text##Type##TokenSet_init(mc_Text##Type##TokenSet_t *ts) { \ BSON_ASSERT_PARAM(ts); \ memset(ts, 0, sizeof(mc_Text##Type##TokenSet_t)); \ } \ void mc_Text##Type##TokenSet_cleanup(mc_Text##Type##TokenSet_t *ts) { \ BSON_ASSERT_PARAM(ts); \ _mongocrypt_buffer_cleanup(&ts->edcDerivedToken); \ _mongocrypt_buffer_cleanup(&ts->escDerivedToken); \ _mongocrypt_buffer_cleanup(&ts->serverDerivedFromDataToken); \ _mongocrypt_buffer_cleanup(&ts->encryptedTokens); \ } \ void mc_Text##Type##TokenSet_shallow_copy(const mc_Text##Type##TokenSet_t *src, mc_Text##Type##TokenSet_t *dst) { \ BSON_ASSERT_PARAM(src); \ BSON_ASSERT_PARAM(dst); \ _mongocrypt_buffer_set_to(&src->edcDerivedToken, &dst->edcDerivedToken); \ _mongocrypt_buffer_set_to(&src->escDerivedToken, &dst->escDerivedToken); \ _mongocrypt_buffer_set_to(&src->serverDerivedFromDataToken, &dst->serverDerivedFromDataToken); \ _mongocrypt_buffer_set_to(&src->encryptedTokens, &dst->encryptedTokens); \ } DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP(Exact) DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP(Substring) DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP(Suffix) DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP(Prefix) #undef DEF_TEXT_SEARCH_TOKEN_SET_INIT_CLEANUP void mc_TextSearchTokenSets_init(mc_TextSearchTokenSets_t *tsts) { BSON_ASSERT_PARAM(tsts); mc_TextExactTokenSet_init(&tsts->exact); _mc_array_init(&tsts->substringArray, sizeof(mc_TextSubstringTokenSet_t)); _mc_array_init(&tsts->suffixArray, sizeof(mc_TextSuffixTokenSet_t)); _mc_array_init(&tsts->prefixArray, sizeof(mc_TextPrefixTokenSet_t)); } void mc_TextSearchTokenSets_cleanup(mc_TextSearchTokenSets_t *tsts) { BSON_ASSERT_PARAM(tsts); mc_TextExactTokenSet_cleanup(&tsts->exact); for (size_t i = 0; i < tsts->substringArray.len; i++) { mc_TextSubstringTokenSet_t *entry = &_mc_array_index(&tsts->substringArray, mc_TextSubstringTokenSet_t, i); mc_TextSubstringTokenSet_cleanup(entry); } _mc_array_destroy(&tsts->substringArray); for (size_t i = 0; i < tsts->suffixArray.len; i++) { mc_TextSuffixTokenSet_t *entry = &_mc_array_index(&tsts->suffixArray, mc_TextSuffixTokenSet_t, i); mc_TextSuffixTokenSet_cleanup(entry); } _mc_array_destroy(&tsts->suffixArray); // Free all prefix token set entries. for (size_t i = 0; i < tsts->prefixArray.len; i++) { mc_TextPrefixTokenSet_t *entry = &_mc_array_index(&tsts->prefixArray, mc_TextPrefixTokenSet_t, i); mc_TextPrefixTokenSet_cleanup(entry); } _mc_array_destroy(&tsts->prefixArray); } void mc_FLE2InsertUpdatePayloadV2_init(mc_FLE2InsertUpdatePayloadV2_t *payload) { BSON_ASSERT_PARAM(payload); memset(payload, 0, sizeof(mc_FLE2InsertUpdatePayloadV2_t)); _mc_array_init(&payload->edgeTokenSetArray, sizeof(mc_EdgeTokenSetV2_t)); mc_TextSearchTokenSets_init(&payload->textSearchTokenSets.tsts); } static void mc_EdgeTokenSetV2_cleanup(mc_EdgeTokenSetV2_t *etc) { BSON_ASSERT_PARAM(etc); _mongocrypt_buffer_cleanup(&etc->edcDerivedToken); _mongocrypt_buffer_cleanup(&etc->escDerivedToken); _mongocrypt_buffer_cleanup(&etc->serverDerivedFromDataToken); _mongocrypt_buffer_cleanup(&etc->encryptedTokens); } void mc_FLE2InsertUpdatePayloadV2_cleanup(mc_FLE2InsertUpdatePayloadV2_t *payload) { BSON_ASSERT_PARAM(payload); _mongocrypt_buffer_cleanup(&payload->edcDerivedToken); _mongocrypt_buffer_cleanup(&payload->escDerivedToken); _mongocrypt_buffer_cleanup(&payload->encryptedTokens); _mongocrypt_buffer_cleanup(&payload->indexKeyId); _mongocrypt_buffer_cleanup(&payload->value); _mongocrypt_buffer_cleanup(&payload->serverEncryptionToken); _mongocrypt_buffer_cleanup(&payload->serverDerivedFromDataToken); _mongocrypt_buffer_cleanup(&payload->plaintext); // Free all EdgeTokenSet entries. for (size_t i = 0; i < payload->edgeTokenSetArray.len; i++) { mc_EdgeTokenSetV2_t entry = _mc_array_index(&payload->edgeTokenSetArray, mc_EdgeTokenSetV2_t, i); mc_EdgeTokenSetV2_cleanup(&entry); } _mc_array_destroy(&payload->edgeTokenSetArray); bson_value_destroy(&payload->indexMin); bson_value_destroy(&payload->indexMax); mc_TextSearchTokenSets_cleanup(&payload->textSearchTokenSets.tsts); } #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR("Duplicate field '" #Name "' in payload bson"); \ goto fail; \ } \ has_##Name = true; #define END_IF_FIELD \ continue; \ } #define PARSE_BINDATA(Name, Type, Dest) \ IF_FIELD(Name) { \ if (!parse_bindata(Type, &iter, &out->Dest, status)) { \ goto fail; \ } \ } \ END_IF_FIELD #define PARSE_BINARY(Name, Dest) PARSE_BINDATA(Name, BSON_SUBTYPE_BINARY, Dest) #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR("Missing field '" #Name "' in payload"); \ goto fail; \ } else \ ((void)0) bool mc_FLE2InsertUpdatePayloadV2_parse(mc_FLE2InsertUpdatePayloadV2_t *out, const _mongocrypt_buffer_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_d = false, has_s = false, has_p = false; bool has_u = false, has_t = false, has_v = false; bool has_e = false, has_l = false, has_k = false; bool has_sp = false, has_pn = false, has_tf = false, has_mn = false, has_mx = false; bson_t in_bson; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); if (in->len < 1) { CLIENT_ERR("FLE2InsertUpdatePayloadV2_parse got too short input"); return false; } if (!bson_init_static(&in_bson, in->data + 1, in->len - 1)) { CLIENT_ERR("FLE2InsertUpdatePayloadV2_parse got invalid BSON"); return false; } if (!bson_validate(&in_bson, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, &in_bson)) { CLIENT_ERR("invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); PARSE_BINARY(d, edcDerivedToken) PARSE_BINARY(s, escDerivedToken) PARSE_BINARY(p, encryptedTokens) PARSE_BINDATA(u, BSON_SUBTYPE_UUID, indexKeyId) IF_FIELD(t) { int32_t type = bson_iter_int32(&iter); if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR("Field 't' expected to hold an int32"); goto fail; } if ((type < 0) || (type > 0xFF)) { CLIENT_ERR("Field 't' must be a valid BSON type, got: %d", type); goto fail; } out->valueType = (bson_type_t)type; } END_IF_FIELD IF_FIELD(k) { int64_t contention = bson_iter_int64(&iter); if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR("Field 'k' expected to hold an int64"); goto fail; } if (!mc_validate_contention(contention, status)) { goto fail; } out->contentionFactor = contention; } END_IF_FIELD PARSE_BINARY(v, value) PARSE_BINARY(e, serverEncryptionToken) PARSE_BINARY(l, serverDerivedFromDataToken) IF_FIELD(sp) { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR("Field 'sp' expected to hold an int64, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); goto fail; } int64_t sparsity = bson_iter_int64(&iter); if (!mc_validate_sparsity(sparsity, status)) { goto fail; } out->sparsity = OPT_I64(sparsity); } END_IF_FIELD IF_FIELD(pn) { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR("Field 'pn' expected to hold an int32, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); goto fail; } int32_t precision = bson_iter_int32(&iter); if (precision < 0) { CLIENT_ERR("Field 'pn' must be non-negative, got: %" PRId32, precision); goto fail; } out->precision = OPT_I32(precision); } END_IF_FIELD IF_FIELD(tf) { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR("Field 'tf' expected to hold an int32, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); goto fail; } int32_t trimFactor = bson_iter_int32(&iter); if (trimFactor < 0) { CLIENT_ERR("Field 'tf' must be non-negative, got: %" PRId32, trimFactor); goto fail; } out->trimFactor = OPT_I32(trimFactor); } END_IF_FIELD IF_FIELD(mn) { bson_value_copy(bson_iter_value(&iter), &out->indexMin); } END_IF_FIELD IF_FIELD(mx) { bson_value_copy(bson_iter_value(&iter), &out->indexMax); } END_IF_FIELD } CHECK_HAS(d); CHECK_HAS(s); CHECK_HAS(p); CHECK_HAS(u); CHECK_HAS(t); CHECK_HAS(v); CHECK_HAS(e); CHECK_HAS(l); CHECK_HAS(k); // The fields `sp`, `pn`, `tf`, `mn`, and `mx` are only set for "range" payloads. if (!_mongocrypt_buffer_from_subrange(&out->userKeyId, &out->value, 0, UUID_LEN)) { CLIENT_ERR("failed to create userKeyId buffer"); goto fail; } out->userKeyId.subtype = BSON_SUBTYPE_UUID; return true; fail: return false; } #define IUPS_APPEND_BINDATA(dst, name, subtype, value) \ if (!_mongocrypt_buffer_append(&(value), dst, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2InsertUpdatePayloadV2_serialize(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); IUPS_APPEND_BINDATA(out, "d", BSON_SUBTYPE_BINARY, payload->edcDerivedToken); IUPS_APPEND_BINDATA(out, "s", BSON_SUBTYPE_BINARY, payload->escDerivedToken); IUPS_APPEND_BINDATA(out, "p", BSON_SUBTYPE_BINARY, payload->encryptedTokens); IUPS_APPEND_BINDATA(out, "u", BSON_SUBTYPE_UUID, payload->indexKeyId); if (!BSON_APPEND_INT32(out, "t", payload->valueType)) { return false; } IUPS_APPEND_BINDATA(out, "v", BSON_SUBTYPE_BINARY, payload->value); IUPS_APPEND_BINDATA(out, "e", BSON_SUBTYPE_BINARY, payload->serverEncryptionToken); IUPS_APPEND_BINDATA(out, "l", BSON_SUBTYPE_BINARY, payload->serverDerivedFromDataToken); if (!BSON_APPEND_INT64(out, "k", payload->contentionFactor)) { return false; } return true; } bool mc_FLE2InsertUpdatePayloadV2_serializeForRange(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); if (!mc_FLE2InsertUpdatePayloadV2_serialize(payload, out)) { return false; } // Append "g" array of EdgeTokenSets. bson_t g_bson; if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, "g", &g_bson)) { return false; } uint32_t g_index = 0; for (size_t i = 0; i < payload->edgeTokenSetArray.len; i++) { mc_EdgeTokenSetV2_t etc = _mc_array_index(&payload->edgeTokenSetArray, mc_EdgeTokenSetV2_t, i); bson_t etc_bson; const char *g_index_string; char storage[16]; bson_uint32_to_string(g_index, &g_index_string, storage, sizeof(storage)); if (!BSON_APPEND_DOCUMENT_BEGIN(&g_bson, g_index_string, &etc_bson)) { return false; } IUPS_APPEND_BINDATA(&etc_bson, "d", BSON_SUBTYPE_BINARY, etc.edcDerivedToken); IUPS_APPEND_BINDATA(&etc_bson, "s", BSON_SUBTYPE_BINARY, etc.escDerivedToken); IUPS_APPEND_BINDATA(&etc_bson, "l", BSON_SUBTYPE_BINARY, etc.serverDerivedFromDataToken); IUPS_APPEND_BINDATA(&etc_bson, "p", BSON_SUBTYPE_BINARY, etc.encryptedTokens); if (!bson_append_document_end(&g_bson, &etc_bson)) { return false; } if (g_index == UINT32_MAX) { break; } g_index++; } if (!bson_append_array_end(out, &g_bson)) { return false; } // Encode parameters that were used to generate the payload. BSON_ASSERT(payload->sparsity.set); if (!BSON_APPEND_INT64(out, "sp", payload->sparsity.value)) { return false; } // Precision may be unset. if (payload->precision.set) { if (!BSON_APPEND_INT32(out, "pn", payload->precision.value)) { return false; } } BSON_ASSERT(payload->trimFactor.set); if (!BSON_APPEND_INT32(out, "tf", payload->trimFactor.value)) { return false; } BSON_ASSERT(payload->indexMin.value_type != BSON_TYPE_EOD); if (!BSON_APPEND_VALUE(out, "mn", &payload->indexMin)) { return false; } BSON_ASSERT(payload->indexMax.value_type != BSON_TYPE_EOD); if (!BSON_APPEND_VALUE(out, "mx", &payload->indexMax)) { return false; } return true; } #define SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Type) \ static bool _fle2_serialize_Text##Type##TokenSet(bson_t *parent, \ const char *field_name, \ const mc_Text##Type##TokenSet_t *ts) { \ BSON_ASSERT_PARAM(parent); \ BSON_ASSERT_PARAM(field_name); \ BSON_ASSERT_PARAM(ts); \ bson_t child; \ if (!BSON_APPEND_DOCUMENT_BEGIN(parent, field_name, &child)) { \ return false; \ } \ IUPS_APPEND_BINDATA(&child, "d", BSON_SUBTYPE_BINARY, ts->edcDerivedToken); \ IUPS_APPEND_BINDATA(&child, "s", BSON_SUBTYPE_BINARY, ts->escDerivedToken); \ IUPS_APPEND_BINDATA(&child, "l", BSON_SUBTYPE_BINARY, ts->serverDerivedFromDataToken); \ IUPS_APPEND_BINDATA(&child, "p", BSON_SUBTYPE_BINARY, ts->encryptedTokens); \ if (!bson_append_document_end(parent, &child)) { \ return false; \ } \ return true; \ } SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Exact) SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Substring) SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Suffix) SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Prefix) #undef SERIALIZE_TEXT_TOKEN_SET_FOR_TYPE_IMPL #undef IUPS_APPEND_BINDATA #define SERIALIZE_ARRAY_OF_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Type) \ static bool _fle2_serialize_array_of_Text##Type##TokenSet(bson_t *parent, \ const char *array_name, \ const mc_array_t *array) { \ BSON_ASSERT_PARAM(parent); \ BSON_ASSERT_PARAM(array_name); \ BSON_ASSERT_PARAM(array); \ bson_t arr_bson; \ if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(parent, array_name, &arr_bson)) { \ return false; \ } \ \ const char *index_string = NULL; \ char storage[16]; \ uint32_t index = 0; \ uint32_t limit = (array->len > UINT32_MAX) ? UINT32_MAX : (uint32_t)array->len; \ for (; index < limit; index++) { \ mc_Text##Type##TokenSet_t ts = _mc_array_index(array, mc_Text##Type##TokenSet_t, index); \ bson_uint32_to_string(index, &index_string, storage, sizeof(storage)); \ \ if (!_fle2_serialize_Text##Type##TokenSet(&arr_bson, index_string, &ts)) { \ return false; \ } \ } \ if (!bson_append_array_end(parent, &arr_bson)) { \ return false; \ } \ return true; \ } SERIALIZE_ARRAY_OF_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Substring) SERIALIZE_ARRAY_OF_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Suffix) SERIALIZE_ARRAY_OF_TEXT_TOKEN_SET_FOR_TYPE_IMPL(Prefix) #undef SERIALIZE_ARRAY_OF_TEXT_TOKEN_SET_FOR_TYPE_IMPL bool mc_FLE2InsertUpdatePayloadV2_serializeForTextSearch(const mc_FLE2InsertUpdatePayloadV2_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); BSON_ASSERT(payload->textSearchTokenSets.set); if (!mc_FLE2InsertUpdatePayloadV2_serialize(payload, out)) { return false; } const mc_TextSearchTokenSets_t *tsts = &payload->textSearchTokenSets.tsts; // Start serializing "b", the TextSearchTokenSets bson_t b_bson; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "b", &b_bson)) { return false; } // Serialize "b.e", the TextExactTokenSet if (!_fle2_serialize_TextExactTokenSet(&b_bson, "e", &tsts->exact)) { return false; } // Serialize "b.s", the array of TextSubstringTokenSet if (!_fle2_serialize_array_of_TextSubstringTokenSet(&b_bson, "s", &tsts->substringArray)) { return false; } // Serialize "b.u", the array of TextSuffixTokenSet if (!_fle2_serialize_array_of_TextSuffixTokenSet(&b_bson, "u", &tsts->suffixArray)) { return false; } // Serialize "b.p", the array of TextPrefixTokenSet if (!_fle2_serialize_array_of_TextPrefixTokenSet(&b_bson, "p", &tsts->prefixArray)) { return false; } // End serializing "b", the TextSearchTokenSets if (!bson_append_document_end(out, &b_bson)) { return false; } return true; } const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayloadV2_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2InsertUpdatePayloadV2_t *iup, const _mongocrypt_buffer_t *user_key, mongocrypt_status_t *status) { const _mongocrypt_value_encryption_algorithm_t *fle2v2 = _mcFLE2v2AEADAlgorithm(); BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iup); BSON_ASSERT_PARAM(user_key); if (iup->value.len == 0) { CLIENT_ERR("FLE2InsertUpdatePayloadV2 value not parsed"); return NULL; } _mongocrypt_buffer_t ciphertext; BSON_ASSERT(iup->value.len >= UUID_LEN); if (!_mongocrypt_buffer_from_subrange(&ciphertext, &iup->value, UUID_LEN, iup->value.len - UUID_LEN)) { CLIENT_ERR("Failed to create ciphertext buffer"); return NULL; } _mongocrypt_buffer_resize(&iup->plaintext, fle2v2->get_plaintext_len(ciphertext.len, status)); uint32_t bytes_written; if (!fle2v2->do_decrypt(crypto, &iup->userKeyId, user_key, &ciphertext, &iup->plaintext, &bytes_written, status)) { return NULL; } iup->plaintext.len = bytes_written; return &iup->plaintext; } libmongocrypt-1.19.0/src/mc-fle2-insert-update-payload.c000066400000000000000000000255231521103432300230640ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-fle2-insert-update-payload-private.h" #include "mc-parse-utils-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" void mc_FLE2InsertUpdatePayload_init(mc_FLE2InsertUpdatePayload_t *payload) { BSON_ASSERT_PARAM(payload); memset(payload, 0, sizeof(mc_FLE2InsertUpdatePayload_t)); _mc_array_init(&payload->edgeTokenSetArray, sizeof(mc_EdgeTokenSet_t)); } static void mc_EdgeTokenSet_cleanup(mc_EdgeTokenSet_t *etc) { BSON_ASSERT_PARAM(etc); _mongocrypt_buffer_cleanup(&etc->edcDerivedToken); _mongocrypt_buffer_cleanup(&etc->escDerivedToken); _mongocrypt_buffer_cleanup(&etc->eccDerivedToken); _mongocrypt_buffer_cleanup(&etc->encryptedTokens); } void mc_FLE2InsertUpdatePayload_cleanup(mc_FLE2InsertUpdatePayload_t *payload) { BSON_ASSERT_PARAM(payload); _mongocrypt_buffer_cleanup(&payload->edcDerivedToken); _mongocrypt_buffer_cleanup(&payload->escDerivedToken); _mongocrypt_buffer_cleanup(&payload->eccDerivedToken); _mongocrypt_buffer_cleanup(&payload->encryptedTokens); _mongocrypt_buffer_cleanup(&payload->indexKeyId); _mongocrypt_buffer_cleanup(&payload->value); _mongocrypt_buffer_cleanup(&payload->serverEncryptionToken); _mongocrypt_buffer_cleanup(&payload->plaintext); // Free all EdgeTokenSet entries. for (size_t i = 0; i < payload->edgeTokenSetArray.len; i++) { mc_EdgeTokenSet_t entry = _mc_array_index(&payload->edgeTokenSetArray, mc_EdgeTokenSet_t, i); mc_EdgeTokenSet_cleanup(&entry); } _mc_array_destroy(&payload->edgeTokenSetArray); } #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR("Duplicate field '" #Name "' in payload bson"); \ goto fail; \ } \ has_##Name = true; #define END_IF_FIELD \ continue; \ } #define PARSE_BINDATA(Name, Type, Dest) \ IF_FIELD(Name) { \ if (!parse_bindata(Type, &iter, &out->Dest, status)) { \ goto fail; \ } \ } \ END_IF_FIELD #define PARSE_BINARY(Name, Dest) PARSE_BINDATA(Name, BSON_SUBTYPE_BINARY, Dest) #define CHECK_HAS(Name) \ if (!has_##Name) { \ CLIENT_ERR("Missing field '" #Name "' in payload"); \ goto fail; \ } else \ ((void)0) bool mc_FLE2InsertUpdatePayload_parse(mc_FLE2InsertUpdatePayload_t *out, const _mongocrypt_buffer_t *in, mongocrypt_status_t *status) { bson_iter_t iter; bool has_d = false, has_s = false, has_c = false; bool has_p = false, has_u = false, has_t = false; bool has_v = false, has_e = false; bson_t in_bson; BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); if (in->len < 1) { CLIENT_ERR("FLE2InsertUpdatePayload_parse got too short input"); return false; } if (!bson_init_static(&in_bson, in->data + 1, in->len - 1)) { CLIENT_ERR("FLE2InsertUpdatePayload_parse got invalid BSON"); return false; } if (!bson_validate(&in_bson, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, &in_bson)) { CLIENT_ERR("invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); PARSE_BINARY(d, edcDerivedToken) PARSE_BINARY(s, escDerivedToken) PARSE_BINARY(c, eccDerivedToken) PARSE_BINARY(p, encryptedTokens) PARSE_BINDATA(u, BSON_SUBTYPE_UUID, indexKeyId) IF_FIELD(t) { int32_t type = bson_iter_int32(&iter); if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR("Field 't' expected to hold an int32"); goto fail; } if ((type < 0) || (type > 0xFF)) { CLIENT_ERR("Field 't' must be a valid BSON type, got: %d", type); goto fail; } out->valueType = (bson_type_t)type; } END_IF_FIELD PARSE_BINARY(v, value) PARSE_BINARY(e, serverEncryptionToken) } CHECK_HAS(d); CHECK_HAS(s); CHECK_HAS(c); CHECK_HAS(p); CHECK_HAS(u); CHECK_HAS(t); CHECK_HAS(v); CHECK_HAS(e); if (!_mongocrypt_buffer_from_subrange(&out->userKeyId, &out->value, 0, UUID_LEN)) { CLIENT_ERR("failed to create userKeyId buffer"); goto fail; } out->userKeyId.subtype = BSON_SUBTYPE_UUID; return true; fail: return false; } #define IUPS_APPEND_BINDATA(dst, name, subtype, value) \ if (!_mongocrypt_buffer_append(&(value), dst, name, -1)) { \ return false; \ } else \ ((void)0) bool mc_FLE2InsertUpdatePayload_serialize(const mc_FLE2InsertUpdatePayload_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); IUPS_APPEND_BINDATA(out, "d", BSON_SUBTYPE_BINARY, payload->edcDerivedToken); IUPS_APPEND_BINDATA(out, "s", BSON_SUBTYPE_BINARY, payload->escDerivedToken); IUPS_APPEND_BINDATA(out, "c", BSON_SUBTYPE_BINARY, payload->eccDerivedToken); IUPS_APPEND_BINDATA(out, "p", BSON_SUBTYPE_BINARY, payload->encryptedTokens); IUPS_APPEND_BINDATA(out, "u", BSON_SUBTYPE_UUID, payload->indexKeyId); if (!BSON_APPEND_INT32(out, "t", payload->valueType)) { return false; } IUPS_APPEND_BINDATA(out, "v", BSON_SUBTYPE_BINARY, payload->value); IUPS_APPEND_BINDATA(out, "e", BSON_SUBTYPE_BINARY, payload->serverEncryptionToken); return true; } bool mc_FLE2InsertUpdatePayload_serializeForRange(const mc_FLE2InsertUpdatePayload_t *payload, bson_t *out) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(payload); if (!mc_FLE2InsertUpdatePayload_serialize(payload, out)) { return false; } // Append "g" array of EdgeTokenSets. bson_t g_bson; if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, "g", &g_bson)) { return false; } uint32_t g_index = 0; for (size_t i = 0; i < payload->edgeTokenSetArray.len; i++) { mc_EdgeTokenSet_t etc = _mc_array_index(&payload->edgeTokenSetArray, mc_EdgeTokenSet_t, i); bson_t etc_bson; const char *g_index_string; char storage[16]; bson_uint32_to_string(g_index, &g_index_string, storage, sizeof(storage)); if (!BSON_APPEND_DOCUMENT_BEGIN(&g_bson, g_index_string, &etc_bson)) { return false; } IUPS_APPEND_BINDATA(&etc_bson, "d", BSON_SUBTYPE_BINARY, etc.edcDerivedToken); IUPS_APPEND_BINDATA(&etc_bson, "s", BSON_SUBTYPE_BINARY, etc.escDerivedToken); IUPS_APPEND_BINDATA(&etc_bson, "c", BSON_SUBTYPE_BINARY, etc.eccDerivedToken); IUPS_APPEND_BINDATA(&etc_bson, "p", BSON_SUBTYPE_BINARY, etc.encryptedTokens); if (!bson_append_document_end(&g_bson, &etc_bson)) { return false; } if (g_index == UINT32_MAX) { break; } g_index++; } if (!bson_append_array_end(out, &g_bson)) { return false; } return true; } #undef IUPS_APPEND_BINDATA const _mongocrypt_buffer_t *mc_FLE2InsertUpdatePayload_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2InsertUpdatePayload_t *iup, const _mongocrypt_buffer_t *user_key, mongocrypt_status_t *status) { const _mongocrypt_value_encryption_algorithm_t *fle2aead = _mcFLE2AEADAlgorithm(); BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iup); BSON_ASSERT_PARAM(user_key); if (iup->value.len == 0) { CLIENT_ERR("FLE2InsertUpdatePayload value not parsed"); return NULL; } _mongocrypt_buffer_t ciphertext; BSON_ASSERT(iup->value.len >= UUID_LEN); if (!_mongocrypt_buffer_from_subrange(&ciphertext, &iup->value, UUID_LEN, iup->value.len - UUID_LEN)) { CLIENT_ERR("Failed to create ciphertext buffer"); return NULL; } _mongocrypt_buffer_resize(&iup->plaintext, fle2aead->get_plaintext_len(ciphertext.len, status)); uint32_t bytes_written; /* ignored */ if (!fle2aead ->do_decrypt(crypto, &iup->userKeyId, user_key, &ciphertext, &iup->plaintext, &bytes_written, status)) { return NULL; } return &iup->plaintext; } libmongocrypt-1.19.0/src/mc-fle2-payload-iev-private-v2.h000066400000000000000000000271051521103432300230630ustar00rootroot00000000000000/* * Copyright 2023-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. */ #ifndef MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H #define MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H #include "mc-fle2-tag-and-encrypted-metadata-block-private.h" #include "mc-tokens-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-status-private.h" /* * FLE2IndexedEqualityEncryptedValueV2, FLE2IndexedRangeEncryptedValueV2, and FLEIndexedTextEncryptedValue * share a common internal implementation. * * Lifecycle: * 1. mc_FLE2IndexedEncryptedValueV2_init * 2. mc_FLE2IndexedEncryptedValueV2_parse * 3. mc_FLE2IndexedEncryptedValueV2_get_S_KeyId * 4. mc_FLE2IndexedEncryptedValueV2_add_S_Key * 5. mc_FLE2IndexedEncryptedValueV2_get_K_KeyId * 6. mc_FLE2IndexedEncryptedValueV2_add_K_Key * 7. mc_FLE2IndexedEncryptedValueV2_get_ClientValue * 8. mc_FLE2IndexedEncryptedValueV2_serialize * 9. mc_FLE2IndexedEncryptedValueV2_destroy * * * FLE2IndexedEqualityEncryptedValueV2 has the following data layout: * * struct FLE2IndexedEqualityEncryptedValueV2 { * uint8_t fle_blob_subtype = 14; * uint8_t S_KeyId[16]; * uint8_t original_bson_type; * uint8_t ServerEncryptedValue[ServerEncryptedValue.length]; * FLE2TagAndEncryptedMetadataBlock metadata; * } * * ServerEncryptedValue := * EncryptCTR(ServerEncryptionToken, K_KeyId || ClientEncryptedValue) * ClientEncryptedValue := EncryptCBCAEAD(K_Key, clientValue, AD=K_KeyId) * * * struct FLE2TagAndEncryptedMetadataBlock { * uint8_t encryptedCount[32]; // EncryptCTR(countEncryptionToken, * // count || contentionFactor) * uint8_t tag[32]; // HMAC-SHA256(count, edcTwiceDerived) * uint8_t encryptedZeros[32]; // EncryptCTR(zerosEncryptionToken, 0*) * } * * * FLE2IndexedRangeEncryptedValueV2 has the following data layout: * * struct FLE2IndexedRangeEncryptedValueV2 { * uint8_t fle_blob_subtype = 15; * uint8_t S_KeyId[16]; * uint8_t original_bson_type; * uint8_t edge_count; * uint8_t ServerEncryptedValue[ServerEncryptedValue.length]; * FLE2TagAndEncryptedMetadataBlock metadata[edge_count]; * } * * Note that this format differs from FLE2IndexedEqualityEncryptedValueV2 * in only two ways: * 1/ `edge_count` is introduced as a 8 bit int following `original_bson_type`. * 2/ Rather than a single metadata block, we have {edge_count} blocks. * * FLE2IndexedTextEncryptedValue has the following data layout: * * struct FLE2IndexedTextEncryptedValue { * uint8_t fle_blob_subtype = 17; * uint8_t S_KeyId[16]; * uint8_t original_bson_type; * uint32_t edge_count; * uint32_t substr_tag_count; * uint32_t suffix_tag_count; * uint8_t ServerEncryptedValue[ServerEncryptedValue.length]; * FLE2TagAndEncryptedMetadataBlock exact_metadata; * FLE2TagAndEncryptedMetadataBlock substr_metadata[substr_tag_count]; * FLE2TagAndEncryptedMetadataBlock suffix_metadata[suffix_tag_count]; * FLE2TagAndEncryptedMetadataBlock prefix_metadata[edge_count - suffix_tag_count - substr_tag_count - 1]; * } * The main difference in this format is that we split `metadata` into 4 * sections, one for each text search index type. We expand edge_count * to be a 32 bit integer rather than 8 bit. We add two 32 bit ints, * `substr_tag_count` and `suffix_tag_count`, following `edge_count` * in order to track the delineation of the metadata. Similarly to * FLE2IndexedEqualityEncryptedValueV2, we have `edge_count` total * blocks. */ typedef enum { kFLE2IEVTypeInitV2, kFLE2IEVTypeEqualityV2, kFLE2IEVTypeRangeV2, kFLE2IEVTypeText, } _mc_fle2_iev_v2_type; typedef struct _mc_FLE2IndexedEncryptedValueV2_t { // Raw payload values uint8_t fle_blob_subtype; uint8_t bson_value_type; uint32_t edge_count; uint32_t substr_tag_count; uint32_t suffix_tag_count; _mongocrypt_buffer_t S_KeyId; _mongocrypt_buffer_t ServerEncryptedValue; // Decode State _mc_fle2_iev_v2_type type; bool ClientEncryptedValueDecoded; bool ClientValueDecoded; // Populated during _add_S_Key // DecryptedServerEncryptedValue := DecryptCTR(S_Key, ServerEncryptedValue) _mongocrypt_buffer_t DecryptedServerEncryptedValue; // Views on DecryptedServerEncryptedValue (DSEV) _mongocrypt_buffer_t K_KeyId; // First 16 octets, UUID _mongocrypt_buffer_t ClientEncryptedValue; // Remainder of DSEV // Populated during _add_K_Key // ClientValue := DecryptCBCAEAD(K_Key, ClientEncryptedValue, AD=K_KeyId) _mongocrypt_buffer_t ClientValue; mc_FLE2TagAndEncryptedMetadataBlock_t *metadata; } mc_FLE2IndexedEncryptedValueV2_t; mc_FLE2IndexedEncryptedValueV2_t *mc_FLE2IndexedEncryptedValueV2_new(void); bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); /* * Populates an mc_FLE2IndexedEncryptedValueV2_t from a buffer. * * Input buffer must take the form of: * fle_blob_subtype (8u) * S_KeyId (8u * 16u) * original_bson_type (8u) * if (range) * edge_count(8u) * if (text) * edge_count(32u) * substr_tag_count(32u) * suffix_tag_count(32u) * ServerEncryptedValue (8u * SEV_len) * metadata (96u * {range || text ? edge_count : 1u}) * * Returns an error if the input buffer is not valid. */ bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /* * Serializes an mc_FLE2IndexedEncryptedValueV2_t into a buffer. * * The serialized output follows the same layout as the input `buf` to * mc_FLE2IndexedEncryptedValueV2_parse, allowing for round-trip * conversions between the serialized and parsed forms. * * Returns an error if the input structure is not valid, or if the buffer * provided is insufficient to hold the serialized data. */ bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValueV2_t *iev, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /** * Validates that a mc_FLE2IndexedEncryptedValueV2_t is well-formed, i.e. values are in their valid * ranges and buffers are correctly sized. Returns an error if the input structure is invalid. */ bool mc_FLE2IndexedEncryptedValueV2_validate(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_add_S_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *S_Key, mongocrypt_status_t *status); const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValueV2_get_ClientEncryptedValue(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_K_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_add_K_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *K_Key, mongocrypt_status_t *status); const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_ClientValue(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); uint32_t mc_FLE2IndexedEncryptedValueV2_get_edge_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_substr_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_suffix_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_edge(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t edge_index, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_exact_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_substr_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValueV2_get_prefix_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status); void mc_FLE2IndexedEncryptedValueV2_destroy(mc_FLE2IndexedEncryptedValueV2_t *iev); #endif /* MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_V2_H */ libmongocrypt-1.19.0/src/mc-fle2-payload-iev-private.h000066400000000000000000000213251521103432300225340ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_H #define MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_H #include "mc-tokens-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-status-private.h" /** * FLE2IndexedEncryptedValue represents an FLE2 encrypted value. It is * created server side. * * FLE2IndexedEncryptedValue represents one of the following payloads: * - FLE2IndexedEqualityEncryptedValue * - FLE2IndexedRangeEncryptedValue * * Both payloads share a common prefix. libmongocrypt does not need to parse the * edges in FLE2IndexedRangeEncryptedValue. */ /* clang-format off */ /* * FLE2IndexedEqualityEncryptedValue has the following data layout: * * struct { * uint8_t fle_blob_subtype = 7; * uint8_t S_KeyId[16]; * uint8_t original_bson_type; * uint8_t InnerEncrypted[InnerEncrypted_length]; * } FLE2IndexedEqualityEncryptedValue * * InnerEncrypted is the output of: Encrypt(key=ServerDataLevel1Token, plaintext=Inner) * ServerDataLevel1Token is created from the key identified by S_KeyId. * * struct { * uint64_t length; // sizeof(K_KeyId) + ClientEncryptedValue_length; * uint8_t K_KeyId[16]; * uint8_t ClientEncryptedValue[ClientEncryptedValue_length]; * uint64_t counter; * uint8_t edc[32]; // EDCDerivedFromDataTokenAndContentionFactorToken * uint8_t esc[32]; // ESCDerivedFromDataTokenAndContentionFactorToken * uint8_t ecc[32]; // ECCDerivedFromDataTokenAndContentionFactorToken *} Inner * * ClientEncryptedValue is the output of: EncryptAEAD(key=K_Key, plaintext=ClientValue, associated_data=K_KeyId) * K_Key is the key identified by K_KeyId. * * See https://github.com/mongodb/mongo/blob/fa94f5fb6216a1cc1e23f5ad4df05295b380070e/src/mongo/crypto/fle_crypto.h#L897 * for the server representation of FLE2IndexedEqualityEncryptedPayload. */ /* * FLE2IndexedRangeEncryptedPayload shares the data layout with * FLE2IndexedEqualityEncryptedValue with the following additional data appended to Inner: * * uint32_t edgeCount; * struct { * uint64_t counter; * uint8_t[32] edc; // EDCDerivedFromDataTokenAndContentionFactorToken * uint8_t[32] esc; // ESCDerivedFromDataTokenAndContentionFactorToken * uint8_t[32] ecc; // ECCDerivedFromDataTokenAndContentionFactorToken * } edges[edgeCount]; * * libmongocrypt ignores the edges. * * See https://github.com/mongodb/mongo/blob/fa94f5fb6216a1cc1e23f5ad4df05295b380070e/src/mongo/crypto/fle_crypto.h#L897 * for the server representation of FLE2IndexedEqualityEncryptedPayload. */ /* clang-format on */ typedef struct _mc_FLE2IndexedEqualityEncryptedValue_t mc_FLE2IndexedEncryptedValue_t; struct _mc_FLE2IndexedEqualityEncryptedValueTokens { uint64_t counter; _mongocrypt_buffer_t edc; _mongocrypt_buffer_t esc; _mongocrypt_buffer_t ecc; }; typedef struct _mc_FLE2IndexedEqualityEncryptedValueTokens mc_FLE2IndexedEqualityEncryptedValueTokens; mc_FLE2IndexedEncryptedValue_t *mc_FLE2IndexedEncryptedValue_new(void); mc_FLE2IndexedEqualityEncryptedValueTokens *mc_FLE2IndexedEqualityEncryptedValueTokens_new(void); /** * This function is used by the server codebase. */ bool mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer(mc_FLE2IndexedEqualityEncryptedValueTokens *tokens, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValue_parse(mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /** * This function is used by the server codebase. */ bool mc_FLE2IndexedEncryptedValue_write(_mongocrypt_crypto_t *crypto, const bson_type_t original_bson_type, const _mongocrypt_buffer_t *S_KeyId, const _mongocrypt_buffer_t *ClientEncryptedValue, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_get_original_bson_type returns * original_bson_type. Returns 0 and sets @status on error. * It is an error to call before mc_FLE2IndexedEncryptedValue_parse. */ bson_type_t mc_FLE2IndexedEncryptedValue_get_original_bson_type(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_get_S_KeyId returns S_KeyId. Returns * NULL and sets @status on error. It is an error to call before * mc_FLE2IndexedEncryptedValue_parse. */ const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_S_KeyId(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_add_S_Key decrypts InnerEncrypted. * Returns false and sets @status on error. It is an error to call before * mc_FLE2IndexedEncryptedValue_parse. */ bool mc_FLE2IndexedEncryptedValue_add_S_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *S_Key, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_decrypt decrypts InnerEncrypted with the * ServerDataEncryptionLevel1Token on the server-side. Returns false and sets * @status on error. It is an error to call before * mc_FLE2IndexedEncryptedValue_parse. * * This function is used by the server codebase. */ bool mc_FLE2IndexedEncryptedValue_decrypt_equality(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *indexed_tokens, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_get_K_KeyId returns Inner.K_KeyId. * Returns NULL and sets @status on error. It is an error to call before * mc_FLE2IndexedEncryptedValue_add_S_Key. */ const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_K_KeyId(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_add_K_Key decrypts * Inner.ClientEncryptedValue. Returns false and sets @status on error. Must * not be called before mc_FLE2IndexedEncryptedValue_add_S_Key. */ bool mc_FLE2IndexedEncryptedValue_add_K_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *K_Key, mongocrypt_status_t *status); /* mc_FLE2IndexedEncryptedValue_get_ClientValue returns the decrypted * Inner.ClientEncryptedValue. Returns NULL and sets @status on error. * It is an error to call before mc_FLE2IndexedEncryptedValue_add_K_Key. */ const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_ClientValue(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); /** * This function is used by the server codebase. */ const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValue_get_ClientEncryptedValue(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); void mc_FLE2IndexedEncryptedValue_destroy(mc_FLE2IndexedEncryptedValue_t *iev); void mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(mc_FLE2IndexedEqualityEncryptedValueTokens *tokens); #endif /* MONGOCRYPT_INDEXED_ENCRYPTED_VALUE_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-payload-iev-v2.c000066400000000000000000001020651521103432300214050ustar00rootroot00000000000000/* * Copyright 2022-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-buffer-private.h" #include "mongocrypt-private.h" #include "mc-dec128.h" #include "mc-fle-blob-subtype-private.h" #include "mc-fle2-payload-iev-private-v2.h" #include "mc-fle2-tag-and-encrypted-metadata-block-private.h" #include "mc-reader-private.h" #include "mc-tokens-private.h" #include "mc-writer-private.h" #include #include #define kMinServerEncryptedValueLen 17U // IV(16) + EncryptCTR(1byte) #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) mc_FLE2IndexedEncryptedValueV2_t *mc_FLE2IndexedEncryptedValueV2_new(void) { return bson_malloc0(sizeof(mc_FLE2IndexedEncryptedValueV2_t)); } bson_type_t mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_bson_value_type " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return BSON_TYPE_EOD; } return (bson_type_t)iev->bson_value_type; } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_S_KeyId " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return NULL; } return &iev->S_KeyId; } bool mc_FLE2IndexedEncryptedValueV2_add_S_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *S_Key, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(S_Key); BSON_ASSERT_PARAM(status); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_S_Key must " "be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->ClientEncryptedValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_S_Key must " "not be called twice"); return false; } if (S_Key->len != MONGOCRYPT_KEY_LEN) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_S_Key expected " "S_Key to be %d bytes, got: %" PRIu32, MONGOCRYPT_KEY_LEN, S_Key->len); return false; } /* Get the TokenKey from the last 32 bytes of S_Key */ _mongocrypt_buffer_t TokenKey; if (!_mongocrypt_buffer_from_subrange(&TokenKey, S_Key, S_Key->len - MONGOCRYPT_TOKEN_KEY_LEN, MONGOCRYPT_TOKEN_KEY_LEN)) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_S_Key unable to " "parse TokenKey from S_Key"); return false; } /* Use TokenKey to create ServerDataEncryptionLevel1Token and decrypt * ServerEncryptedValue into ClientEncryptedValue */ mc_ServerDataEncryptionLevel1Token_t *token = mc_ServerDataEncryptionLevel1Token_new(crypto, &TokenKey, status); if (!token) { return false; } bool ret = false; const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm(); const uint32_t DecryptedServerEncryptedValueLen = fle2alg->get_plaintext_len(iev->ServerEncryptedValue.len, status); if (!mongocrypt_status_ok(status)) { goto fail; } if (DecryptedServerEncryptedValueLen <= UUID_LEN) { CLIENT_ERR("Invalid ServerEncryptedValue length, got %" PRIu32 ", expected more than %d", DecryptedServerEncryptedValueLen, UUID_LEN); goto fail; } _mongocrypt_buffer_resize(&iev->DecryptedServerEncryptedValue, DecryptedServerEncryptedValueLen); uint32_t bytes_written = 0; if (!fle2alg->do_decrypt(crypto, NULL /* aad */, mc_ServerDataEncryptionLevel1Token_get(token), &iev->ServerEncryptedValue, &iev->DecryptedServerEncryptedValue, &bytes_written, status)) { goto fail; } BSON_ASSERT(bytes_written == DecryptedServerEncryptedValueLen); if (!_mongocrypt_buffer_from_subrange(&iev->K_KeyId, &iev->DecryptedServerEncryptedValue, 0, UUID_LEN)) { CLIENT_ERR("Error creating K_KeyId subrange from DecryptedServerEncryptedValue"); goto fail; } iev->K_KeyId.subtype = BSON_SUBTYPE_UUID; BSON_ASSERT(iev->DecryptedServerEncryptedValue.len > UUID_LEN); if (!_mongocrypt_buffer_from_subrange(&iev->ClientEncryptedValue, &iev->DecryptedServerEncryptedValue, UUID_LEN, iev->DecryptedServerEncryptedValue.len - UUID_LEN)) { CLIENT_ERR("Error creating ClientEncryptedValue subrange from " "DecryptedServerEncryptedValue"); goto fail; } iev->ClientEncryptedValueDecoded = true; ret = true; fail: mc_ServerDataEncryptionLevel1Token_destroy(token); return ret; } const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValueV2_get_ClientEncryptedValue(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->ClientEncryptedValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_" "ClientEncryptedValue must be called after " "mc_FLE2IndexedEncryptedValueV2_add_S_Key"); return NULL; } return &iev->ClientEncryptedValue; } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_K_KeyId(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->ClientEncryptedValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_K_KeyID " "must be called after " "mc_FLE2IndexedEncryptedValueV2_add_S_Key"); return NULL; } return &iev->K_KeyId; } bool mc_FLE2IndexedEncryptedValueV2_add_K_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *K_Key, mongocrypt_status_t *status) { const _mongocrypt_value_encryption_algorithm_t *fle2v2aead = _mcFLE2v2AEADAlgorithm(); BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(K_Key); BSON_ASSERT_PARAM(status); if (!iev->ClientEncryptedValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_K_Key must be " "called after " "mc_FLE2IndexedEncryptedValueV2_add_S_Key"); return false; } if (iev->ClientValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_add_K_Key must not " "be called twice"); return false; } /* Attempt to decrypt ClientEncryptedValue */ const uint32_t ClientValueLen = fle2v2aead->get_plaintext_len(iev->ClientEncryptedValue.len, status); if (!mongocrypt_status_ok(status)) { return false; } _mongocrypt_buffer_t clientValue; _mongocrypt_buffer_init_size(&clientValue, ClientValueLen); uint32_t bytes_written = 0; if (!fle2v2aead->do_decrypt(crypto, &iev->K_KeyId, K_Key, &iev->ClientEncryptedValue, &clientValue, &bytes_written, status)) { _mongocrypt_buffer_cleanup(&clientValue); return false; } BSON_ASSERT(bytes_written > 0); BSON_ASSERT(bytes_written <= ClientValueLen); _mongocrypt_buffer_steal(&iev->ClientValue, &clientValue); iev->ClientValue.len = bytes_written; iev->ClientValueDecoded = true; return true; } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValueV2_get_ClientValue(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->ClientValueDecoded) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_ClientValue must " "be called after " "mc_FLE2IndexedEncryptedValueV2_add_K_Key"); return NULL; } return &iev->ClientValue; } void mc_FLE2IndexedEncryptedValueV2_destroy(mc_FLE2IndexedEncryptedValueV2_t *iev) { if (!iev) { return; } _mongocrypt_buffer_cleanup(&iev->ClientValue); _mongocrypt_buffer_cleanup(&iev->DecryptedServerEncryptedValue); _mongocrypt_buffer_cleanup(&iev->ServerEncryptedValue); _mongocrypt_buffer_cleanup(&iev->S_KeyId); if (iev->metadata) { for (uint32_t i = 0; i < iev->edge_count; i++) { mc_FLE2TagAndEncryptedMetadataBlock_cleanup(&iev->metadata[i]); } // Metadata array is dynamically allocated bson_free(iev->metadata); } bson_free(iev); } uint32_t mc_FLE2IndexedEncryptedValueV2_get_edge_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return 0; } if (!(iev->type == kFLE2IEVTypeRangeV2 || iev->type == kFLE2IEVTypeText)) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge_count must be called with type range or text"); return 0; } return iev->edge_count; } bool mc_FLE2IndexedEncryptedValueV2_get_substr_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(count); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_substr_tag_count " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_substr_tag_count must be called with type text"); return false; } *count = iev->substr_tag_count; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_suffix_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(count); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_suffix_tag_count " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_suffix_tag_count must be called with type text"); return false; } *count = iev->suffix_tag_count; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count(const mc_FLE2IndexedEncryptedValueV2_t *iev, uint32_t *count, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(count); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count must be called with type text"); return false; } *count = (uint32_t)(iev->edge_count - iev->substr_tag_count - iev->suffix_tag_count - 1); return true; } bool mc_FLE2IndexedEncryptedValueV2_get_edge(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t edge_index, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeRangeV2 && iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with type range"); return false; } if (edge_index >= iev->edge_count) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_edge must be called with index edge_index less " "than edge count"); return false; } // Write edge into out struct *out = iev->metadata[edge_index]; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeEqualityV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_metadata must be called with type equality"); return false; } // Write edge into out struct *out = *iev->metadata; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_exact_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_exact_metadata " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_exact_metadata must be called with type text"); return false; } // Write edge into out struct *out = iev->metadata[0]; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_substr_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_substr_metadata " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_substr_metadata must be called with type text"); return false; } if (block_index >= iev->substr_tag_count) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_substr_metadata must be called with index block_index less " "than substr tag count"); return false; } // Write edge into out struct *out = iev->metadata[block_index + 1 /* exact block */]; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); if (iev->type == kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata " "must be called after " "mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata must be called with type text"); return false; } if (block_index >= iev->suffix_tag_count) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata must be called with index block_index less " "than suffix tag count"); return false; } // Write edge into out struct *out = iev->metadata[block_index + iev->substr_tag_count + 1 /* exact block */]; return true; } bool mc_FLE2IndexedEncryptedValueV2_get_prefix_metadata(const mc_FLE2IndexedEncryptedValueV2_t *iev, mc_FLE2TagAndEncryptedMetadataBlock_t *out, const uint32_t block_index, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(out); // We can skip the check for text type because get_prefix_tag_count does it for us. uint32_t prefix_tag_count; if (!mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count(iev, &prefix_tag_count, status)) { return false; } if (block_index >= prefix_tag_count) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_get_prefix_metadata must be called with index block_index less " "than prefix tag count"); return false; } // Write edge into out struct *out = iev->metadata[block_index + iev->suffix_tag_count + iev->substr_tag_count + 1 /* exact block */]; return true; } bool mc_FLE2IndexedEncryptedValueV2_parse(mc_FLE2IndexedEncryptedValueV2_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(buf); if ((buf->data == NULL) || (buf->len == 0)) { CLIENT_ERR("Empty buffer passed to mc_FLE2IndexedEncryptedValueV2_parse"); return false; } if (iev->type != kFLE2IEVTypeInitV2) { CLIENT_ERR("mc_FLE2IndexedRangeEncryptedValueV2_parse must not be " "called twice"); return false; } mc_reader_t reader; mc_reader_init_from_buffer(&reader, buf, __func__); CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->fle_blob_subtype, status)); if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2) { iev->type = kFLE2IEVTypeEqualityV2; } else if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2) { iev->type = kFLE2IEVTypeRangeV2; } else if (iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedTextEncryptedValue) { iev->type = kFLE2IEVTypeText; } else { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse expected " "fle_blob_subtype MC_SUBTYPE_FLE2Indexed(Equality|Range|Text)EncryptedValue[V2] got: %" PRIu8, iev->fle_blob_subtype); return false; } /* Read S_KeyId. */ CHECK_AND_RETURN(mc_reader_read_uuid_buffer(&reader, &iev->S_KeyId, status)); /* Read original_bson_type. */ CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->bson_value_type, status)); /* Read edge_count, substr_tag_count, suffix_tag_count */ // Set equality edge_count to 1 as it doesn't technically exist but // there will be a singular metadata block // Set substr/suffix_tag_count to 0 for all types besides text iev->substr_tag_count = 0; iev->suffix_tag_count = 0; if (iev->type == kFLE2IEVTypeEqualityV2) { iev->edge_count = 1; } else { if (iev->type == kFLE2IEVTypeRangeV2) { uint8_t ec; CHECK_AND_RETURN(mc_reader_read_u8(&reader, &ec, status)); if (ec == 0) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse edge count must not be 0 for type " "range, but found edge count is 0."); return false; } iev->edge_count = (uint32_t)ec; } else if (iev->type == kFLE2IEVTypeText) { CHECK_AND_RETURN(mc_reader_read_u32(&reader, &iev->edge_count, status)); CHECK_AND_RETURN(mc_reader_read_u32(&reader, &iev->substr_tag_count, status)); CHECK_AND_RETURN(mc_reader_read_u32(&reader, &iev->suffix_tag_count, status)); // Upconvert so that addition doesn't overflow if ((uint64_t)iev->edge_count < (uint64_t)iev->substr_tag_count + (uint64_t)iev->suffix_tag_count + 1) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_parse for text type expected edge count to be " "at least substr_tag_count + suffix_count + 1, but: %" PRIu32 " < %" PRIu32 " + %" PRIu32 " + 1", iev->edge_count, iev->substr_tag_count, iev->suffix_tag_count); return false; } } } // Maximum edge_count(4294967295) times kMetadataLen(96) fits easily without overflowing uint64. const uint64_t metadata_len = (uint64_t)iev->edge_count * (uint64_t)kMetadataLen; /* Read ServerEncryptedValue. */ const uint64_t min_required_len = kMinServerEncryptedValueLen + metadata_len; const uint64_t SEV_and_metadata_len = mc_reader_get_remaining_length(&reader); if (SEV_and_metadata_len < min_required_len) { CLIENT_ERR("Invalid payload size %" PRIu64 ", smaller than minimum length %" PRIu64, SEV_and_metadata_len, min_required_len); return false; } const uint64_t SEV_len = SEV_and_metadata_len - metadata_len; CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &iev->ServerEncryptedValue, SEV_len, status)); iev->metadata = (mc_FLE2TagAndEncryptedMetadataBlock_t *)bson_malloc0( iev->edge_count * sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t)); // Read each metadata element in buff for (uint32_t i = 0; i < iev->edge_count; i++) { _mongocrypt_buffer_t tmp_buf; const uint8_t *mbuf = NULL; CHECK_AND_RETURN(mc_reader_read_bytes(&reader, &mbuf, kMetadataLen, status)); _mongocrypt_buffer_from_data(&tmp_buf, mbuf, kMetadataLen); CHECK_AND_RETURN(mc_FLE2TagAndEncryptedMetadataBlock_parse(&iev->metadata[i], &tmp_buf, status)); } return true; } static inline uint32_t mc_FLE2IndexedEncryptedValueV2_serialized_length(const mc_FLE2IndexedEncryptedValueV2_t *iev) { // fle_blob_subtype: 1 byte // S_KeyId: UUID_LEN bytes // bson_value_type: 1 byte // if range: edge_count: 1 byte // if text: edge + tag counts: 12 bytes // ServerEncryptedValue: ServerEncryptedValue.len bytes // metadata: edge_count * kMetadataLen bytes return iev->ServerEncryptedValue.len + 1 + UUID_LEN + 1 + (iev->type == kFLE2IEVTypeRangeV2 ? 1 : 0) + (iev->type == kFLE2IEVTypeText ? 12 : 0) + iev->edge_count * kMetadataLen; } bool mc_FLE2IndexedEncryptedValueV2_serialize(const mc_FLE2IndexedEncryptedValueV2_t *iev, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(buf); if (iev->type != kFLE2IEVTypeRangeV2 && iev->type != kFLE2IEVTypeEqualityV2 && iev->type != kFLE2IEVTypeText) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_serialize must be called with type equality, range, or text"); return false; } uint32_t expected_len = mc_FLE2IndexedEncryptedValueV2_serialized_length(iev); mc_writer_t writer; _mongocrypt_buffer_resize(buf, expected_len); mc_writer_init_from_buffer(&writer, buf, __func__); // Serialize fle_blob_subtype CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->fle_blob_subtype, status)); // Serialize S_KeyId CHECK_AND_RETURN(mc_writer_write_uuid_buffer(&writer, &iev->S_KeyId, status)); // Serialize bson_value_type CHECK_AND_RETURN(mc_writer_write_u8(&writer, iev->bson_value_type, status)); if (iev->type == kFLE2IEVTypeRangeV2) { // Serialize edge_count (only serialized for types range and text) CHECK_AND_RETURN(mc_writer_write_u8(&writer, (uint8_t)iev->edge_count, status)); } else if (iev->type == kFLE2IEVTypeText) { // Serialize substr/suffix_tag_count (only serialized for text) CHECK_AND_RETURN(mc_writer_write_u32(&writer, iev->edge_count, status)); CHECK_AND_RETURN(mc_writer_write_u32(&writer, iev->substr_tag_count, status)); CHECK_AND_RETURN(mc_writer_write_u32(&writer, iev->suffix_tag_count, status)); } // Serialize encrypted value CHECK_AND_RETURN( mc_writer_write_buffer(&writer, &iev->ServerEncryptedValue, iev->ServerEncryptedValue.len, status)); // Serialize metadata for (uint32_t i = 0; i < iev->edge_count; ++i) { _mongocrypt_buffer_t tmp_buf; _mongocrypt_buffer_init(&tmp_buf); CHECK_AND_RETURN(mc_FLE2TagAndEncryptedMetadataBlock_serialize(&iev->metadata[i], &tmp_buf, status)); CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &tmp_buf, kMetadataLen, status)); _mongocrypt_buffer_cleanup(&tmp_buf); } return true; } static bool is_fle2_equality_indexed_supported_type(int bson_type) { switch (bson_type) { case BSON_TYPE_BINARY: case BSON_TYPE_CODE: case BSON_TYPE_REGEX: case BSON_TYPE_UTF8: case BSON_TYPE_INT32: case BSON_TYPE_INT64: case BSON_TYPE_BOOL: case BSON_TYPE_TIMESTAMP: case BSON_TYPE_DATE_TIME: case BSON_TYPE_OID: case BSON_TYPE_SYMBOL: case BSON_TYPE_DBPOINTER: return true; default: // All other defined types are non-deterministic or singletons. return false; } } static bool is_fle2_range_indexed_supported_type(int bson_type) { switch (bson_type) { case BSON_TYPE_INT32: case BSON_TYPE_INT64: case BSON_TYPE_DATE_TIME: case BSON_TYPE_DOUBLE: #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() case BSON_TYPE_DECIMAL128: #endif return true; default: return false; } } static bool is_fle2_text_indexed_supported_type(int bson_type) { return bson_type == BSON_TYPE_UTF8; } #define CHECK(condition, msg) \ do { \ if (!(condition)) { \ CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_validate failed: " msg); \ return false; \ } \ } while (0) static bool validate_for_equality(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { CHECK(iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2, "fle_blob_subtype does not match type"); CHECK(is_fle2_equality_indexed_supported_type(iev->bson_value_type), "bson_value_type is invalid"); CHECK(iev->edge_count == 1, "edge_count must be 1 for equality"); return true; } static bool validate_for_range(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { CHECK(iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2, "fle_blob_subtype does not match type"); CHECK(is_fle2_range_indexed_supported_type(iev->bson_value_type), "bson_value_type is invalid"); if (iev->edge_count > (uint32_t)UINT8_MAX) { CLIENT_ERR("mc_FLE2IndexedEncryptedValueV2_validate failed: edge count for range encrypted value " "must be less than max uint8_t. Got: %" PRIu32, iev->edge_count); } return true; } static bool validate_for_text(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { CHECK(iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedTextEncryptedValue, "fle_blob_subtype does not match type"); CHECK(is_fle2_text_indexed_supported_type(iev->bson_value_type), "bson_value_type is invalid"); CHECK((uint64_t)iev->edge_count >= (uint64_t)iev->substr_tag_count + (uint64_t)iev->suffix_tag_count + 1, "edge_count is smaller than substr_tag_count + suffix_tag_count + 1"); return true; } bool mc_FLE2IndexedEncryptedValueV2_validate(const mc_FLE2IndexedEncryptedValueV2_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); CHECK(iev->type == kFLE2IEVTypeEqualityV2 || iev->type == kFLE2IEVTypeRangeV2 || iev->type == kFLE2IEVTypeText, "type was init or unknown"); if (iev->type == kFLE2IEVTypeEqualityV2) { validate_for_equality(iev, status); } else if (iev->type == kFLE2IEVTypeRangeV2) { validate_for_range(iev, status); } else { validate_for_text(iev, status); } if (!mongocrypt_status_ok(status)) { return false; } CHECK(iev->ServerEncryptedValue.len >= kMinServerEncryptedValueLen, "SEV.len is less than minimum"); CHECK(iev->S_KeyId.len == UUID_LEN, "S_KeyId is not the correct length for a UUID"); CHECK(!iev->ClientValueDecoded || iev->ClientEncryptedValueDecoded, "Found decrypted client value without encrypted client value"); if (iev->ClientEncryptedValueDecoded) { const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm(); const uint32_t DecryptedServerEncryptedValueLen = fle2alg->get_plaintext_len(iev->ServerEncryptedValue.len, status); if (!mongocrypt_status_ok(status)) { return false; } CHECK(iev->DecryptedServerEncryptedValue.len == DecryptedServerEncryptedValueLen, "DSEV.len was unexpected"); CHECK(iev->ClientEncryptedValue.len == iev->DecryptedServerEncryptedValue.len - UUID_LEN, "CEV.len was unexpected"); CHECK(iev->K_KeyId.len == UUID_LEN, "K_KeyId is not the correct length for a UUID"); } if (iev->ClientValueDecoded) { const _mongocrypt_value_encryption_algorithm_t *fle2v2aead = _mcFLE2v2AEADAlgorithm(); const uint32_t ClientValueLen = fle2v2aead->get_plaintext_len(iev->ClientEncryptedValue.len, status); if (!mongocrypt_status_ok(status)) { return false; } CHECK(iev->ClientValue.len == ClientValueLen, "ClientValue.len was unexpected"); } CHECK(iev->edge_count > 0, "edge_count must be at least 1"); for (uint32_t i = 0; i < iev->edge_count; i++) { if (!mc_FLE2TagAndEncryptedMetadataBlock_validate(&iev->metadata[i], status)) { return false; } } return true; } libmongocrypt-1.19.0/src/mc-fle2-payload-iev.c000066400000000000000000000516231521103432300210630ustar00rootroot00000000000000/* * Copyright 2022-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-buffer-private.h" #include "mongocrypt-private.h" #include "mc-fle-blob-subtype-private.h" #include "mc-fle2-payload-iev-private.h" #include "mc-reader-private.h" #include "mc-tokens-private.h" #include "mc-writer-private.h" #include #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) struct _mc_FLE2IndexedEqualityEncryptedValue_t { _mongocrypt_buffer_t S_KeyId; _mongocrypt_buffer_t InnerEncrypted; _mongocrypt_buffer_t Inner; _mongocrypt_buffer_t K_KeyId; _mongocrypt_buffer_t ClientValue; _mongocrypt_buffer_t ClientEncryptedValue; uint8_t original_bson_type; uint8_t fle_blob_subtype; bool parsed; bool inner_decrypted; bool client_value_decrypted; }; mc_FLE2IndexedEncryptedValue_t *mc_FLE2IndexedEncryptedValue_new(void) { return bson_malloc0(sizeof(mc_FLE2IndexedEncryptedValue_t)); } mc_FLE2IndexedEqualityEncryptedValueTokens *mc_FLE2IndexedEqualityEncryptedValueTokens_new(void) { return bson_malloc0(sizeof(mc_FLE2IndexedEqualityEncryptedValueTokens)); } bool mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer(mc_FLE2IndexedEqualityEncryptedValueTokens *tokens, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(tokens); BSON_ASSERT_PARAM(buf); mc_reader_t reader; mc_reader_init_from_buffer(&reader, buf, __func__); CHECK_AND_RETURN(mc_reader_read_u64(&reader, &tokens->counter, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &tokens->edc, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &tokens->esc, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &tokens->ecc, status)); return true; } static bool mc_fle2IndexedEncryptedValue_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *ClientEncryptedValue, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); static bool safe_uint32_t_sum(const uint32_t a, const uint32_t b, uint32_t *out, mongocrypt_status_t *status) { if (a > UINT32_MAX - b) { CLIENT_ERR("safe_uint32_t_sum overflow, %" PRIu32 ", %" PRIu32, a, b); return false; } *out = a + b; return true; } bool mc_FLE2IndexedEncryptedValue_write(_mongocrypt_crypto_t *crypto, const bson_type_t original_bson_type, const _mongocrypt_buffer_t *S_KeyId, const _mongocrypt_buffer_t *ClientEncryptedValue, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { #define CHECK_AND_GOTO(x) \ if (!(x)) { \ goto cleanup; \ } else \ ((void)0) bool ok = false; BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(index_tokens); BSON_ASSERT_PARAM(S_KeyId); BSON_ASSERT_PARAM(ClientEncryptedValue); BSON_ASSERT_PARAM(token); BSON_ASSERT_PARAM(index_tokens); BSON_ASSERT_PARAM(buf); if (ClientEncryptedValue->len == 0) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_write iev must have an encrypted value"); return ok; } if (S_KeyId->len == 0) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_write iev SKeyId must have value"); return ok; } _mongocrypt_buffer_t encryption_out; _mongocrypt_buffer_init(&encryption_out); CHECK_AND_GOTO(mc_fle2IndexedEncryptedValue_encrypt(crypto, ClientEncryptedValue, token, index_tokens, &encryption_out, status)); mc_writer_t writer; mc_writer_init_from_buffer(&writer, buf, __func__); const uint8_t subtype = (uint8_t)MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue; if (((int)original_bson_type < 0) || ((int)original_bson_type > 0xFF)) { CLIENT_ERR("Field 't' must be a valid BSON type, got: %d", (int)original_bson_type); CHECK_AND_GOTO(false); } const uint8_t bson_type = (uint8_t)original_bson_type; CHECK_AND_GOTO(mc_writer_write_u8(&writer, subtype, status)); CHECK_AND_GOTO(mc_writer_write_buffer(&writer, S_KeyId, S_KeyId->len, status)); CHECK_AND_GOTO(mc_writer_write_u8(&writer, bson_type, status)); CHECK_AND_GOTO(mc_writer_write_buffer(&writer, &encryption_out, encryption_out.len, status)); ok = true; cleanup: _mongocrypt_buffer_cleanup(&encryption_out); return ok; #undef CHECK_AND_GOTO } static bool mc_fle2IndexedEncryptedValue_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *ClientEncryptedValue, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { #define CHECK_AND_GOTO(x) \ if (!(x)) { \ goto cleanup; \ } else \ ((void)0) const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm(); bool ok = false; _mongocrypt_buffer_t in; _mongocrypt_buffer_t iv; _mongocrypt_buffer_init(&in); _mongocrypt_buffer_init_size(&iv, MONGOCRYPT_IV_LEN); uint32_t expected_buf_size = 0; CHECK_AND_GOTO(safe_uint32_t_sum(ClientEncryptedValue->len, (uint32_t)(sizeof(uint64_t) * 2 + (32 * 3)), &expected_buf_size, status)); _mongocrypt_buffer_resize(&in, expected_buf_size); uint32_t ciphertext_len = fle2alg->get_ciphertext_len(expected_buf_size, status); if (ciphertext_len == 0) { goto cleanup; } _mongocrypt_buffer_resize(out, ciphertext_len); mc_writer_t writer; mc_writer_init_from_buffer(&writer, &in, __func__); uint64_t length; length = ClientEncryptedValue->len; CHECK_AND_GOTO(mc_writer_write_u64(&writer, length, status)); CHECK_AND_GOTO(mc_writer_write_buffer(&writer, ClientEncryptedValue, ClientEncryptedValue->len, status)); CHECK_AND_GOTO(mc_writer_write_u64(&writer, index_tokens->counter, status)); CHECK_AND_GOTO(mc_writer_write_prfblock_buffer(&writer, &index_tokens->edc, status)); CHECK_AND_GOTO(mc_writer_write_prfblock_buffer(&writer, &index_tokens->esc, status)); CHECK_AND_GOTO(mc_writer_write_prfblock_buffer(&writer, &index_tokens->ecc, status)); const _mongocrypt_buffer_t *token_buf = mc_ServerDataEncryptionLevel1Token_get(token); uint32_t bytes_written; CHECK_AND_GOTO(_mongocrypt_random(crypto, &iv, MONGOCRYPT_IV_LEN, status)); CHECK_AND_GOTO(fle2alg->do_encrypt(crypto, &iv, NULL /* aad */, token_buf, &in, out, &bytes_written, status)); ok = true; cleanup: _mongocrypt_buffer_cleanup(&iv); _mongocrypt_buffer_cleanup(&in); return ok; #undef CHECK_AND_GOTO } bool mc_FLE2IndexedEncryptedValue_parse(mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(buf); if (iev->parsed) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_parse must not be called twice"); return false; } mc_reader_t reader; mc_reader_init_from_buffer(&reader, buf, __func__); CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->fle_blob_subtype, status)); if (iev->fle_blob_subtype != MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue && iev->fle_blob_subtype != MC_SUBTYPE_FLE2IndexedRangeEncryptedValue) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_parse expected " "fle_blob_subtype %d or %d got: %" PRIu8, MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue, MC_SUBTYPE_FLE2IndexedRangeEncryptedValue, iev->fle_blob_subtype); return false; } /* Read S_KeyId. */ CHECK_AND_RETURN(mc_reader_read_uuid_buffer(&reader, &iev->S_KeyId, status)); /* Read original_bson_type. */ CHECK_AND_RETURN(mc_reader_read_u8(&reader, &iev->original_bson_type, status)); /* Read InnerEncrypted. */ CHECK_AND_RETURN(mc_reader_read_buffer_to_end(&reader, &iev->InnerEncrypted, status)); iev->parsed = true; return true; } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_S_KeyId(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->parsed) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_get_S_KeyId must be " "called after mc_FLE2IndexedEncryptedValue_parse"); return NULL; } return &iev->S_KeyId; } static bool mc_FLE2IndexedEncryptedValue_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *indexed_tokens, mongocrypt_status_t *status); bool mc_FLE2IndexedEncryptedValue_add_S_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *S_Key, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(S_Key); /* Attempt to decrypt InnerEncrypted */ if (S_Key->len != MONGOCRYPT_KEY_LEN) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_add_S_Key expected " "S_Key to be %d bytes, got: %" PRIu32, MONGOCRYPT_KEY_LEN, S_Key->len); return false; } /* Get the TokenKey from the last 32 bytes of S_Key */ _mongocrypt_buffer_t TokenKey; if (!_mongocrypt_buffer_from_subrange(&TokenKey, S_Key, S_Key->len - MONGOCRYPT_TOKEN_KEY_LEN, MONGOCRYPT_TOKEN_KEY_LEN)) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_add_S_Key unable to " "parse TokenKey from S_Key"); return false; } /* Use TokenKey to create ServerDataEncryptionLevel1Token and decrypt * InnerEncrypted. */ mc_ServerDataEncryptionLevel1Token_t *token = mc_ServerDataEncryptionLevel1Token_new(crypto, &TokenKey, status); if (!token) { return false; } bool ret = mc_FLE2IndexedEncryptedValue_decrypt(crypto, iev, token, NULL, status); mc_ServerDataEncryptionLevel1Token_destroy(token); return ret; } static bool mc_FLE2IndexedEncryptedValue_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *indexed_tokens, mongocrypt_status_t *status) { const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm(); BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(token); if (!iev->parsed) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_decrypt must be " "called after mc_FLE2IndexedEncryptedValue_parse"); return false; } if (iev->inner_decrypted) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_decrypt must not be " "called twice"); return false; } const _mongocrypt_buffer_t *token_buf = mc_ServerDataEncryptionLevel1Token_get(token); uint32_t bytes_written; _mongocrypt_buffer_resize(&iev->Inner, fle2alg->get_plaintext_len(iev->InnerEncrypted.len, status)); /* Decrypt InnerEncrypted. */ if (!fle2alg->do_decrypt(crypto, NULL /* aad */, token_buf, &iev->InnerEncrypted, &iev->Inner, &bytes_written, status)) { return false; } mc_reader_t reader; mc_reader_init_from_buffer(&reader, &iev->Inner, __func__); /* Parse Inner for K_KeyId. */ uint64_t length; /* length is sizeof(K_KeyId) + ClientEncryptedValue_length. */ CHECK_AND_RETURN(mc_reader_read_u64(&reader, &length, status)); /* Read K_KeyId. */ CHECK_AND_RETURN(mc_reader_read_uuid_buffer(&reader, &iev->K_KeyId, status)); /* Read ClientEncryptedValue. */ uint64_t expected_length = mc_reader_get_consumed_length(&reader) + length - 16; if (length > iev->Inner.len || expected_length > iev->Inner.len) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_decrypt expected " "byte length >= %" PRIu64 " got: %" PRIu32, expected_length, iev->Inner.len); return false; } CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &iev->ClientEncryptedValue, length - 16, status)); // Caller has asked us to parse the other tokens if (indexed_tokens != NULL) { CHECK_AND_RETURN(mc_reader_read_u64(&reader, &indexed_tokens->counter, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &indexed_tokens->edc, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &indexed_tokens->esc, status)); CHECK_AND_RETURN(mc_reader_read_prfblock_buffer(&reader, &indexed_tokens->ecc, status)); } iev->inner_decrypted = true; return true; } bool mc_FLE2IndexedEncryptedValue_decrypt_equality(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, mc_ServerDataEncryptionLevel1Token_t *token, mc_FLE2IndexedEqualityEncryptedValueTokens *indexed_tokens, mongocrypt_status_t *status) { BSON_ASSERT(iev->fle_blob_subtype == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue); return mc_FLE2IndexedEncryptedValue_decrypt(crypto, iev, token, indexed_tokens, status); } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_K_KeyId(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->inner_decrypted) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_get_K_KeyId must be called " "after mc_FLE2IndexedEncryptedValue_add_S_Key"); return NULL; } return &iev->K_KeyId; } bool mc_FLE2IndexedEncryptedValue_add_K_Key(_mongocrypt_crypto_t *crypto, mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *K_Key, mongocrypt_status_t *status) { const _mongocrypt_value_encryption_algorithm_t *fle2aead = _mcFLE2AEADAlgorithm(); BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(iev); BSON_ASSERT_PARAM(K_Key); if (!iev->inner_decrypted) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_add_K_Key must be called after " "mc_FLE2IndexedEncryptedValue_add_S_Key"); return false; } if (iev->client_value_decrypted) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_add_K_Key must not be " "called twice"); return false; } /* Attempt to decrypt ClientEncryptedValue */ _mongocrypt_buffer_resize(&iev->ClientValue, fle2aead->get_plaintext_len(iev->ClientEncryptedValue.len, status)); uint32_t bytes_written; if (!fle2aead->do_decrypt(crypto, &iev->K_KeyId, K_Key, &iev->ClientEncryptedValue, &iev->ClientValue, &bytes_written, status)) { return false; } iev->client_value_decrypted = true; return true; } const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValue_get_ClientEncryptedValue(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); return &iev->ClientEncryptedValue; } const _mongocrypt_buffer_t *mc_FLE2IndexedEncryptedValue_get_ClientValue(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->client_value_decrypted) { CLIENT_ERR("mc_FLE2IndexedEqualityEncryptedValue_getClientValue must be called " "after mc_FLE2IndexedEncryptedValue_add_K_Key"); return NULL; } return &iev->ClientValue; } void mc_FLE2IndexedEncryptedValue_destroy(mc_FLE2IndexedEncryptedValue_t *iev) { if (!iev) { return; } _mongocrypt_buffer_cleanup(&iev->S_KeyId); _mongocrypt_buffer_cleanup(&iev->InnerEncrypted); _mongocrypt_buffer_cleanup(&iev->Inner); _mongocrypt_buffer_cleanup(&iev->K_KeyId); _mongocrypt_buffer_cleanup(&iev->ClientValue); _mongocrypt_buffer_cleanup(&iev->ClientEncryptedValue); bson_free(iev); } void mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(mc_FLE2IndexedEqualityEncryptedValueTokens *tokens) { if (!tokens) { return; } _mongocrypt_buffer_cleanup(&tokens->edc); _mongocrypt_buffer_cleanup(&tokens->esc); _mongocrypt_buffer_cleanup(&tokens->ecc); bson_free(tokens); } bson_type_t mc_FLE2IndexedEncryptedValue_get_original_bson_type(const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iev); if (!iev->parsed) { CLIENT_ERR("mc_FLE2IndexedEncryptedValue_get_original_bson_type must be " "called after mc_FLE2IndexedEncryptedValue_parse"); return 0; } return iev->original_bson_type; } libmongocrypt-1.19.0/src/mc-fle2-payload-uev-common-private.h000066400000000000000000000073001521103432300240330ustar00rootroot00000000000000/* * Copyright 2023-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. */ #ifndef MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H #define MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H #include "mc-fle-blob-subtype-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-status-private.h" /** * Deserializes the data in @buf and assigns the parsed values to * to the output parameters. * Callers are expected to call _mongocrypt_buffer_init() on the * @key_uuid and @ciphertext output buffers prior to this call, and * call _mongocrypt_buffer_cleanup() afterwards. * Returns false and sets @status on error. */ bool _mc_FLE2UnindexedEncryptedValueCommon_parse(const _mongocrypt_buffer_t *buf, uint8_t *fle_blob_subtype, uint8_t *original_bson_type, _mongocrypt_buffer_t *key_uuid, _mongocrypt_buffer_t *ciphertext, mongocrypt_status_t *status); /** * Decrypts @ciphertext onto the @plaintext buffer. The @plaintext * pointer is returned on success. The @fle_blob_subtype must be an * unindexed type, and determines the decryption algorithm to use. * Returns NULL and sets @status on error. */ const _mongocrypt_buffer_t *_mc_FLE2UnindexedEncryptedValueCommon_decrypt(_mongocrypt_crypto_t *crypto, mc_fle_blob_subtype_t fle_blob_subtype, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *ciphertext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *plaintext, mongocrypt_status_t *status); /** * Encrypts @plaintext onto the @out buffer. The @fle_blob_subtype must * be an unindexed type, and determines the encryption algorithm to use. * Returns false and sets @status on error. */ bool _mc_FLE2UnindexedEncryptedValueCommon_encrypt(_mongocrypt_crypto_t *crypto, mc_fle_blob_subtype_t fle_blob_subtype, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); #endif /* MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_COMMON_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-payload-uev-common.c000066400000000000000000000175531521103432300223710ustar00rootroot00000000000000/* * Copyright 2023-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 "mc-fle2-payload-uev-common-private.h" #include "mc-reader-private.h" #include "mongocrypt-private.h" #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) bool _mc_FLE2UnindexedEncryptedValueCommon_parse(const _mongocrypt_buffer_t *buf, uint8_t *fle_blob_subtype, uint8_t *original_bson_type, _mongocrypt_buffer_t *key_uuid, _mongocrypt_buffer_t *ciphertext, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(fle_blob_subtype); BSON_ASSERT_PARAM(original_bson_type); BSON_ASSERT_PARAM(key_uuid); BSON_ASSERT_PARAM(ciphertext); mc_reader_t reader; mc_reader_init_from_buffer(&reader, buf, __func__); /* Read fle_blob_subtype. */ CHECK_AND_RETURN(mc_reader_read_u8(&reader, fle_blob_subtype, status)); /* Read key_uuid. */ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, key_uuid, 16, status)); key_uuid->subtype = BSON_SUBTYPE_UUID; /* Read original_bson_type. */ CHECK_AND_RETURN(mc_reader_read_u8(&reader, original_bson_type, status)); /* Read ciphertext. */ CHECK_AND_RETURN(mc_reader_read_buffer(&reader, ciphertext, mc_reader_get_remaining_length(&reader), status)); return true; } const _mongocrypt_buffer_t *_mc_FLE2UnindexedEncryptedValueCommon_decrypt(_mongocrypt_crypto_t *crypto, mc_fle_blob_subtype_t fle_blob_subtype, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *ciphertext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *plaintext, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(key_uuid); BSON_ASSERT_PARAM(ciphertext); BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(plaintext); BSON_ASSERT(MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype || MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 == fle_blob_subtype); const _mongocrypt_value_encryption_algorithm_t *fle2aead = (MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype) ? _mcFLE2AEADAlgorithm() : _mcFLE2v2AEADAlgorithm(); /* Serialize associated data: fle_blob_subtype || key_uuid || * original_bson_type */ _mongocrypt_buffer_t AD; _mongocrypt_buffer_init(&AD); if (key_uuid->len > UINT32_MAX - 2) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueCommon_decrypt expected " "key UUID length <= %" PRIu32 " got: %" PRIu32, UINT32_MAX - 2u, key_uuid->len); return NULL; } _mongocrypt_buffer_resize(&AD, 1 + key_uuid->len + 1); AD.data[0] = (uint8_t)fle_blob_subtype; memcpy(AD.data + 1, key_uuid->data, key_uuid->len); AD.data[1 + key_uuid->len] = (uint8_t)original_bson_type; const uint32_t plaintext_len = fle2aead->get_plaintext_len(ciphertext->len, status); if (plaintext_len == 0) { _mongocrypt_buffer_cleanup(&AD); return NULL; } _mongocrypt_buffer_resize(plaintext, plaintext_len); uint32_t bytes_written; if (!fle2aead->do_decrypt(crypto, &AD, key, ciphertext, plaintext, &bytes_written, status)) { _mongocrypt_buffer_cleanup(&AD); return NULL; } // Some block cipher modes (eg. CBC) may write fewer bytes than the size // estimate that the plaintext buffer was allocated with. Therefore, the // plaintext buffer length must be updated to the actual size written. plaintext->len = bytes_written; _mongocrypt_buffer_cleanup(&AD); return plaintext; } bool _mc_FLE2UnindexedEncryptedValueCommon_encrypt(_mongocrypt_crypto_t *crypto, mc_fle_blob_subtype_t fle_blob_subtype, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { _mongocrypt_buffer_t iv = {0}; _mongocrypt_buffer_t AD = {0}; bool res = false; BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(key_uuid); BSON_ASSERT_PARAM(plaintext); BSON_ASSERT_PARAM(key); BSON_ASSERT_PARAM(out); BSON_ASSERT(MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype || MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 == fle_blob_subtype); const _mongocrypt_value_encryption_algorithm_t *fle2aead = (MC_SUBTYPE_FLE2UnindexedEncryptedValue == fle_blob_subtype) ? _mcFLE2AEADAlgorithm() : _mcFLE2v2AEADAlgorithm(); _mongocrypt_buffer_resize(&iv, MONGOCRYPT_IV_LEN); if (!_mongocrypt_random(crypto, &iv, MONGOCRYPT_IV_LEN, status)) { goto fail; } /* Serialize associated data: fle_blob_subtype || key_uuid || * original_bson_type */ { if (key_uuid->len > UINT32_MAX - 2) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueCommon_encrypt expected " "key UUID length <= %" PRIu32 " got: %" PRIu32, UINT32_MAX - 2u, key_uuid->len); goto fail; } _mongocrypt_buffer_resize(&AD, 1 + key_uuid->len + 1); AD.data[0] = (uint8_t)fle_blob_subtype; memcpy(AD.data + 1, key_uuid->data, key_uuid->len); AD.data[1 + key_uuid->len] = (uint8_t)original_bson_type; } /* Encrypt. */ { const uint32_t cipherlen = fle2aead->get_ciphertext_len(plaintext->len, status); if (cipherlen == 0) { goto fail; } _mongocrypt_buffer_resize(out, cipherlen); uint32_t bytes_written; /* unused. */ if (!fle2aead->do_encrypt(crypto, &iv, &AD, key, plaintext, out, &bytes_written, status)) { goto fail; } } res = true; fail: _mongocrypt_buffer_cleanup(&AD); _mongocrypt_buffer_cleanup(&iv); return res; } libmongocrypt-1.19.0/src/mc-fle2-payload-uev-private.h000066400000000000000000000077241521103432300225570ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_PRIVATE_H #define MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-status-private.h" /** * FLE2UnindexedEncryptedValue represents an FLE2 unindexed encrypted value. * It is created client side. * * FLE2UnindexedEncryptedValue has the following data layout: * * struct { * uint8_t fle_blob_subtype = 6; * uint8_t key_uuid[16]; * uint8_t original_bson_type; * uint8_t ciphertext[ciphertext_length]; * } FLE2UnindexedEncryptedValue; * * ciphertext is the output of: * EncryptAEAD_AES_256_CTR_HMAC_SHA_256( * key=K_Key, * plaintext=ClientValue, * associated_data=(fle_blob_subtype || key_uuid || original_bson_type)) */ typedef struct _mc_FLE2UnindexedEncryptedValue_t mc_FLE2UnindexedEncryptedValue_t; mc_FLE2UnindexedEncryptedValue_t *mc_FLE2UnindexedEncryptedValue_new(void); bool mc_FLE2UnindexedEncryptedValue_parse(mc_FLE2UnindexedEncryptedValue_t *uev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValue_get_original_bson_type returns * original_bson_type. Returns 0 and sets @status on error. * It is an error to call before mc_FLE2UnindexedEncryptedValue_parse. */ bson_type_t mc_FLE2UnindexedEncryptedValue_get_original_bson_type(const mc_FLE2UnindexedEncryptedValue_t *uev, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValue_get_key_uuid returns key_uuid. Returns * NULL and sets @status on error. It is an error to call before * mc_FLE2UnindexedEncryptedValue_parse. */ const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValue_get_key_uuid(const mc_FLE2UnindexedEncryptedValue_t *uev, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValue_decrypt decrypts ciphertext. * Returns NULL and sets @status on error. It is an error to call before * mc_FLE2UnindexedEncryptedValue_parse. */ const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValue_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2UnindexedEncryptedValue_t *uev, const _mongocrypt_buffer_t *key, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValue_encrypt outputs the ciphertext field of * FLEUnindexedEncryptedValue into @out. Returns false and sets @status on * error. */ bool mc_FLE2UnindexedEncryptedValue_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); void mc_FLE2UnindexedEncryptedValue_destroy(mc_FLE2UnindexedEncryptedValue_t *uev); #endif /* MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-payload-uev-v2-private.h000066400000000000000000000100761521103432300230760ustar00rootroot00000000000000/* * Copyright 2023-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. */ #ifndef MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H #define MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt-status-private.h" /** * FLE2UnindexedEncryptedValueV2 represents a FLE2 protocol version 2 * unindexed encrypted value. It is created client side. * * FLE2UnindexedEncryptedValueV2 has the following data layout: * * struct { * uint8_t fle_blob_subtype = 16; * uint8_t key_uuid[16]; * uint8_t original_bson_type; * uint8_t ciphertext[ciphertext_length]; * } FLE2UnindexedEncryptedValueV2; * * ciphertext is the output of: * EncryptAEAD_AES_256_CBC_HMAC_SHA_256( * key=K_Key, * plaintext=ClientValue, * associated_data=(fle_blob_subtype || key_uuid || original_bson_type)) */ typedef struct _mc_FLE2UnindexedEncryptedValueV2_t mc_FLE2UnindexedEncryptedValueV2_t; mc_FLE2UnindexedEncryptedValueV2_t *mc_FLE2UnindexedEncryptedValueV2_new(void); bool mc_FLE2UnindexedEncryptedValueV2_parse(mc_FLE2UnindexedEncryptedValueV2_t *uev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type returns * original_bson_type. Returns 0 and sets @status on error. * It is an error to call before mc_FLE2UnindexedEncryptedValueV2_parse. */ bson_type_t mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type(const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValueV2_get_key_uuid returns key_uuid. Returns * NULL and sets @status on error. It is an error to call before * mc_FLE2UnindexedEncryptedValueV2_parse. */ const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValueV2_decrypt decrypts ciphertext. * Returns NULL and sets @status on error. It is an error to call before * mc_FLE2UnindexedEncryptedValueV2_parse. */ const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValueV2_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2UnindexedEncryptedValueV2_t *uev, const _mongocrypt_buffer_t *key, mongocrypt_status_t *status); /* mc_FLE2UnindexedEncryptedValueV2_encrypt outputs the ciphertext field of * FLEUnindexedEncryptedValueV2 into @out. Returns false and sets @status on * error. */ bool mc_FLE2UnindexedEncryptedValueV2_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); void mc_FLE2UnindexedEncryptedValueV2_destroy(mc_FLE2UnindexedEncryptedValueV2_t *uev); #endif /* MONGOCRYPT_FLE2_UNINDEXED_ENCRYPTED_VALUE_V2_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-fle2-payload-uev-v2.c000066400000000000000000000137411521103432300214230ustar00rootroot00000000000000/* * Copyright 2023-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 "mc-fle2-payload-uev-common-private.h" #include "mc-fle2-payload-uev-v2-private.h" #include "mongocrypt-private.h" struct _mc_FLE2UnindexedEncryptedValueV2_t { _mongocrypt_buffer_t key_uuid; uint8_t original_bson_type; _mongocrypt_buffer_t ciphertext; _mongocrypt_buffer_t plaintext; bool parsed; }; mc_FLE2UnindexedEncryptedValueV2_t *mc_FLE2UnindexedEncryptedValueV2_new(void) { mc_FLE2UnindexedEncryptedValueV2_t *uev = bson_malloc0(sizeof(mc_FLE2UnindexedEncryptedValueV2_t)); return uev; } bool mc_FLE2UnindexedEncryptedValueV2_parse(mc_FLE2UnindexedEncryptedValueV2_t *uev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); BSON_ASSERT_PARAM(buf); if (uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueV2_parse must not be called twice"); return false; } uint8_t fle_blob_subtype; if (!_mc_FLE2UnindexedEncryptedValueCommon_parse(buf, &fle_blob_subtype, &uev->original_bson_type, &uev->key_uuid, &uev->ciphertext, status)) { return false; } if (MC_SUBTYPE_FLE2UnindexedEncryptedValueV2 != fle_blob_subtype) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueV2_parse expected " "fle_blob_subtype=%d got: %" PRIu8, MC_SUBTYPE_FLE2UnindexedEncryptedValueV2, fle_blob_subtype); return false; } uev->parsed = true; return true; } bson_type_t mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type(const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type must be " "called after mc_FLE2UnindexedEncryptedValueV2_parse"); return 0; } return uev->original_bson_type; } const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(const mc_FLE2UnindexedEncryptedValueV2_t *uev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueV2_get_key_uuid must be " "called after mc_FLE2UnindexedEncryptedValueV2_parse"); return NULL; } return &uev->key_uuid; } const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValueV2_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2UnindexedEncryptedValueV2_t *uev, const _mongocrypt_buffer_t *key, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(uev); BSON_ASSERT_PARAM(key); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValueV2_decrypt must be " "called after mc_FLE2UnindexedEncryptedValueV2_parse"); return NULL; } return _mc_FLE2UnindexedEncryptedValueCommon_decrypt(crypto, MC_SUBTYPE_FLE2UnindexedEncryptedValueV2, &uev->key_uuid, uev->original_bson_type, &uev->ciphertext, key, &uev->plaintext, status); } bool mc_FLE2UnindexedEncryptedValueV2_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _mc_FLE2UnindexedEncryptedValueCommon_encrypt(crypto, MC_SUBTYPE_FLE2UnindexedEncryptedValueV2, key_uuid, original_bson_type, plaintext, key, out, status); } void mc_FLE2UnindexedEncryptedValueV2_destroy(mc_FLE2UnindexedEncryptedValueV2_t *uev) { if (NULL == uev) { return; } _mongocrypt_buffer_cleanup(&uev->key_uuid); _mongocrypt_buffer_cleanup(&uev->ciphertext); _mongocrypt_buffer_cleanup(&uev->plaintext); bson_free(uev); } libmongocrypt-1.19.0/src/mc-fle2-payload-uev.c000066400000000000000000000136141521103432300210750ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-fle2-payload-uev-common-private.h" #include "mc-fle2-payload-uev-private.h" #include "mongocrypt-private.h" struct _mc_FLE2UnindexedEncryptedValue_t { _mongocrypt_buffer_t key_uuid; uint8_t original_bson_type; _mongocrypt_buffer_t ciphertext; _mongocrypt_buffer_t plaintext; bool parsed; }; mc_FLE2UnindexedEncryptedValue_t *mc_FLE2UnindexedEncryptedValue_new(void) { mc_FLE2UnindexedEncryptedValue_t *uev = bson_malloc0(sizeof(mc_FLE2UnindexedEncryptedValue_t)); return uev; } bool mc_FLE2UnindexedEncryptedValue_parse(mc_FLE2UnindexedEncryptedValue_t *uev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); BSON_ASSERT_PARAM(buf); if (uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValue_parse must not be called twice"); return false; } uint8_t fle_blob_subtype; if (!_mc_FLE2UnindexedEncryptedValueCommon_parse(buf, &fle_blob_subtype, &uev->original_bson_type, &uev->key_uuid, &uev->ciphertext, status)) { return false; } if (fle_blob_subtype != MC_SUBTYPE_FLE2UnindexedEncryptedValue) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValue_parse expected " "fle_blob_subtype=%d got: %" PRIu8, MC_SUBTYPE_FLE2UnindexedEncryptedValue, fle_blob_subtype); return false; } uev->parsed = true; return true; } bson_type_t mc_FLE2UnindexedEncryptedValue_get_original_bson_type(const mc_FLE2UnindexedEncryptedValue_t *uev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValue_get_original_bson_type must be " "called after mc_FLE2UnindexedEncryptedValue_parse"); return 0; } return uev->original_bson_type; } const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValue_get_key_uuid(const mc_FLE2UnindexedEncryptedValue_t *uev, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(uev); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValue_get_key_uuid must be " "called after mc_FLE2UnindexedEncryptedValue_parse"); return NULL; } return &uev->key_uuid; } const _mongocrypt_buffer_t *mc_FLE2UnindexedEncryptedValue_decrypt(_mongocrypt_crypto_t *crypto, mc_FLE2UnindexedEncryptedValue_t *uev, const _mongocrypt_buffer_t *key, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(crypto); BSON_ASSERT_PARAM(uev); BSON_ASSERT_PARAM(key); if (!uev->parsed) { CLIENT_ERR("mc_FLE2UnindexedEncryptedValue_decrypt must be " "called after mc_FLE2UnindexedEncryptedValue_parse"); return NULL; } return _mc_FLE2UnindexedEncryptedValueCommon_decrypt(crypto, MC_SUBTYPE_FLE2UnindexedEncryptedValue, &uev->key_uuid, uev->original_bson_type, &uev->ciphertext, key, &uev->plaintext, status); } bool mc_FLE2UnindexedEncryptedValue_encrypt(_mongocrypt_crypto_t *crypto, const _mongocrypt_buffer_t *key_uuid, bson_type_t original_bson_type, const _mongocrypt_buffer_t *plaintext, const _mongocrypt_buffer_t *key, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { return _mc_FLE2UnindexedEncryptedValueCommon_encrypt(crypto, MC_SUBTYPE_FLE2UnindexedEncryptedValue, key_uuid, original_bson_type, plaintext, key, out, status); } void mc_FLE2UnindexedEncryptedValue_destroy(mc_FLE2UnindexedEncryptedValue_t *uev) { if (NULL == uev) { return; } _mongocrypt_buffer_cleanup(&uev->key_uuid); _mongocrypt_buffer_cleanup(&uev->ciphertext); _mongocrypt_buffer_cleanup(&uev->plaintext); bson_free(uev); } libmongocrypt-1.19.0/src/mc-fle2-range-operator-private.h000066400000000000000000000020231521103432300232410ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_FLE2_RANGE_OPERATOR_PRIVATE_H #define MC_FLE2_RANGE_OPERATOR_PRIVATE_H typedef enum { FLE2RangeOperator_kNone = 0, FLE2RangeOperator_kGt = 1, FLE2RangeOperator_kGte = 2, FLE2RangeOperator_kLt = 3, FLE2RangeOperator_kLte = 4, FLE2RangeOperator_min_val = FLE2RangeOperator_kNone, FLE2RangeOperator_max_val = FLE2RangeOperator_kLte } mc_FLE2RangeOperator_t; #endif // MC_FLE2_RANGE_OPERATOR_PRIVATE_H libmongocrypt-1.19.0/src/mc-fle2-rfds-private.h000066400000000000000000000075421521103432300212650ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-fle2-range-operator-private.h" #include "mc-rangeopts-private.h" #include "mongocrypt-buffer-private.h" #include #include typedef struct { const char *field; struct { bool set; bson_iter_t value; bool included; } lower; struct { bool set; bson_iter_t value; bool included; } upper; bool isAggregateExpression; int nOps; mc_FLE2RangeOperator_t firstOp; mc_FLE2RangeOperator_t secondOp; } mc_FLE2RangeFindDriverSpec_t; // `mc_FLE2RangeFindDriverSpec_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_FLE2RangeFindDriverSpec_t, BSON_ALIGNOF(mc_FLE2RangeFindDriverSpec_t) >= BSON_ALIGNOF(bson_iter_t)); // mc_FLE2RangeFindDriverSpec_parse parses a FLE2RangeFindDriverSpec document. bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec, const bson_t *in, mongocrypt_status_t *status); // mc_FLE2RangeFindDriverSpec_to_placeholders creates a new document with // placeholders to encrypt. // // `out` must be initialized by caller. bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *spec, const mc_RangeOpts_t *range_opts, int64_t maxContentionFactor, const _mongocrypt_buffer_t *user_key_id, const _mongocrypt_buffer_t *index_key_id, int32_t payloadId, bson_t *out, mongocrypt_status_t *status); typedef struct { // isStub is true when edgesInfo is not appended. bool isStub; const _mongocrypt_buffer_t *user_key_id; const _mongocrypt_buffer_t *index_key_id; bson_iter_t lowerBound; bool lbIncluded; bson_iter_t upperBound; bool ubIncluded; int32_t payloadId; mc_FLE2RangeOperator_t firstOp; mc_FLE2RangeOperator_t secondOp; bson_iter_t indexMin; bson_iter_t indexMax; int64_t maxContentionFactor; int64_t sparsity; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_makeRangeFindPlaceholder_args_t; // `mc_makeRangeFindPlaceholder_args_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_makeRangeFindPlaceholder_args_t, BSON_ALIGNOF(mc_makeRangeFindPlaceholder_args_t) >= BSON_ALIGNOF(bson_iter_t)); // mc_makeRangeFindPlaceholder creates a placeholder to be consumed by // libmongocrypt to encrypt a range find query. It is included in the header to // be used by tests. bool mc_makeRangeFindPlaceholder(mc_makeRangeFindPlaceholder_args_t *args, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); // mc_getNextPayloadId returns a payload ID. It is thread safe. It resets to 0 // after reaching INT32_MAX. int32_t mc_getNextPayloadId(void); libmongocrypt-1.19.0/src/mc-fle2-rfds.c000066400000000000000000000541161521103432300176070ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-fle2-rfds-private.h" #include "mc-fle-blob-subtype-private.h" // MC_SUBTYPE_FLE2EncryptionPlaceholder #include "mongocrypt-private.h" // CLIENT_ERR #include "mc-mlib/thread.h" // mlib_once_flag #include // INFINITY static mc_FLE2RangeOperator_t get_operator_type(const char *key) { BSON_ASSERT_PARAM(key); if (0 == strcmp(key, "$gt")) { return FLE2RangeOperator_kGt; } else if (0 == strcmp(key, "$gte")) { return FLE2RangeOperator_kGte; } else if (0 == strcmp(key, "$lt")) { return FLE2RangeOperator_kLt; } else if (0 == strcmp(key, "$lte")) { return FLE2RangeOperator_kLte; } else { return FLE2RangeOperator_kNone; } } static const char *mc_FLE2RangeOperator_to_string(mc_FLE2RangeOperator_t op) { switch (op) { case FLE2RangeOperator_kGt: return "$gt"; case FLE2RangeOperator_kGte: return "$gte"; case FLE2RangeOperator_kLt: return "$lt"; case FLE2RangeOperator_kLte: return "$lte"; case FLE2RangeOperator_kNone: return "none"; default: return "Unknown"; } } static bool is_supported_operator(const char *key) { BSON_ASSERT_PARAM(key); return get_operator_type(key) != FLE2RangeOperator_kNone; } // Suffix an error message with the JSON string form of bson. #define ERR_WITH_BSON(bson, fmt, ...) \ if (1) { \ char *_str = bson_as_canonical_extended_json((bson), NULL); \ CLIENT_ERR(fmt ": %s", __VA_ARGS__, _str); \ bson_free(_str); \ } else \ (void)0 // Parses a document like {$and: []} and outputs an iterator to the array. static bool parse_and(const bson_t *in, bson_iter_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bson_iter_t and = {0}; if (!bson_iter_init(&and, in) || !bson_iter_next(&and) || 0 != strcmp(bson_iter_key(&and), "$and")) { ERR_WITH_BSON(in, "%s", "error unable to find '$and'"); return false; } if (!BSON_ITER_HOLDS_ARRAY(&and)) { ERR_WITH_BSON(in, "%s", "expected '$and' to be array"); return false; } *out = and; if (bson_iter_next(&and)) { ERR_WITH_BSON(in, "unexpected extra key '%s' after '$and'", bson_iter_key(&and)); return false; } return true; } typedef struct { const char *op_type_str; const char *field; bson_iter_t value; mc_FLE2RangeOperator_t op_type; } operator_value_t; // Parses a document like {$gt: ["$age", 5]}. static bool parse_aggregate_expression(const bson_t *orig, bson_iter_t *in, operator_value_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(orig); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bson_iter_t array = {0}, value; const char *op_type_str = bson_iter_key(in); bool ok = false; const char *field; if (!BSON_ITER_HOLDS_ARRAY(in)) { ERR_WITH_BSON(orig, "%s", "expected argument to be array"); goto fail; } if (!bson_iter_recurse(in, &array)) { ERR_WITH_BSON(orig, "%s", "failed to recurse into array"); goto fail; } // Expect exactly 2 elements, like ["$age", 5]. The first element is the // field path. The second element is the value. if (!bson_iter_next(&array)) { ERR_WITH_BSON(orig, "expected 2 elements in operand %s, got 0", op_type_str); goto fail; } if (!BSON_ITER_HOLDS_UTF8(&array)) { ERR_WITH_BSON(orig, "expected UTF-8 as first element in %s", op_type_str); goto fail; } field = bson_iter_utf8(&array, NULL); if (!bson_iter_next(&array)) { ERR_WITH_BSON(orig, "expected 2 elements in operand %s, got 1", op_type_str); goto fail; } value = array; if (bson_iter_next(&array)) { ERR_WITH_BSON(orig, "expected 2 elements in operand %s, got > 2", op_type_str); goto fail; } *out = (operator_value_t){ .field = field, .op_type_str = op_type_str, .op_type = get_operator_type(op_type_str), .value = value, }; ok = true; fail: return ok; } // Parses a document like {age: {$gt: 5}}. static bool parse_match_expression(const bson_t *orig, bson_iter_t *in, operator_value_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(orig); BSON_ASSERT_PARAM(in); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bson_iter_t document = {0}, value; const char *op_type_str; bool ok = false; const char *field = bson_iter_key(in); if (!BSON_ITER_HOLDS_DOCUMENT(in)) { ERR_WITH_BSON(orig, "%s", "expected argument to be document"); goto fail; } if (!bson_iter_recurse(in, &document)) { ERR_WITH_BSON(orig, "%s", "failed to recurse into document"); goto fail; } // Expect exactly 1 elements, like {$gt: 5}. if (!bson_iter_next(&document)) { ERR_WITH_BSON(orig, "expected 1 elements in operand %s, got 0", field); goto fail; } op_type_str = bson_iter_key(&document); if (!is_supported_operator(op_type_str)) { ERR_WITH_BSON(orig, "unsupported operator: %s", op_type_str); goto fail; } value = document; if (bson_iter_next(&document)) { ERR_WITH_BSON(orig, "expected 1 elements in operand %s, got > 1", field); goto fail; } *out = (operator_value_t){ .field = field, .op_type_str = op_type_str, .op_type = get_operator_type(op_type_str), .value = value, }; ok = true; fail: return ok; } bool mc_FLE2RangeFindDriverSpec_parse(mc_FLE2RangeFindDriverSpec_t *spec, const bson_t *in, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(in); BSON_ASSERT(status || true); *spec = (mc_FLE2RangeFindDriverSpec_t){0}; // `in` may be an Aggregate Expression with this form: // {$and: [{$gt: ["$age", 5]}, {$lt:["$age", 50]}]} // Or `in` may be a Match Expression with this form: // {$and: [{age: {$gt: 5}}, {age: {$lt: 50}} ]} bson_iter_t and = {0}, array = {0}; bool ok = false; if (!parse_and(in, &and, status)) { goto fail; } // Iterate over array elements. if (!bson_iter_recurse(&and, &array)) { ERR_WITH_BSON(in, "%s", "failed to recurse into '$and'"); goto fail; } enum { UNKNOWN, MATCH_EXPRESSION, AGGREGATE_EXPRESSION } arg_type = UNKNOWN; while (bson_iter_next(&array)) { bson_iter_t doc; if (!BSON_ITER_HOLDS_DOCUMENT(&array)) { ERR_WITH_BSON(in, "%s", "expected document in '$and' array"); goto fail; } if (!bson_iter_recurse(&array, &doc)) { ERR_WITH_BSON(in, "%s", "failed to recurse into '$and' element"); goto fail; } if (!bson_iter_next(&doc)) { ERR_WITH_BSON(in, "%s", "unexpected empty '$and' array document"); goto fail; } if (arg_type == UNKNOWN) { // Attempt to determine argument type by inspecting first key. if (is_supported_operator(bson_iter_key(&doc))) { // Assume the document is part of an Aggregate Expression, like: // {$gt: ["$age", 5]} arg_type = AGGREGATE_EXPRESSION; spec->isAggregateExpression = true; } else { // Assume the document is part of a Match Expression, like: // {age: {$gt: 5}} arg_type = MATCH_EXPRESSION; } } operator_value_t op = {0}; switch (arg_type) { case AGGREGATE_EXPRESSION: if (!parse_aggregate_expression(in, &doc, &op, status)) { goto fail; } break; case MATCH_EXPRESSION: if (!parse_match_expression(in, &doc, &op, status)) { goto fail; } break; case UNKNOWN: default: ERR_WITH_BSON(in, "%s", "unexpected unknown expression type"); goto fail; } switch (op.op_type) { case FLE2RangeOperator_kGt: case FLE2RangeOperator_kGte: if (spec->lower.set) { ERR_WITH_BSON(in, "unexpected duplicate bound %s", op.op_type_str); goto fail; } spec->lower.set = true; spec->lower.value = op.value; spec->lower.included = op.op_type == FLE2RangeOperator_kGte; break; case FLE2RangeOperator_kLt: case FLE2RangeOperator_kLte: if (spec->upper.set) { ERR_WITH_BSON(in, "unexpected duplicate bound %s", op.op_type_str); goto fail; } spec->upper.set = true; spec->upper.value = op.value; spec->upper.included = op.op_type == FLE2RangeOperator_kLte; break; case FLE2RangeOperator_kNone: default: ERR_WITH_BSON(in, "unsupported operator type %s", op.op_type_str); goto fail; } if (spec->field && 0 != strcmp(spec->field, op.field)) { ERR_WITH_BSON(in, "unexpected field mismatch. Expected all fields to be " "%s, but got %s", spec->field, op.field); goto fail; } spec->nOps++; switch (spec->nOps) { case 1: spec->firstOp = op.op_type; break; case 2: spec->secondOp = op.op_type; break; default: ERR_WITH_BSON(in, "expected 1 or 2 operators, got > 2: %s", op.op_type_str); goto fail; } spec->field = op.field; } ok = true; fail: return ok; } // mc_makeRangeFindPlaceholder creates a placeholder to be consumed by // libmongocrypt for encryption. bool mc_makeRangeFindPlaceholder(mc_makeRangeFindPlaceholder_args_t *args, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(args); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bool ok = false; bson_t *edgesInfo = bson_new(); bson_t *v = bson_new(); bson_t *p = bson_new(); _mongocrypt_buffer_init(out); #define TRY(stmt) \ if (!(stmt)) { \ CLIENT_ERR("error appending BSON for placeholder"); \ goto fail; \ } else \ ((void)0) // create edgesInfo. if (!args->isStub) { TRY(bson_append_iter(edgesInfo, "lowerBound", -1, &args->lowerBound)); TRY(BSON_APPEND_BOOL(edgesInfo, "lbIncluded", args->lbIncluded)); TRY(bson_append_iter(edgesInfo, "upperBound", -1, &args->upperBound)); TRY(BSON_APPEND_BOOL(edgesInfo, "ubIncluded", args->ubIncluded)); TRY(bson_append_iter(edgesInfo, "indexMin", -1, &args->indexMin)); TRY(bson_append_iter(edgesInfo, "indexMax", -1, &args->indexMax)); if (args->precision.set) { TRY(BSON_APPEND_INT32(edgesInfo, "precision", args->precision.value)); } if (args->trimFactor.set) { TRY(BSON_APPEND_INT32(edgesInfo, "trimFactor", args->trimFactor.value)); } TRY(BSON_APPEND_DOCUMENT(v, "edgesInfo", edgesInfo)); } // create v. TRY(BSON_APPEND_INT32(v, "payloadId", args->payloadId)); TRY(BSON_APPEND_INT32(v, "firstOperator", (int32_t)args->firstOp)); if (args->secondOp) { TRY(BSON_APPEND_INT32(v, "secondOperator", (int32_t)args->secondOp)); } // create placeholder. TRY(BSON_APPEND_INT32(p, "t", MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND)); TRY(BSON_APPEND_INT32(p, "a", MONGOCRYPT_FLE2_ALGORITHM_RANGE)); TRY(_mongocrypt_buffer_append(args->index_key_id, p, "ki", 2)); TRY(_mongocrypt_buffer_append(args->user_key_id, p, "ku", 2)); TRY(BSON_APPEND_DOCUMENT(p, "v", v)); TRY(BSON_APPEND_INT64(p, "cm", args->maxContentionFactor)); TRY(BSON_APPEND_INT64(p, "s", args->sparsity)); #undef TRY BSON_ASSERT(p->len < UINT32_MAX); _mongocrypt_buffer_resize(out, p->len + 1u); out->subtype = BSON_SUBTYPE_ENCRYPTED; out->data[0] = MC_SUBTYPE_FLE2EncryptionPlaceholder; memcpy(out->data + 1, bson_get_data(p), p->len); ok = true; fail: bson_destroy(p); bson_destroy(v); bson_destroy(edgesInfo); return ok; } bool mc_FLE2RangeFindDriverSpec_to_placeholders(mc_FLE2RangeFindDriverSpec_t *spec, const mc_RangeOpts_t *range_opts, int64_t maxContentionFactor, const _mongocrypt_buffer_t *user_key_id, const _mongocrypt_buffer_t *index_key_id, int32_t payloadId, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(spec); BSON_ASSERT_PARAM(range_opts); BSON_ASSERT_PARAM(user_key_id); BSON_ASSERT_PARAM(index_key_id); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); _mongocrypt_buffer_t p1 = {0}, p2 = {0}; bson_t infDoc = BSON_INITIALIZER; bson_iter_t negInf, posInf; bson_t minMaxDoc = BSON_INITIALIZER; bson_iter_t indexMin, indexMax; bool ok = false; BCON_APPEND(&infDoc, "p", BCON_DOUBLE(INFINITY), "n", BCON_DOUBLE(-INFINITY)); #define TRY(stmt) \ if (!(stmt)) { \ CLIENT_ERR("error transforming BSON for FLE2RangeFindDriverSpec: %s", #stmt); \ goto fail; \ } else \ ((void)0) TRY(bson_iter_init_find(&posInf, &infDoc, "p")); TRY(bson_iter_init_find(&negInf, &infDoc, "n")); // Apply default index min/max values. { bson_type_t index_type; if (spec->lower.set) { index_type = bson_iter_type(&spec->lower.value); } else if (spec->upper.set) { index_type = bson_iter_type(&spec->upper.value); } else { CLIENT_ERR("expected lower or upper bound to be set"); goto fail; } if (!mc_RangeOpts_appendMin(range_opts, index_type, "indexMin", &minMaxDoc, status)) { goto fail; } if (!mc_RangeOpts_appendMax(range_opts, index_type, "indexMax", &minMaxDoc, status)) { goto fail; } TRY(bson_iter_init_find(&indexMin, &minMaxDoc, "indexMin")); TRY(bson_iter_init_find(&indexMax, &minMaxDoc, "indexMax")); } mc_makeRangeFindPlaceholder_args_t args = {.isStub = false, .user_key_id = user_key_id, .index_key_id = index_key_id, .lowerBound = spec->lower.set ? spec->lower.value : negInf, .lbIncluded = spec->lower.set ? spec->lower.included : true, .upperBound = spec->upper.set ? spec->upper.value : posInf, .ubIncluded = spec->upper.set ? spec->upper.included : true, .payloadId = payloadId, .firstOp = spec->firstOp, .secondOp = spec->secondOp, .indexMin = indexMin, .indexMax = indexMax, .precision = range_opts->precision, .maxContentionFactor = maxContentionFactor, .sparsity = range_opts->sparsity, .trimFactor = range_opts->trimFactor}; // First operator is the non-stub. if (!mc_makeRangeFindPlaceholder(&args, &p1, status)) { goto fail; } // Second operator (if required) is a stub. if (spec->nOps == 2) { mc_makeRangeFindPlaceholder_args_t args = {.isStub = true, .user_key_id = user_key_id, .index_key_id = index_key_id, .payloadId = payloadId, .firstOp = spec->firstOp, .secondOp = spec->secondOp, .maxContentionFactor = maxContentionFactor, .sparsity = range_opts->sparsity}; // First operator is the non-stub. if (!mc_makeRangeFindPlaceholder(&args, &p2, status)) { goto fail; } } if (spec->isAggregateExpression) { /* Create an Aggregate Expression document like: { "$and" : [ {"$gt" : [ "$age", "" ]}, {"$lt" : [ "$age", "" ]} ] } */ bson_t and; TRY(BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, "$and", &and)); bson_t elem; TRY(BSON_APPEND_DOCUMENT_BEGIN(&and, "0", &elem)); bson_t operator; TRY(BSON_APPEND_ARRAY_UNSAFE_BEGIN(&elem, mc_FLE2RangeOperator_to_string(spec->firstOp), &operator)); TRY(BSON_APPEND_UTF8(&operator, "0", spec->field)); TRY(_mongocrypt_buffer_append(&p1, &operator, "1", 1)); TRY(bson_append_array_end(&elem, &operator)); TRY(bson_append_document_end(&and, &elem)); if (spec->nOps == 2) { TRY(BSON_APPEND_DOCUMENT_BEGIN(&and, "1", &elem)); TRY(BSON_APPEND_ARRAY_UNSAFE_BEGIN(&elem, mc_FLE2RangeOperator_to_string(spec->secondOp), &operator)); TRY(BSON_APPEND_UTF8(&operator, "0", spec->field)); TRY(_mongocrypt_buffer_append(&p2, &operator, "1", 1)); TRY(bson_append_array_end(&elem, &operator)); TRY(bson_append_document_end(&and, &elem)); } TRY(bson_append_array_end(out, &and)); } else { /* Create a Match Expression document like: { "$and" : [ {"age" : { "$gt": "" }}, {"age" : { "$lt": "" }} ] } */ bson_t and; TRY(BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, "$and", &and)); bson_t elem; TRY(BSON_APPEND_DOCUMENT_BEGIN(&and, "0", &elem)); bson_t operator; TRY(BSON_APPEND_DOCUMENT_BEGIN(&elem, spec->field, &operator)); const char *op_str = mc_FLE2RangeOperator_to_string(spec->firstOp); TRY(_mongocrypt_buffer_append(&p1, &operator, op_str, -1)); TRY(bson_append_document_end(&elem, &operator)); TRY(bson_append_document_end(&and, &elem)); if (spec->nOps == 2) { TRY(BSON_APPEND_DOCUMENT_BEGIN(&and, "1", &elem)); TRY(BSON_APPEND_DOCUMENT_BEGIN(&elem, spec->field, &operator)); const char *op_str = mc_FLE2RangeOperator_to_string(spec->secondOp); TRY(_mongocrypt_buffer_append(&p2, &operator, op_str, -1)); TRY(bson_append_document_end(&elem, &operator)); TRY(bson_append_document_end(&and, &elem)); } TRY(bson_append_array_end(out, &and)); } #undef TRY ok = true; fail: _mongocrypt_buffer_cleanup(&p2); _mongocrypt_buffer_cleanup(&p1); bson_destroy(&minMaxDoc); bson_destroy(&infDoc); return ok; } static mlib_once_flag payloadId_init_flag = MLIB_ONCE_INITIALIZER; static mongocrypt_mutex_t payloadId_mutex; static int32_t payloadId = 0; static void payloadId_init_mutex(void) { _mongocrypt_mutex_init(&payloadId_mutex); } void mc_reset_payloadId_for_testing(void); // -Wmissing-prototypes: for testing only. void mc_reset_payloadId_for_testing(void) { mlib_call_once(&payloadId_init_flag, payloadId_init_mutex); MONGOCRYPT_WITH_MUTEX(payloadId_mutex) { payloadId = 0; } } // mc_getNextPayloadId is thread safe. int32_t mc_getNextPayloadId(void) { mlib_call_once(&payloadId_init_flag, payloadId_init_mutex); int32_t ret; MONGOCRYPT_WITH_MUTEX(payloadId_mutex) { ret = payloadId; payloadId = payloadId < INT32_MAX ? payloadId + 1 : 0; } return ret; } libmongocrypt-1.19.0/src/mc-fle2-tag-and-encrypted-metadata-block-private.h000066400000000000000000000042041521103432300264730ustar00rootroot00000000000000/* * Copyright 2024-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. */ #ifndef MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H #define MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H #include "mongocrypt-buffer-private.h" #define kFieldLen 32U #define kMetadataLen 96U // encryptedCount(32) + tag(32) + encryptedZeros(32) typedef struct _mc_FLE2TagAndEncryptedMetadataBlock_t { // The contiguous metadata block buffer. _mongocrypt_buffer_t rawBlock; // The following are unowned buffers that each points to a field in rawBlock. _mongocrypt_buffer_t encryptedCount; _mongocrypt_buffer_t tag; _mongocrypt_buffer_t encryptedZeros; } mc_FLE2TagAndEncryptedMetadataBlock_t; void mc_FLE2TagAndEncryptedMetadataBlock_init(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata); void mc_FLE2TagAndEncryptedMetadataBlock_cleanup(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata); bool mc_FLE2TagAndEncryptedMetadataBlock_parse(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_FLE2TagAndEncryptedMetadataBlock_validate(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, mongocrypt_status_t *status); #endif /* MC_FLE2_TAG_AND_ENCRYPTED_METADATA_BLOCK_H */ libmongocrypt-1.19.0/src/mc-fle2-tag-and-encrypted-metadata-block.c000066400000000000000000000113421521103432300250170ustar00rootroot00000000000000/* * Copyright 2024-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 "mc-fle2-tag-and-encrypted-metadata-block-private.h" #include "mc-reader-private.h" #include "mc-writer-private.h" #include "mongocrypt-private.h" #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) void mc_FLE2TagAndEncryptedMetadataBlock_init(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata) { BSON_ASSERT_PARAM(metadata); memset(metadata, 0, sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t)); } void mc_FLE2TagAndEncryptedMetadataBlock_cleanup(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata) { BSON_ASSERT_PARAM(metadata); _mongocrypt_buffer_cleanup(&metadata->rawBlock); _mongocrypt_buffer_cleanup(&metadata->encryptedCount); _mongocrypt_buffer_cleanup(&metadata->tag); _mongocrypt_buffer_cleanup(&metadata->encryptedZeros); } bool mc_FLE2TagAndEncryptedMetadataBlock_parse(mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(metadata); BSON_ASSERT_PARAM(buf); if ((buf->data == NULL) || (buf->len == 0)) { CLIENT_ERR("Empty buffer passed to mc_FLE2IndexedEncryptedValueV2_parse"); return false; } mc_reader_t reader; mc_reader_init_from_buffer(&reader, buf, __func__); mc_FLE2TagAndEncryptedMetadataBlock_init(metadata); CHECK_AND_RETURN(mc_reader_read_buffer(&reader, &metadata->rawBlock, kMetadataLen, status)); uint64_t offset = 0; _mongocrypt_buffer_from_data(&metadata->encryptedCount, metadata->rawBlock.data + offset, kFieldLen); offset += kFieldLen; _mongocrypt_buffer_from_data(&metadata->tag, metadata->rawBlock.data + offset, kFieldLen); offset += kFieldLen; _mongocrypt_buffer_from_data(&metadata->encryptedZeros, metadata->rawBlock.data + offset, kFieldLen); return true; } bool mc_FLE2TagAndEncryptedMetadataBlock_serialize(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(metadata); BSON_ASSERT_PARAM(buf); _mongocrypt_buffer_resize(buf, kMetadataLen); mc_writer_t writer; mc_writer_init_from_buffer(&writer, buf, __func__); CHECK_AND_RETURN(mc_writer_write_buffer(&writer, &metadata->rawBlock, kMetadataLen, status)); return true; } #define CHECK(condition, msg) \ do { \ if (!(condition)) { \ CLIENT_ERR("mc_FLE2TagAndEncryptedMetadataBlock_validate failed: " msg); \ return false; \ } \ } while (0) bool mc_FLE2TagAndEncryptedMetadataBlock_validate(const mc_FLE2TagAndEncryptedMetadataBlock_t *metadata, mongocrypt_status_t *status) { CHECK(metadata->rawBlock.len == kMetadataLen, "Length of metadata block was unexpected"); CHECK(metadata->encryptedCount.len == kFieldLen, "Length of encrypted count was unexpected"); CHECK(metadata->tag.len == kFieldLen, "Length of tag was unexpected"); CHECK(metadata->encryptedZeros.len == kFieldLen, "Length of encrypted zeros was unexpected"); return true; } libmongocrypt-1.19.0/src/mc-mlib/000077500000000000000000000000001521103432300165735ustar00rootroot00000000000000libmongocrypt-1.19.0/src/mc-mlib/check.hpp000066400000000000000000000044661521103432300203730ustar00rootroot00000000000000#ifndef MLIB_CHECK_HPP_INCLUDED #define MLIB_CHECK_HPP_INCLUDED #include #include #include namespace mlib { namespace detail { struct check_info { const char *filename; int line; const char *expr; }; struct nil {}; template struct bound_lhs { check_info info; Left value; #define DEFOP(Oper) \ template nil operator Oper(Rhs rhs) const noexcept { \ if (value Oper rhs) { \ return {}; \ } \ std::fprintf(stderr, "%s:%d: CHECK( %s ) failed!\n", info.filename, info.line, info.expr); \ std::cerr << "Expanded expression: " << value << " " #Oper " " << rhs << '\n'; \ std::exit(2); \ } DEFOP(==) DEFOP(!=) DEFOP(<) DEFOP(<=) DEFOP(>) DEFOP(>=) #undef DEFOP }; struct check_magic { check_info info; template bound_lhs operator->*(Oper op) { return bound_lhs{info, op}; } }; struct check_consume { void operator=(nil) { } void operator=(const bound_lhs &l) { // Invoke the test for truthiness: (void)(l == true); } }; /** * @brief Create an assertion that prints the expanded expression upon failure. * * Only supports simple comparison binary expressions, and plain boolean * expressions */ #define MLIB_CHECK(Cond) \ ::mlib::detail::check_consume{} = \ ::mlib::detail::check_magic{::mlib::detail::check_info{__FILE__, __LINE__, #Cond}}->*Cond } // namespace detail } // namespace mlib #endif // MLIB_CHECK_HPP_INCLUDED libmongocrypt-1.19.0/src/mc-mlib/endian.h000066400000000000000000000024721521103432300202070ustar00rootroot00000000000000#ifndef MLIB_ENDIAN_H_INCLUDED #define MLIB_ENDIAN_H_INCLUDED #ifdef __has_include // Some platforms require including other headers that will define the necessary // macros #if __has_include() // This may recursively include our own file in a pathological case, but that // won't cause an issue. The default will include the system's version instead: #include #endif #if __has_include() #include #endif #endif #include "./macros.h" enum mlib_endian_kind { #ifdef _MSC_VER // MSVC only targets little-endian arches at the moment MLIB_ENDIAN_LITTLE = 1234, MLIB_ENDIAN_BIG = 4321, MLIB_ENDIAN_NATIVE = MLIB_ENDIAN_LITTLE, #elif defined(__BYTE_ORDER__) // Commonly built-in defined in newer compilers MLIB_ENDIAN_LITTLE = __ORDER_LITTLE_ENDIAN__, MLIB_ENDIAN_BIG = __ORDER_BIG_ENDIAN__, MLIB_ENDIAN_NATIVE = __BYTE_ORDER__, #elif defined(__BYTE_ORDER) // Common in or MLIB_ENDIAN_LITTLE = __LITTLE_ENDIAN, MLIB_ENDIAN_BIG = __BIG_ENDIAN, MLIB_ENDIAN_NATIVE = __BYTE_ORDER, #else #error This compiler does not define an endianness macro. #endif }; enum { MLIB_IS_LITTLE_ENDIAN = MLIB_ENDIAN_NATIVE == MLIB_ENDIAN_LITTLE, MLIB_IS_BIG_ENDIAN = MLIB_ENDIAN_NATIVE == MLIB_ENDIAN_BIG, }; #endif // MLIB_ENDIAN_H_INCLUDED libmongocrypt-1.19.0/src/mc-mlib/error.h000066400000000000000000000030711521103432300200760ustar00rootroot00000000000000#ifndef MLIB_ERROR_PRIVATE_H #define MLIB_ERROR_PRIVATE_H #include "./user-check.h" #include "./str.h" #ifdef _WIN32 #include "./windows-lean.h" #else #include #endif /** * @brief Obtain a string containing an error message corresponding to an error * code from the host platform. * * @param errn An error code for the system. (e.g. GetLastError() on Windows) * @return mstr A new string containing the resulting error. Must be freed with * @ref mstr_free(). */ static inline mstr merror_system_error_string(int errn) { #ifdef _WIN32 wchar_t *wbuffer = NULL; DWORD slen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)errn, 0, (LPWSTR)&wbuffer, 0, NULL); if (slen == 0) { return mstr_copy_cstr("[Error while getting error string from FormatMessageW()]"); } mstr_narrow_result narrow = mstr_win32_narrow(wbuffer); LocalFree(wbuffer); assert(narrow.error == 0); // Messages from FormatMessage contain an additional CR+LF if (mstr_ends_with(narrow.string.view, mstrv_lit("\r\n"))) { mstr_inplace_remove_suffix(&narrow.string, 2); } return narrow.string; #else errno = 0; char *const str = strerror(errn); if (errno) { return mstr_copy_cstr("[Error while getting error string from strerror()]"); } return mstr_copy_cstr(str); #endif } #endif // MLIB_ERROR_PRIVATE_H libmongocrypt-1.19.0/src/mc-mlib/int128.h000066400000000000000000000506211521103432300177750ustar00rootroot00000000000000#ifndef MLIB_INT128_H_INCLUDED #define MLIB_INT128_H_INCLUDED #include "./macros.h" #include "./str.h" #include #include #include #include // Deliberately using old-style casts for C and C++ compatibility. #if defined(__cplusplus) && defined(__clang__) _Pragma("clang diagnostic push"); _Pragma("clang diagnostic ignored \"-Wold-style-cast\""); #endif MLIB_C_LINKAGE_BEGIN /** * @brief A 128-bit binary integer */ typedef union { struct { uint64_t lo; uint64_t hi; } r; #if defined(__SIZEOF_INT128__) // These union members are only for the purpose of debugging visualization // and testing, and will only appear correctly on little-endian platforms. __int128_t signed_; __uint128_t unsigned_; #endif } mlib_int128; /// Define an int128 from a literal within [INT64_MIN, INT64_MAX] #define MLIB_INT128(N) MLIB_INIT(mlib_int128) MLIB_INT128_C(N) /// Define an int128 from a literal within [INT64_MIN, INT64_MAX] (usable as a /// constant init) #define MLIB_INT128_C(N) MLIB_INT128_FROM_PARTS((uint64_t)INT64_C(N), (INT64_C(N) < 0 ? UINT64_MAX : 0)) /** * @brief Cast an integral value to an mlib_int128 * * If the argument is signed and less-than zero, it will be sign-extended */ #define MLIB_INT128_CAST(N) \ MLIB_INIT(mlib_int128) \ MLIB_INT128_FROM_PARTS((uint64_t)(N), ((N) < 0 ? UINT64_MAX : 0)) /** * @brief Create an mlib_int128 from the low and high parts of the integer * * @param LowWord_u64 The low-value 64 bits of the number * @param HighWord_u64 The high-value 64 bits of the number */ #define MLIB_INT128_FROM_PARTS(LowWord_u64, HighWord_u64) \ { {LowWord_u64, HighWord_u64}, } /// Maximum value of int128 when treated as a signed integer #define MLIB_INT128_SMAX MLIB_INT128_FROM_PARTS(UINT64_MAX, UINT64_MAX & ~(UINT64_C(1) << 63)) /// Minimum value of int128, when treated as a signed integer #define MLIB_INT128_SMIN MLIB_INT128_FROM_PARTS(0, UINT64_C(1) << 63) /// Maximum value of int128, when treated as an unsigned integer #define MLIB_INT128_UMAX MLIB_INT128_FROM_PARTS(UINT64_MAX, UINT64_MAX) /** * @brief Compare two 128-bit integers as unsigned integers * * @return (R < 0) if (left < right) * @return (R > 0) if (left > right) * @return (R = 0) if (left == right) */ static mlib_constexpr_fn int mlib_int128_ucmp(mlib_int128 left, mlib_int128 right) { if (left.r.hi > right.r.hi) { return 1; } else if (left.r.hi < right.r.hi) { return -1; } else if (left.r.lo > right.r.lo) { return 1; } else if (left.r.lo < right.r.lo) { return -1; } else { return 0; } } /** * @brief Compare two 128-bit integers as signed integers * * @return (R < 0) if (left < right) * @return (R > 0) if (left > right) * @return (R = 0) if (left == right) */ static mlib_constexpr_fn int mlib_int128_scmp(mlib_int128 left, mlib_int128 right) { if ((left.r.hi & (1ull << 63)) == (right.r.hi & (1ull << 63))) { // Same signed-ness, so they are as comparable as unsigned return mlib_int128_ucmp(left, right); } else if (left.r.hi & (1ull << 63)) { // The left is negative return -1; } else { // The right is negative return 1; } } /** * @brief Determine whether the two 128-bit integers are equal * * @retval true If left == right * @retval false Otherwise */ static mlib_constexpr_fn bool mlib_int128_eq(mlib_int128 left, mlib_int128 right) { return mlib_int128_ucmp(left, right) == 0; } /** * @brief Add two 128-bit integers together * * @return mlib_int128 The sum of the two addends. Overflow will wrap. */ static mlib_constexpr_fn mlib_int128 mlib_int128_add(mlib_int128 left, mlib_int128 right) { uint64_t losum = left.r.lo + right.r.lo; // Overflow check unsigned carry = (losum < left.r.lo || losum < right.r.lo); uint64_t hisum = left.r.hi + right.r.hi + carry; return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(losum, hisum); } /** * @brief Treat the given 128-bit integer as signed, and return its * negated value */ static mlib_constexpr_fn mlib_int128 mlib_int128_negate(mlib_int128 v) { mlib_int128 r = MLIB_INT128_FROM_PARTS(~v.r.lo, ~v.r.hi); r = mlib_int128_add(r, MLIB_INT128(1)); return r; } /** * @brief Subtract two 128-bit integers * * @return mlib_int128 The difference between `from` and `less` */ static mlib_constexpr_fn mlib_int128 mlib_int128_sub(mlib_int128 from, mlib_int128 less) { unsigned borrow = from.r.lo < less.r.lo; uint64_t low = from.r.lo - less.r.lo; uint64_t high = from.r.hi - less.r.hi; high -= borrow; return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(low, high); } /** * @brief Bitwise left-shift a 128-bit integer * * @param val The value to modify * @param off The offset to shift left. If negative, shifts right * @return The result of the shift operation */ static mlib_constexpr_fn mlib_int128 mlib_int128_lshift(mlib_int128 val, int off) { if (off > 0) { if (off >= 64) { off -= 64; uint64_t high = val.r.lo << off; return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(0, high); } else { uint64_t low = val.r.lo << off; uint64_t high = val.r.hi << off; high |= val.r.lo >> (64 - off); return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(low, high); } } else if (off < 0) { off = -off; if (off >= 64) { off -= 64; uint64_t low = val.r.hi >> off; return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(low, 0); } else { uint64_t high = val.r.hi >> off; uint64_t low = val.r.lo >> off; low |= val.r.hi << (64 - off); return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(low, high); } } else { return val; } } /** * @brief Bitwise logical right-shift a 128-bit integer * * @param val The value to modify. No "sign bit" is respected. * @param off The offset to shift right. If negative, shifts left * @return The result of the shift operation */ static mlib_constexpr_fn mlib_int128 mlib_int128_rshift(mlib_int128 val, int off) { return mlib_int128_lshift(val, -off); } /** * @brief Bitwise-or two 128-bit integers */ static mlib_constexpr_fn mlib_int128 mlib_int128_bitor(mlib_int128 l, mlib_int128 r) { return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo | r.r.lo, l.r.hi | r.r.hi); } /** * @brief Bitwise-and two 128-bit integers */ static mlib_constexpr_fn mlib_int128 mlib_int128_bitand(mlib_int128 l, mlib_int128 r) { return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(l.r.lo & r.r.lo, l.r.hi & r.r.hi); } // Multiply two 64bit integers to get a 128-bit result without overflow static mlib_constexpr_fn mlib_int128 _mlibUnsignedMult128(uint64_t left, uint64_t right) { // Perform a Knuth 4.3.1M multiplication uint32_t u[2] = {(uint32_t)left, (uint32_t)(left >> 32)}; uint32_t v[2] = {(uint32_t)right, (uint32_t)(right >> 32)}; uint32_t w[4] = {0}; for (int j = 0; j < 2; ++j) { uint64_t t = 0; for (int i = 0; i < 2; ++i) { t += (uint64_t)(u[i]) * v[j] + w[i + j]; w[i + j] = (uint32_t)t; t >>= 32; } w[j + 2] = (uint32_t)t; } return MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(((uint64_t)w[1] << 32) | w[0], ((uint64_t)w[3] << 32) | w[2]); } /** * @brief Multiply two mlib_int128s together. Overflow will wrap. */ static mlib_constexpr_fn mlib_int128 mlib_int128_mul(mlib_int128 l, mlib_int128 r) { // Multiply the low-order word mlib_int128 ret = _mlibUnsignedMult128(l.r.lo, r.r.lo); // Accumulate the high-order parts: ret.r.hi += l.r.lo * r.r.hi; ret.r.hi += l.r.hi * r.r.lo; return ret; } /// Get the number of leading zeros in a 64bit number. static mlib_constexpr_fn int _mlibCountLeadingZeros_u64(uint64_t bits) { int n = 0; if (bits == 0) { return 64; } while (!(1ull << 63 & bits)) { ++n; bits <<= 1; } return n; } static mlib_constexpr_fn int _mlibCountLeadingZeros_u128(mlib_int128 r) { int clz_l = _mlibCountLeadingZeros_u64(r.r.hi); if (clz_l != 64) { return clz_l; } int clz_r = _mlibCountLeadingZeros_u64(r.r.lo); return clz_l + clz_r; } /// Implementation of Knuth's algorithm 4.3.1 D for unsigned integer division static mlib_constexpr_fn void _mlibKnuth431D(uint32_t *const u, const int ulen, const uint32_t *const v, const int vlen, uint32_t *quotient) { // Part D1 (normalization) is done by caller, // normalized in u and v (radix b is 2^32) typedef uint64_t u64; typedef int64_t i64; typedef uint32_t u32; const int m = ulen - vlen - 1; const int n = vlen; // 'd' is 2^32. Shifting left and right is equivalent to mult and division by // d, respectively. // D2 int j = m; for (;;) { // D3: Select two u32 as a u64: u64 two = ((u64)(u[j + n]) << 32) | u[j + n - 1]; // D3: Partial quotient: qÌ‚ u64 q = two / v[n - 1]; // D3: Partial remainder: rÌ‚ u64 r = two % v[n - 1]; // D3: Compute qÌ‚ and rÌ‚ while (q >> 32 || q * (u64)v[n - 2] > (r << 32 | u[j + n - 2])) { q--; r += v[n - 1]; if (r >> 32) { break; } } // D4: Multiply and subtract i64 k = 0; i64 t = 0; for (int i = 0; i < n; ++i) { u64 prod = (u32)q * (u64)(v[i]); t = u[i + j] - k - (u32)prod; u[i + j] = (u32)t; k = (i64)(prod >> 32) - (t >> 32); } t = u[j + n] - k; u[j + n] = (u32)t; quotient[j] = (u32)q; // D5: Test remainder if (t < 0) { // D6: Add back --quotient[j]; k = 0; for (int i = 0; i < n; ++i) { t = u[i + j] + k + v[i]; u[i + j] = (u32)(t); k = t >> 32; } u[j + n] += (u32)k; } // D7: --j; if (j < 0) { break; } } // Denormalization (D8) is done by caller. } /// The result of 128-bit division typedef struct mlib_int128_divmod_result { /// The quotient of the division operation (rounds to zero) mlib_int128 quotient; /// The remainder of the division operation mlib_int128 remainder; } mlib_int128_divmod_result; /// Divide a 128-bit number by a 64bit number. static mlib_constexpr_fn struct mlib_int128_divmod_result _mlibDivide_u128_by_u64(const mlib_int128 numer, const uint64_t denom) { mlib_int128 adjusted = numer; adjusted.r.hi %= denom; int d = _mlibCountLeadingZeros_u64(denom); typedef uint32_t u32; typedef uint64_t u64; if (d >= 32) { // jk: We're dividing by less than UINT32_MAX: We can do a simple short // division of two base32 numbers. // Treat the denominator as a single base32 digit: const u32 d0 = (u32)denom; // And the numerator as four base32 digits: const u64 n0 = (u32)(numer.r.lo); const u64 n1 = (u32)(numer.r.lo >> 32); // We don't need to split n2 and n3. (n3,n2) will be the first partial // dividend const u64 n3_n2 = numer.r.hi; // First partial remainder: (n3,n2) % d0 const u64 r1 = n3_n2 % d0; // Second partial dividend: (r1,n1) const u64 r1_n1 = (r1 << 32) + n1; // Second partial remainder: (r1,n1) % d0 const u64 r0 = r1_n1 % d0; // Final partial dividend: (r0,n0) const u64 r0_n0 = (r0 << 32) + n0; // Final remainder: (r0,n0) % d0 const u64 rem = r0_n0 % d0; // Form the quotient as four base32 digits: // Least quotient digit: (r0,n0) / d0 const u64 q0 = r0_n0 / d0; // Second quotient digit: (r1,n1) / d0 const u64 q1 = r1_n1 / d0; // Third and fourth quotient digit: (n3,n2) / d0 const u64 q3_q2 = n3_n2 / d0; // Low word of the quotient: (q1,q0) const u64 q1_q0 = (q1 << 32) + q0; return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(q1_q0, q3_q2), MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(rem, 0), }; } // Normalize for a Knuth 4.3.1D division. Convert the integers into two // base-32 numbers, with u and v being arrays of digits: u32 u[5] = { (u32)(adjusted.r.lo << d), (u32)(adjusted.r.lo >> (32 - d)), (u32)(adjusted.r.hi << d), (u32)(adjusted.r.hi >> (32 - d)), 0, }; if (d != 0) { // Extra bits from overlap: u[2] |= (u32)(adjusted.r.lo >> (64 - d)); u[4] |= (u32)(adjusted.r.hi >> (64 - d)); } u32 v[2] = { (u32)(denom << d), (u32)(denom >> (32 - d)), }; u32 qparts[3] = {0}; _mlibKnuth431D(u, 5, v, 2, qparts); u64 rem = ((u64)u[1] << (32 - d)) | (u[0] >> d); u64 quo = ((u64)qparts[1] << 32) | qparts[0]; return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(quo, numer.r.hi / denom), MLIB_INIT(mlib_int128) MLIB_INT128_FROM_PARTS(rem, 0), }; } /** * @brief Perform a combined division+remainder of two 128-bit numbers * * @param numer The dividend * @param denom The divisor * @return A struct with .quotient and .remainder results */ static mlib_constexpr_fn mlib_int128_divmod_result mlib_int128_divmod(mlib_int128 numer, mlib_int128 denom) { const uint64_t nhi = numer.r.hi; const uint64_t nlo = numer.r.lo; const uint64_t dhi = denom.r.hi; const uint64_t dlo = denom.r.lo; if (dhi > nhi) { // Denominator is definitely larger than numerator. Quotient is zero, // remainder is full numerator. return MLIB_INIT(mlib_int128_divmod_result){MLIB_INT128(0), numer}; } else if (dhi == nhi) { // High words are equal if (nhi == 0) { // Both high words are zero, so this is just a division of two 64bit // numbers return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INT128_CAST(nlo / dlo), MLIB_INT128_CAST(nlo % dlo), }; } else if (nlo > dlo) { // The numerator is larger than the denom and the high word on the // denom is non-zero, so this cannot divide to anything greater than 1. return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INT128(1), mlib_int128_sub(numer, denom), }; } else if (nlo < dlo) { // numer.r.lo < denom.r.lo and denom.r.hi > denom.r.lo, so the // integer division becomes zero return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INT128(0), numer, }; } else { // N / N is one return MLIB_INIT(mlib_int128_divmod_result){MLIB_INT128(1), MLIB_INT128(0)}; } } else if (dhi == 0) { // No high in denominator. We can use a u128/u64 return _mlibDivide_u128_by_u64(numer, denom.r.lo); } else { // We'll need to do a full u128/u128 division // Normalize for Knuth 4.3.1D int d = _mlibCountLeadingZeros_u64(denom.r.hi); // Does the denom have only three base32 digits? const bool has_three = d >= 32; d &= 31; uint32_t u[5] = { (uint32_t)(numer.r.lo << d), (uint32_t)(numer.r.lo >> (32 - d)), (uint32_t)(numer.r.hi << d), (uint32_t)(numer.r.hi >> (32 - d)), 0, }; uint32_t v[4] = { (uint32_t)(denom.r.lo << d), (uint32_t)(denom.r.lo >> (32 - d)), (uint32_t)(denom.r.hi << d), (uint32_t)(denom.r.hi >> (32 - d)), }; if (d != 0) { u[2] |= (uint32_t)(numer.r.lo >> (64 - d)); u[4] |= (uint32_t)(numer.r.hi >> (64 - d)); v[2] |= (uint32_t)(denom.r.lo >> (64 - d)); } uint32_t q[2] = {0}; if (has_three) { _mlibKnuth431D(u, 5, v, 3, q); } else { _mlibKnuth431D(u, 5, v, 4, q); } mlib_int128 remainder = MLIB_INT128_FROM_PARTS(((uint64_t)u[1] << 32) | u[0], ((uint64_t)u[3] << 32) | u[2]); remainder = mlib_int128_rshift(remainder, d); return MLIB_INIT(mlib_int128_divmod_result){ MLIB_INT128_CAST(q[0] | (uint64_t)q[1] << 32), remainder, }; } } /** * @brief Perform a division of two 128-bit numbers */ static mlib_constexpr_fn mlib_int128 mlib_int128_div(mlib_int128 numer, mlib_int128 denom) { return mlib_int128_divmod(numer, denom).quotient; } /** * @brief Perform a modulus of two 128-bit numbers */ static mlib_constexpr_fn mlib_int128 mlib_int128_mod(mlib_int128 numer, mlib_int128 denom) { return mlib_int128_divmod(numer, denom).remainder; } /** * @brief Get the nth power of ten as a 128-bit number */ static mlib_constexpr_fn mlib_int128 mlib_int128_pow10(uint8_t nth) { mlib_int128 r = MLIB_INT128(1); while (nth-- > 0) { r = mlib_int128_mul(r, MLIB_INT128(10)); } return r; } /** * @brief Get the Nth power of two as a 128-bit number */ static mlib_constexpr_fn mlib_int128 mlib_int128_pow2(uint8_t nth) { return mlib_int128_lshift(MLIB_INT128(1), (int)nth); } /** * @brief Read a 128-bit unsigned integer from a base-10 string */ static mlib_constexpr_fn mlib_int128 mlib_int128_from_string(const char *s, const char **end) { int radix = 10; if (mlib_strlen(s) > 2 && s[0] == '0') { // Check for a different radix char b = s[1]; if (b == 'b' || b == 'B') { radix = 2; s += 2; } else if (b == 'c' || b == 'C') { radix = 8; s += 2; } else if (b == 'x' || b == 'X') { radix = 16; s += 2; } else { radix = 8; s += 1; } } mlib_int128 ret = MLIB_INT128(0); for (; *s; ++s) { char c = *s; if (c == '\'') { // Digit separator. Skip it; continue; } if (c >= 'a') { // Uppercase (if a letter, otherwise some other punct): c = (char)(c - ('a' - 'A')); } int digit = c - '0'; if (c >= 'A') { // It's actually a letter (or garbage, which we'll catch later) digit = (c - 'A') + 10; } if (digit > radix || digit < 0) { // The digit is outside of our radix, or garbage break; } ret = mlib_int128_mul(ret, MLIB_INT128_CAST(radix)); ret = mlib_int128_add(ret, MLIB_INT128_CAST(digit)); } if (end) { *end = s; } return ret; } /** * @brief Truncate a 128-bit number to a 64-bit number */ static mlib_constexpr_fn uint64_t mlib_int128_to_u64(mlib_int128 v) { return v.r.lo; } /// The result type of formatting a 128-bit number typedef struct { /// The character array of the number as a base10 string. Null-terminated. char str[40]; } mlib_int128_charbuf; /** * @brief Format a 128-bit integer into a string of base10 digits. * * @return mlib_int128_charbuf a struct containing a .str character array */ static mlib_constexpr_fn mlib_int128_charbuf mlib_int128_format(mlib_int128 i) { mlib_int128_charbuf into = {{0}}; char *out = into.str + (sizeof into) - 1; int len = 0; if (mlib_int128_eq(i, MLIB_INT128(0))) { *out-- = '0'; len = 1; } while (!mlib_int128_eq(i, MLIB_INT128(0))) { mlib_int128_divmod_result dm = mlib_int128_divmod(i, MLIB_INT128(10)); uint64_t v = mlib_int128_to_u64(dm.remainder); char digits[] = "0123456789"; char d = digits[v]; *out = d; --out; i = dm.quotient; ++len; } for (int j = 0; j < len; ++j) { into.str[j] = out[j + 1]; } into.str[len] = 0; return into; } MLIB_C_LINKAGE_END // Deliberately using old-style casts for C and C++ compatibility. #if defined(__cplusplus) && defined(__clang__) _Pragma("clang diagnostic pop"); #endif #endif // MLIB_INT128_H_INCLUDED libmongocrypt-1.19.0/src/mc-mlib/int128.test.c000066400000000000000000000001451521103432300207420ustar00rootroot00000000000000#include "./int128.h" // This file checks for C compilability. Other tests are defined in .test.cpp libmongocrypt-1.19.0/src/mc-mlib/int128.test.cpp000066400000000000000000000341421521103432300213060ustar00rootroot00000000000000#include "./int128.h" #include "./endian.h" #include "./check.hpp" #define CHECK MLIB_CHECK #include #include #include #include #include // Deliberately using old-style casts for C and C++ compatibility. #if defined(__clang__) _Pragma("clang diagnostic ignored \"-Wold-style-cast\""); #endif #if (defined(__GNUC__) && __GNUC__ < 7 && !defined(__clang__)) || (defined(_MSC_VER) && _MSC_VER < 1920) // Old GCC and old MSVC have partially-broken constexpr that prevents us from // properly using static_assert with from_string() #define BROKEN_CONSTEXPR #elif (defined(_MSC_VER) && _MSC_VER >= 1930) // Avoid internal compiler error on VS 2022 versions 17.0 and newer when // evaluating mlib_int128_from_string via operator""_i128. Assumed to be related // to: https://developercommunity.visualstudio.com/t/User-defined-literals-cause-ICEs/10259122 #define BROKEN_CONSTEXPR #endif #ifndef BROKEN_CONSTEXPR // Basic checks with static_asserts, check constexpr correctness and fail fast static_assert(mlib_int128_eq(MLIB_INT128(0), MLIB_INT128_FROM_PARTS(0, 0)), "fail"); static_assert(mlib_int128_eq(MLIB_INT128(4), MLIB_INT128_FROM_PARTS(4, 0)), "fail"); static_assert(mlib_int128_eq(MLIB_INT128(34), MLIB_INT128_FROM_PARTS(34, 0)), "fail"); static_assert(mlib_int128_eq(MLIB_INT128(34 + 8), MLIB_INT128_FROM_PARTS(42, 0)), "fail"); static_assert(mlib_int128_eq(MLIB_INT128_CAST(94), MLIB_INT128_FROM_PARTS(94, 0)), "fail"); static_assert(mlib_int128_eq(mlib_int128_lshift(MLIB_INT128_CAST(1), 64), MLIB_INT128_FROM_PARTS(0, 1)), "fail"); static_assert(mlib_int128_eq(mlib_int128_lshift(MLIB_INT128_CAST(1), 127), MLIB_INT128_FROM_PARTS(0, 1ull << 63)), "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_CAST(2), MLIB_INT128(0)) > 0, "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_CAST(-2), MLIB_INT128(0)) < 0, "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_CAST(0), MLIB_INT128(0)) == 0, "fail"); // Unsigned compare doesn't believe in negative numbers: static_assert(mlib_int128_ucmp(MLIB_INT128_CAST(-2), MLIB_INT128(0)) > 0, "fail"); #endif // BROKEN_CONSTEXPR // Literals, for test convenience: #ifndef BROKEN_CONSTEXPR constexpr #endif mlib_int128 operator""_i128(const char *s) { return mlib_int128_from_string(s, NULL); } #ifndef BROKEN_CONSTEXPR constexpr #endif mlib_int128 operator""_i128(const char *s, size_t) { return mlib_int128_from_string(s, NULL); } // Operators, for test convenience constexpr bool operator==(mlib_int128 l, mlib_int128 r) { return mlib_int128_eq(l, r); } constexpr bool operator<(mlib_int128 l, mlib_int128 r) { return mlib_int128_scmp(l, r) < 0; } #ifndef BROKEN_CONSTEXPR static_assert(mlib_int128_eq(MLIB_INT128(0), 0_i128), "fail"); static_assert(mlib_int128_eq(MLIB_INT128(65025), 65025_i128), "fail"); static_assert(mlib_int128_eq(MLIB_INT128_FROM_PARTS(0, 1), 18446744073709551616_i128), "fail"); static_assert(mlib_int128_eq(MLIB_INT128_UMAX, 340282366920938463463374607431768211455_i128), "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_SMIN, MLIB_INT128_SMAX) < 0, "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_SMAX, MLIB_INT128_SMIN) > 0, "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_CAST(-12), MLIB_INT128_CAST(0)) < 0, "fail"); static_assert(mlib_int128_scmp(MLIB_INT128_CAST(12), MLIB_INT128_CAST(0)) > 0, "fail"); // Simple arithmetic: static_assert(mlib_int128_scmp(mlib_int128_add(MLIB_INT128_SMAX, 1_i128), MLIB_INT128_SMIN) == 0, "fail"); static_assert(mlib_int128_scmp(mlib_int128_negate(MLIB_INT128_CAST(-42)), MLIB_INT128(42)) == 0, "fail"); static_assert(mlib_int128_scmp(mlib_int128_sub(5_i128, 3_i128), 2_i128) == 0, "fail"); static_assert(mlib_int128_scmp(mlib_int128_sub(3_i128, 5_i128), mlib_int128_negate(2_i128)) == 0, "fail"); static_assert(mlib_int128_ucmp(mlib_int128_sub(3_i128, 5_i128), mlib_int128_sub(MLIB_INT128_UMAX, 1_i128)) == 0, "fail"); static_assert(mlib_int128_scmp(mlib_int128_lshift(1_i128, 127), MLIB_INT128_SMIN) == 0, "fail"); static_assert(mlib_int128_scmp(mlib_int128_rshift(mlib_int128_lshift(1_i128, 127), 127), 1_i128) == 0, "fail"); // With no high-32 bits in the denominator static_assert(mlib_int128_div(316356263640858117670580590964547584140_i128, 13463362962560749016052695684_i128) == 23497566285_i128, "fail"); // Remainder correctness with high bit set: static_assert(mlib_int128_mod(292590981272581782572061492191999425232_i128, 221673222198185508195462959065350495048_i128) == 70917759074396274376598533126648930184_i128, "fail"); // Remainder with 64bit denom: static_assert(mlib_int128_mod(2795722437127403543495742528_i128, 708945413_i128) == 619266642_i128, "fail"); // 10-div: static_assert(mlib_int128_div(MLIB_INT128_SMAX, 10_i128) == 17014118346046923173168730371588410572_i128, "fail"); #endif // BROKEN_CONSTEXPR inline std::ostream &operator<<(std::ostream &out, const mlib_int128 &v) { out << mlib_int128_format(v).str; return out; } #ifndef BROKEN_CONSTEXPR static_assert(mlib_int128(MLIB_INT128_UMAX) == 340282366920938463463374607431768211455_i128, "fail"); // Check sign extension works correctly: static_assert(mlib_int128(MLIB_INT128_CAST(INT64_MIN)) == mlib_int128_negate(9223372036854775808_i128), "fail"); static_assert(mlib_int128(MLIB_INT128_CAST(INT64_MIN)) < mlib_int128_negate(9223372036854775807_i128), "fail"); static_assert(mlib_int128_negate(9223372036854775809_i128) < mlib_int128(MLIB_INT128_CAST(INT64_MIN)), "fail"); #endif #ifdef __SIZEOF_INT128__ // mlib_int128_to_native converts mlib_int128 to native __uint128_t. // Endian-ness is accounted for. static __uint128_t mlib_int128_to_native(mlib_int128 in) { __uint128_t out; uint8_t *out_u8 = (uint8_t *)&out; if (MLIB_IS_BIG_ENDIAN) { // Copy hi, then lo. memcpy(out_u8, &in.r.hi, sizeof(in.r.hi)); memcpy(out_u8 + sizeof(in.r.hi), &in.r.lo, sizeof(in.r.lo)); } else { // Copy lo, then hi. memcpy(out_u8, &in.r.lo, sizeof(in.r.lo)); memcpy(out_u8 + sizeof(in.r.lo), &in.r.hi, sizeof(in.r.hi)); } return out; } // native_to_mlib_int128 converts native __uint128_t to mlib_int128. // Endian-ness is accounted for. static mlib_int128 native_to_mlib_int128(__uint128_t in) { mlib_int128 out; uint8_t *in_u8 = (uint8_t *)∈ if (MLIB_IS_BIG_ENDIAN) { // Copy hi, then lo. memcpy(&out.r.hi, in_u8, sizeof(out.r.hi)); memcpy(&out.r.lo, in_u8 + sizeof(out.r.hi), sizeof(out.r.lo)); } else { // Copy lo, then hi. memcpy(&out.r.lo, in_u8, sizeof(out.r.lo)); memcpy(&out.r.hi, in_u8 + sizeof(out.r.lo), sizeof(out.r.hi)); } return out; } #endif // __SIZEOF_INT128__ static mlib_int128_divmod_result div_check(mlib_int128 num, mlib_int128 den) { // std::cout << "Check: " << num << " ÷ " << den << '\n'; mlib_int128_divmod_result res = mlib_int128_divmod(num, den); #ifdef __SIZEOF_INT128__ // When we have an existing i128 impl, test against that: __uint128_t num1 = mlib_int128_to_native(num); __uint128_t den1 = mlib_int128_to_native(den); __uint128_t q = num1 / den1; __uint128_t r = num1 % den1; mlib_int128_divmod_result expect; expect.quotient = native_to_mlib_int128(q); expect.remainder = native_to_mlib_int128(r); if (!mlib_int128_eq(expect.quotient, res.quotient) || !mlib_int128_eq(expect.remainder, res.remainder)) { std::cout << "unexpected result in division" << " num=" << mlib_int128_format(num).str << " den=" << mlib_int128_format(den).str << " expect.quotient=" << mlib_int128_format(expect.quotient).str << " expect.remainder=" << mlib_int128_format(expect.remainder).str << std::endl; } CHECK(expect.quotient == res.quotient); CHECK(expect.remainder == res.remainder); #endif // Check inversion by multiplication provides the correct result auto invert = mlib_int128_mul(res.quotient, den); invert = mlib_int128_add(invert, res.remainder); CHECK(invert == num); return res; } // Runtime checks, easier to debug that static_asserts int main() { mlib_int128 zero = MLIB_INT128(0); CHECK(mlib_int128_eq(zero, MLIB_INT128(0))); CHECK(mlib_int128_eq(zero, 0_i128)); CHECK(zero == 0_i128); auto two = MLIB_INT128(2); auto four = mlib_int128_add(two, two); CHECK(four == MLIB_INT128(4)); CHECK(four == 4_i128); CHECK(two == mlib_int128_add(two, zero)); // Addition wraps: mlib_int128 max = MLIB_INT128_SMAX; auto more = mlib_int128_add(max, four); CHECK(more == mlib_int128_add(MLIB_INT128_SMIN, MLIB_INT128(3))); // "Wrap" around zero: auto ntwo = MLIB_INT128_CAST(-2); auto sum = mlib_int128_add(ntwo, four); CHECK(sum == two); auto eight = mlib_int128_lshift(two, 2); CHECK(eight == MLIB_INT128(8)); auto big = mlib_int128_lshift(two, 72); CHECK(mlib_int128_scmp(big, MLIB_INT128(0)) > 0); auto four_v2 = mlib_int128_lshift(eight, -1); CHECK(four == four_v2); // Negative literals: CHECK(MLIB_INT128(-64) == mlib_int128_negate(64_i128)); CHECK(mlib_int128_mul(1_i128, 2_i128) == 2_i128); CHECK(mlib_int128_mul(1_i128, 0_i128) == 0_i128); CHECK(mlib_int128_mul(0_i128, 0_i128) == 0_i128); CHECK(mlib_int128_mul(2_i128, 73_i128) == 146_i128); CHECK(mlib_int128_mul(28468554863115876158655557_i128, 73_i128) == 2078204505007458959581855661_i128); CHECK(mlib_int128_mul(MLIB_INT128_CAST(-7), 4_i128) == MLIB_INT128_CAST(-28)); CHECK(mlib_int128_mul(MLIB_INT128_CAST(-7), MLIB_INT128_CAST(-7)) == 49_i128); // It's useful to specify bit patterns directly auto in_binary = 0b110101010110100100001101111001111010100010111100100101101011010110101001010110110011000100000100011110010101101001111110001000_i128; CHECK(in_binary == 70917759074396274376598533126648930184_i128); CHECK( in_binary == "0b110101010110100100001101111001111010100010111100100101101011010110101001010110110011000100000100011110010101101001111110001000"_i128); // Or hexadecimal auto in_hex = 0x355a4379ea2f25ad6a56cc411e569f88_i128; CHECK(in_hex == 70917759074396274376598533126648930184_i128); int8_t n = -12; CHECK(mlib_int128_scmp(zero, MLIB_INT128_CAST(n)) > 0); CHECK(mlib_int128_ucmp(zero, MLIB_INT128_CAST(n)) < 0); auto _2pow127 = mlib_int128_pow2(127); CHECK(std::string(mlib_int128_format(_2pow127).str) == "170141183460469231731687303715884105728"); auto r = div_check(27828649044156246570177174673037165454_i128, 499242349997913298655486252455941907_i128); CHECK(r.quotient == 55_i128); CHECK(r.remainder == 370319794271015144125430787960360569_i128); r = div_check(64208687961221311123721027584_i128, 3322092839076102144_i128); CHECK(r.remainder == 3155565729965670400_i128); // This division will trigger the rare Knuth 4.3.1D/D6 condition: r = div_check(31322872034807296605612234499929458960_i128, 34573864092216774938021667884_i128); CHECK(r.quotient == 905969663_i128); CHECK(r.remainder == 34573864092065898160364055868_i128); // Self-divide: r = div_check(628698094597401606590302208_i128, 628698094597401606590302208_i128); CHECK(r.quotient == 1_i128); CHECK(r.remainder == 0_i128); // With no high-32 bits in the denominator r = div_check(316356263640858117670580590964547584140_i128, 13463362962560749016052695684_i128); CHECK(r.quotient == 23497566285_i128); // Remainder correctness with high bit set: r = div_check(292590981272581782572061492191999425232_i128, 221673222198185508195462959065350495048_i128); CHECK(r.remainder == 70917759074396274376598533126648930184_i128); // Remainder with 64bit denom: r = div_check(2795722437127403543495742528_i128, 708945413_i128); CHECK(r.remainder == 619266642_i128); // 10-div: CHECK(mlib_int128_div(MLIB_INT128_SMAX, 10_i128) == 17014118346046923173168730371588410572_i128); std::random_device rd; std::seed_seq seed({rd(), rd(), rd(), rd()}); // Pick every numerator bit pattern from 0b00'00 to 0b11'11 for (auto nbits = 0u; nbits < 16u; ++nbits) { // This is an extremely rudimentary thread pool to parallelize the // division checks. It doesn't need to be rigorous or optimal, it only // needs to "just work." std::vector threads; // Pick every denominator bit pattern from 0b00'01 to 0b11'11: for (auto dbits = 1u; dbits < 16u; ++dbits) { // Randomness: std::mt19937 random; random.seed(seed); // Spawn a thread for this denominator bit pattern: threads.emplace_back([nbits, dbits, random]() mutable { std::uniform_int_distribution dist; // 100k random divisions: for (auto i = 0; i < 100000; ++i) { // Generate a denominator auto den = 0_i128; while (den == 0_i128) { // Regenerate until we don't have zero (very // unlikely, but be safe!) uint64_t dlo = 0, dhi = 0; (dbits & 1) && (dlo |= dist(random)); (dbits & 2) && (dlo |= (uint64_t)dist(random) << 32); (dbits & 4) && (dhi |= dist(random)); (dbits & 8) && (dhi |= (uint64_t)dist(random) << 32); den = MLIB_INT128_FROM_PARTS(dlo, dhi); } // Generate a numerator uint64_t nlo = 0, nhi = 0; (nbits & 1) && (nlo |= dist(random)); (nbits & 2) && (nlo |= (uint64_t)dist(random) << 32); (nbits & 4) && (nhi |= dist(random)); (nbits & 8) && (nhi |= (uint64_t)dist(random) << 32); mlib_int128 num = MLIB_INT128_FROM_PARTS(nlo, nhi); // Divide them: div_check(num, den); } }); } // Join the threads that are dividing: for (auto &t : threads) { t.join(); } } } libmongocrypt-1.19.0/src/mc-mlib/macros.h000066400000000000000000000026571521103432300202420ustar00rootroot00000000000000#ifndef MLIB_MACROS_H_INCLUDED #define MLIB_MACROS_H_INCLUDED #include "./user-check.h" /** * @brief Cross-C/C++ compatibility for a compound initializer to be treated as * a braced initializer * */ #ifdef __cplusplus #define MLIB_INIT(T) T #else #define MLIB_INIT(T) (T) #endif #ifdef __cplusplus #define _mlibCLinkageBegin extern "C" { #define _mlibCLinkageEnd } #else #define _mlibCLinkageBegin #define _mlibCLinkageEnd #endif /// Mark the beginning of a C-language-linkage section #define MLIB_C_LINKAGE_BEGIN _mlibCLinkageBegin /// End a C-language-linkage section #define MLIB_C_LINKAGE_END _mlibCLinkageEnd #if (defined(__cpp_constexpr) && __cpp_constexpr >= 201304L) || (defined(__cplusplus) && __cplusplus >= 201402L) \ || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) #define _mlibConstexprFn constexpr inline #else #define _mlibConstexprFn inline #endif /** * @brief Mark a function as constexpr * * Expands to `constexpr inline` in C++14 and above (and someday C26...?). * "inline" otherwise. */ #define mlib_constexpr_fn _mlibConstexprFn #ifdef __GNUC__ #define MLIB_ANNOTATE_PRINTF(FStringArgAt, VarArgsStartAt) \ __attribute__((format(__printf__, FStringArgAt, VarArgsStartAt))) #else #define MLIB_ANNOTATE_PRINTF(FStringArgAt, VarArgsStartAt) /* no-op */ #endif #endif // MLIB_MACROS_H_INCLUDED libmongocrypt-1.19.0/src/mc-mlib/path.h000066400000000000000000000244271521103432300177110ustar00rootroot00000000000000#ifndef MONGOCRYPT_PATH_PRIVATE_H #define MONGOCRYPT_PATH_PRIVATE_H #include "./user-check.h" #include "./str.h" #include #ifndef _WIN32 #include #else #include "./windows-lean.h" #endif typedef enum mpath_format { /// The POSIX path format MPATH_POSIX = 'p', /// The Win32 path format MPATH_WIN32 = 'w', /// The native path format for the current platform MPATH_NATIVE = #ifdef _WIN32 MPATH_WIN32, #else MPATH_POSIX, #endif } mpath_format; /** * @brief Determine if the given character is a path separator for the given * path format * * @param c A path character * @param f The path format to use */ static inline bool mpath_is_sep(char c, mpath_format f) { if (f == MPATH_WIN32) { return c == '/' || c == '\\'; } else { return c == '/'; } } /** * @brief Obtain the preferred path separator character for the given format */ static inline char mpath_preferred_sep(mpath_format f) { if (f == MPATH_WIN32) { return '\\'; } else { return '/'; } } /** * @brief Obtain the path string denoting the application's current working * directory * * @return mstr A new string which must be freed with mstr_free() */ static inline mstr mpath_current_path(void) { #if defined(_WIN32) while (1) { DWORD len = GetCurrentDirectoryW(0, NULL); wchar_t *wstr = calloc(sizeof(wchar_t), len); DWORD got_len = GetCurrentDirectoryW(len, wstr); if (got_len > len) { free(wstr); continue; } mstr_narrow_result nar = mstr_win32_narrow(wstr); free(wstr); assert(nar.error == 0); return nar.string; } #else mstr_mut mut = mstr_new(8096); char *p = getcwd(mut.raw.data, mut.raw.len); if (p == NULL) { mstr_free(mut.mstr); return MSTR_NULL; } mstr ret = mstr_copy_cstr(mut.raw.data); mstr_free(mut.mstr); return ret; #endif } /** * @brief Determine whether the given path string has a trailing path separator */ static inline bool mpath_has_trailing_sep(mstr_view path, mpath_format f) { return path.len && mpath_is_sep(path.data[path.len - 1], f); } /** * @brief Obtain the parent path of the given path. */ static inline mstr_view mpath_parent(mstr_view path, mpath_format f) { if (mpath_has_trailing_sep(path, f)) { // Remove trailing separators: while (mpath_has_trailing_sep(path, f)) { path = mstrv_remove_suffix(path, 1); } return path; } // Remove everything that isn't a path separator: while (path.len != 0 && !mpath_has_trailing_sep(path, f)) { path = mstrv_remove_suffix(path, 1); } // Remove trailing separators again while (path.len > 1 && mpath_has_trailing_sep(path, f)) { path = mstrv_remove_suffix(path, 1); } // The result is the parent path. return path; } /** * @brief Obtain the filename denoted by the given path. * * The returned path will include no directory separators. If the given path * ends with a directory separator, the single-dot '.' path is returned instead. */ static inline mstr_view mpath_filename(mstr_view path, mpath_format f) { if (!path.len) { return path; } const char *it = path.data + path.len; while (it != path.data && !mpath_is_sep(it[-1], f)) { --it; } size_t off = (size_t)(it - path.data); mstr_view fname = mstrv_subview(path, off, path.len); if (fname.len == 0) { return mstrv_lit("."); } return fname; } /** * @brief Join the two given paths into a single path * * The two strings will be combined into a single string with a path separator * between them. If either string is empty, the other string will be copied * without modification. * * @param base The left-hand of the join * @param suffix The right-hand of the join * @param f The path format to use * @return mstr A new string resulting from the join */ static inline mstr mpath_join(mstr_view base, mstr_view suffix, mpath_format f) { if (!base.len) { return mstr_copy(suffix); } if (mpath_has_trailing_sep(base, f)) { return mstr_append(base, suffix); } if (!suffix.len) { return mstr_copy(base); } if (mpath_is_sep(suffix.data[0], f)) { return mstr_append(base, suffix); } // We must insert a path separator between the two strings assert(base.len <= SIZE_MAX - suffix.len - 1u); mstr_mut r = mstr_new(base.len + suffix.len + 1); char *p = r.raw.data; memcpy(p, base.data, base.len); p += base.len; *p++ = mpath_preferred_sep(f); memcpy(p, suffix.data, suffix.len); return r.mstr; } /** * @brief Obtain the root name for the given path. * * For the Windows format, this will return the drive letter, if present. * Otherwise, this will return an empty string. */ static inline mstr_view mpath_root_name(mstr_view path, mpath_format f) { if (f == MPATH_WIN32 && path.len > 1) { char c = path.data[0]; if (path.len > 2 && path.data[1] == ':' && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) { return mstrv_subview(path, 0, 2); } } return mstrv_subview(path, 0, 0); } /** * @brief Return the root directory of the given path, if present. * * @note This will not include the drive letter of a Win32 path. */ static inline mstr_view mpath_root_directory(mstr_view path, mpath_format f) { mstr_view rname = mpath_root_name(path, f); path = mstrv_subview(path, rname.len, path.len); if (path.len && mpath_is_sep(path.data[0], f)) { return mstrv_subview(path, 0, 1); } else { return mstrv_subview(path, 0, 0); } } /** * @brief Obtain the root filepath of the given path. * * This will include both the root name and the root filepath, if present. */ static inline mstr_view mpath_root_path(mstr_view path, mpath_format f) { mstr_view rname = mpath_root_name(path, f); mstr_view rdir = mpath_root_directory(path, f); assert(rname.len <= SIZE_MAX - rdir.len); return mstrv_subview(path, 0, rname.len + rdir.len); } /** * @brief Determine whether the given filepath designates a single unambiguous * filesystem location. * * @note A Win32 filepath without a drive letter is not absolute! */ static inline bool mpath_is_absolute(mstr_view path, mpath_format f) { if (f == MPATH_WIN32) { // Win32 requires both a root name and a root directory for an absolute // path return mpath_root_name(path, f).len && mpath_root_directory(path, f).len; } else { // POSIX doesn't have "root names" return mpath_root_directory(path, f).len; } } /** * @brief Obtain a relative path from the given filepath * * If the path has a root path, returns the content of the path following that * root path, otherwise returns the same path itself. */ static inline mstr_view mpath_relative_path(mstr_view path, mpath_format f) { mstr_view root = mpath_root_path(path, f); return mstrv_subview(path, root.len, path.len); } /** * @brief Convert the filepath from one format to a preferred form in another * format. * * @note The return value must be freed with mstr_free() */ static inline mstr mpath_to_format(mpath_format from, mstr_view path, mpath_format to) { mstr_mut ret = mstr_new(path.len); const char *p = path.data; char *out = ret.raw.data; const char *stop = path.data + path.len; for (; p != stop; ++p, ++out) { if (mpath_is_sep(*p, from)) { *out = mpath_preferred_sep(to); } else { *out = *p; } } return ret.mstr; } /** * @brief Determine whether the given path is relative (not absolute) */ static inline bool mpath_is_relative(mstr_view path, mpath_format f) { return !mpath_is_absolute(path, f); } /** * @brief Convert the given path to an absolute path, if it is not already. * * @note The return value must be freed with mstr_free() */ static inline mstr mpath_absolute(mstr_view path, mpath_format f); /** * @brief Resolve a path to an absolute path from the given base path. * * @note This is not the same as mpath_join(): If the given path is already * absolute, returns that path unchanged. Otherwise, resolves that path as being * relative to `base`. * * @note If `base` is also a relative path, it will also be given to * mpath_absolute() to resolve it. */ static inline mstr mpath_absolute_from(mstr_view path, mstr_view base, mpath_format f) { mstr_view rname = mpath_root_name(path, f); mstr_view rdir = mpath_root_directory(path, f); if (rname.len) { if (rdir.len) { // The path is already fully absolute return mstr_copy(path); } else { mstr abs_base = mpath_absolute(base, f); mstr_view base_rdir = mpath_root_directory(abs_base.view, f); mstr_view base_relpath = mpath_relative_path(abs_base.view, f); mstr_view relpath = mpath_relative_path(path, f); mstr ret = mstr_copy(rname); mstr_assign(&ret, mpath_join(ret.view, base_rdir, f)); mstr_assign(&ret, mpath_join(ret.view, base_relpath, f)); mstr_assign(&ret, mpath_join(ret.view, relpath, f)); mstr_free(abs_base); return ret; } } else { // No root name if (rdir.len) { if (f == MPATH_POSIX) { // No root name, but a root directory on a POSIX path indicates an // absolute path return mstr_copy(path); } mstr abs_base = mpath_absolute(base, f); mstr_view base_rname = mpath_root_name(abs_base.view, f); mstr ret = mpath_join(base_rname, path, f); mstr_free(abs_base); return ret; } else { mstr abs_base = mpath_absolute(base, f); mstr r = mpath_join(abs_base.view, path, f); mstr_free(abs_base); return r; } } } static inline mstr mpath_absolute(mstr_view path, mpath_format f) { if (mpath_is_absolute(path, f)) { return mstr_copy(path); } mstr cur = mpath_current_path(); mstr ret = mpath_absolute_from(path, cur.view, MPATH_NATIVE); mstr_assign(&ret, mpath_to_format(MPATH_NATIVE, ret.view, f)); mstr_free(cur); return ret; } #endif // MONGOCRYPT_PATH_PRIVATE_H libmongocrypt-1.19.0/src/mc-mlib/path.test.c000066400000000000000000000056171521103432300206620ustar00rootroot00000000000000#include "./path.h" #define TEST_DECOMP(Part, Given, Expect) \ MSTR_ASSERT_EQ(mpath_##Part(mstrv_view_cstr(Given), MPATH_NATIVE), mstrv_view_cstr(Expect)) static void test_make_absolute(mpath_format f, const char *part, const char *base, const char *expect) { mstr result = mpath_absolute_from(mstrv_view_cstr(part), mstrv_view_cstr(base), f); MSTR_ASSERT_EQ(result.view, mstrv_view_cstr(expect)); mstr_free(result); } int main(void) { mstr s = mstr_copy_cstr("/foo/bar/baz.txt"); MSTR_ASSERT_EQ(mpath_parent(s.view, MPATH_NATIVE), mstrv_lit("/foo/bar")); MSTR_ASSERT_EQ(mpath_parent(mpath_parent(s.view, MPATH_NATIVE), MPATH_NATIVE), mstrv_lit("/foo")); mstr_assign( &s, mpath_join(mpath_parent(mstrv_lit("/foo/bar/baz.txt"), MPATH_WIN32), mstrv_lit("quux.pdf"), MPATH_WIN32)); MSTR_ASSERT_EQ(s.view, mstrv_lit("/foo/bar\\quux.pdf")); mstr_assign( &s, mpath_join(mpath_parent(mstrv_lit("/foo/bar/baz.txt"), MPATH_POSIX), mstrv_lit("quux.pdf"), MPATH_POSIX)); MSTR_ASSERT_EQ(s.view, mstrv_lit("/foo/bar/quux.pdf")); TEST_DECOMP(parent, "/foo", "/"); TEST_DECOMP(parent, "/foo/", "/foo"); TEST_DECOMP(parent, "foo/", "foo"); TEST_DECOMP(parent, ".", ""); TEST_DECOMP(parent, "..", ""); TEST_DECOMP(parent, "foo", ""); TEST_DECOMP(parent, "/", ""); TEST_DECOMP(parent, "foo/bar", "foo"); TEST_DECOMP(parent, "/foo/bar", "/foo"); TEST_DECOMP(parent, "///foo///bar", "///foo"); TEST_DECOMP(parent, "/.", "/"); TEST_DECOMP(filename, "foo.exe", "foo.exe"); TEST_DECOMP(filename, "/foo.exe", "foo.exe"); TEST_DECOMP(filename, "/", "."); TEST_DECOMP(filename, "/foo", "foo"); TEST_DECOMP(filename, "/foo/", "."); TEST_DECOMP(filename, "/foo/..", ".."); TEST_DECOMP(filename, "/foo/.", "."); TEST_DECOMP(filename, "", ""); TEST_DECOMP(relative_path, "", ""); TEST_DECOMP(relative_path, ".", "."); TEST_DECOMP(relative_path, "..", ".."); TEST_DECOMP(relative_path, "foo", "foo"); TEST_DECOMP(relative_path, "/", ""); test_make_absolute(MPATH_POSIX, "foo", "/bar", "/bar/foo"); test_make_absolute(MPATH_POSIX, "baz.txt", "/bar/foo/", "/bar/foo/baz.txt"); test_make_absolute(MPATH_WIN32, "foo", "C:/bar", "C:/bar\\foo"); test_make_absolute(MPATH_WIN32, "baz.txt", "D:/bar/foo/", "D:/bar/foo/baz.txt"); // Just test calling with each combo, no validation. mstr_assign(&s, mpath_absolute(mstrv_lit("foo"), MPATH_WIN32)); mstr_assign(&s, mpath_absolute(mstrv_lit("foo"), MPATH_POSIX)); mstr_assign(&s, mpath_absolute(mstrv_lit("/foo"), MPATH_WIN32)); mstr_assign(&s, mpath_absolute(mstrv_lit("/foo"), MPATH_POSIX)); mstr_assign(&s, mpath_absolute(mstrv_lit("Z:/foo"), MPATH_WIN32)); mstr_assign(&s, mpath_absolute(mstrv_lit("Z:/foo"), MPATH_POSIX)); mstr_free(s); } libmongocrypt-1.19.0/src/mc-mlib/str.h000066400000000000000000000720771521103432300175710ustar00rootroot00000000000000#ifndef MONGOCRYPT_STR_PRIVATE_H #define MONGOCRYPT_STR_PRIVATE_H #include "./macros.h" #include "./user-check.h" #include #include #include /* INT_MAX */ #include #include #include #include #include #if defined(MLIB_HAVE_STRINGS_H) #include /* For strncasecmp. */ #endif // Deliberately using old-style casts for C and C++ compatibility. #if defined(__cplusplus) && defined(__clang__) _Pragma("clang diagnostic push"); _Pragma("clang diagnostic ignored \"-Wold-style-cast\""); #endif MLIB_C_LINKAGE_BEGIN /** * @brief A simple non-owning string-view type. * * The viewed string can be treated as an array of char. It's pointed-to data * must not be freed or manipulated. * * @note The viewed string is NOT guaranteed to be null-terminated. It WILL * be null-terminated if: Directly created from a string literal via * @ref mstrv_lit, OR created by accessing the @ref mstr::view member of an * @ref mstr object, OR returned from @ref mstrv_view_cstr. */ typedef struct mstr_view { /** * @brief Pointer to the beginning of the code unit array. * * @note DO NOT MODIFY */ const char *data; /** * @brief Length of the pointed-to code unit array * * @note DO NOT MODIFY */ size_t len; } mstr_view; /** * @brief A simple string utility type. * * This string type has the following semantics: * * The member `data` is a pointer to the beginning of a read-only array of code * units. This array will always be null-terminated, but MAY contain * intermittent null characters. The member `len` is the length of the code unit * array (not including the null terminator). These two members should not be * modified. * * The `view` member is a union member that will view the `mstr` as an * @ref mstr_view. * * If you create an @ref mstr, it MUST eventually be passed to @ref mstr_free() * * The pointed-to code units of an mstr are immutable. To initialize the * contents of an mstr, @ref mstr_new returns an @ref mstr_mut, which can then * be "sealed" by converting it to an @ref mstr through the @ref mstr_mut::mstr * union member. * * By convention, passing/returning an `mstr` to/from a function should * relinquish ownership of that `mstr` to the callee/caller, respectively. * Passing or returning an `mstr_view` is non-owning. */ typedef union mstr { struct { /** * @brief Pointer to the beginning of the code unit array. * * @note DO NOT MODIFY */ const char *data; /** * @brief Length of the pointed-to code unit array * * @note DO NOT MODIFY */ size_t len; } raw; /** * @brief A non-owning `mstr_view` of the string */ mstr_view view; } mstr; /** * @brief An interface for initializing the contents of an mstr. * * Returned by @ref mstr_new(). Once initialization is complete, the result can * be used as an @ref mstr by accessing the @ref mstr_mut::mstr member. */ typedef union mstr_mut { struct { /** * @brief Pointer to the beginning of the mutable code unit array. * * @note DO NOT MODIFY THE POINTER VALUE. Only modify the pointed-to * characters. */ char *data; /** * @brief Length of the pointed-to code unit array. * * @note DO NOT MODIFY */ size_t len; } raw; /// Convert the mutable string to an immutable string union mstr mstr; /// Convert the mutable string to an immutable string view mstr_view view; } mstr_mut; /** * @brief A null @ref mstr */ #define MSTR_NULL (MLIB_INIT(mstr){{NULL, 0}}) /** * @brief A null @ref mstr_view */ #define MSTRV_NULL (MLIB_INIT(mstr_view){NULL, 0}) /** * @brief Create an @ref mstr_view that views the given string literal */ #define mstrv_lit(String) (mstrv_view_data(String "", (sizeof String) - 1)) /** * @brief Create a new mutable code-unit array of the given length, * zero-initialized. The caller can then modify the code units in the array via * the @ref mstr_mut::data member. Once finished modifying, can be converted to * an immutable mstr by copying the @ref mtsr_mut::mstr union member. * * @param len The length of the new string. * @return mstr_mut A new mstr_mut * * @note The @ref mstr_mut::mstr member MUST eventually be given to * @ref mstr_free(). */ static inline mstr_mut mstr_new(size_t len) { #ifndef __clang_analyzer__ assert(len < SIZE_MAX); return MLIB_INIT(mstr_mut){{(char *)calloc(1, len + 1), len}}; #else // Clang-analyzer is smart enough to see the calloc(), but not smart enough // to link it to the free() in mstr_free() return MLIB_INIT(mstr_mut){}; #endif } /** * @brief Create a non-owning @ref mstr_view from the given C string and length * * @param s A pointer to the beginning of a character array. * @param len The length of the character array, in code units * @return mstr_view A non-owning string. */ static inline mstr_view mstrv_view_data(const char *s, size_t len) { return MLIB_INIT(mstr_view){s, len}; } /** * @brief Create a non-owning @ref mstr_view from a C-style null-terminated * string. * * @param s A pointer to a null-terminated character array * @return mstr_view A view of the pointed-to string */ static inline mstr_view mstrv_view_cstr(const char *s) { return mstrv_view_data(s, strlen(s)); } /** * @brief Create an @ref mstr from the given character array and length. * * @param s A pointer to a character array * @param len The length of the string to create * @return mstr A new null-terminated string with the contents copied from the * pointed-to array. * * @note The resulting string will be null-terminated. */ static inline mstr mstr_copy_data(const char *s, size_t len) { mstr_mut r = mstr_new(len); memcpy(r.raw.data, s, len); return r.mstr; } /** * @brief Create an @ref mstr from A C-style null-terminated string. * * @param s A pointer to a null-terminated character array * @return mstr A new string copied from the pointed-to string */ static inline mstr mstr_copy_cstr(const char *s) { return mstr_copy_data(s, strlen(s)); } /** * @brief Copy the contents of the given string view * * @param s A string view to copy from * @return mstr A new string copied from the given view */ static inline mstr mstr_copy(mstr_view s) { return mstr_copy_data(s.data, s.len); } /** * @brief Free the resources of the given string * * @param s The string to free */ static inline void mstr_free(mstr s) { free((char *)s.raw.data); } /** * @brief Resize the given mutable string, maintaining the existing content, and * zero-initializing any added characters. * * @param s The @ref mstr_mut to update * @param new_len The new length of the string */ static inline void mstrm_resize(mstr_mut *s, size_t new_len) { if (new_len <= s->raw.len) { s->raw.len = new_len; } else { const size_t old_len = s->raw.len; #ifndef __clang_analyzer__ // Clang-analyzer is smart enough to see the calloc(), but not smart // enough to link it to the free() in mstr_free() assert(new_len < SIZE_MAX); s->raw.data = (char *)realloc((char *)s->raw.data, new_len + 1); assert(s->raw.data); #endif s->raw.len = new_len; assert(new_len >= old_len); memset(s->raw.data + old_len, 0, new_len - old_len); } s->raw.data[new_len] = (char)0; } /** * @brief Free and re-assign the given @ref mstr * * @param s Pointer to an @ref mstr. This will be freed, then updated to the * value of @ref from * @param from An @ref mstr to take from. * * @note Ownership of the resource is handed to the pointed-to @ref s. * Equivalent to: * * ```c * mstr s = some_mstr(); * mstr another = get_another_mstr(); * mstr_free(s); * s = another; * ``` * * Intended as a convenience for rebinding an @ref mstr in a single statement * from an expression returning a new @ref mstr, which may itself use @ref s, * without requiring a temporary variable, for example: * * ```c * mstr s = get_mstr(); * mstr_assign(&s, convert_to_uppercase(s.view)); * ``` */ static inline void mstr_assign(mstr *s, mstr from) { mstr_free(*s); *s = from; } /** * @brief Find the index of the first occurrence of the given "needle" as a * substring of another string. * * @param given A string to search within * @param needle The substring to search for * @return int The zero-based index of the first instance of `needle` in * `given`, or -1 if no substring is found. */ static inline int mstr_find(mstr_view given, mstr_view needle) { const char *const scan_end = given.data + given.len; const char *const needle_end = needle.data + needle.len; for (const char *scan = given.data; scan != scan_end; ++scan) { size_t remain = (size_t)(scan_end - scan); if (remain < needle.len) { break; } const char *subscan = scan; for (const char *nscan = needle.data; nscan != needle_end; ++nscan, ++subscan) { if (*nscan == *subscan) { continue; } else { goto skip; } } // Got through the whole loop of scanning the needle return (int)(scan - given.data); skip: (void)0; } return -1; } /** * @brief Find the index of the last occurrence of the given "needle" as a * substring of another string. * * @param given A string to search within * @param needle The substring to search for * @return int The zero-based index of the last instance of `needle` in * `given`, or -1 if no substring is found. */ static inline int mstr_rfind(mstr_view given, mstr_view needle) { if (needle.len > given.len) { return -1; } const char *scan = given.data + given.len - needle.len; const char *const needle_end = needle.data + needle.len; for (; scan >= given.data; --scan) { const char *subscan = scan; for (const char *nscan = needle.data; nscan != needle_end; ++nscan, ++subscan) { if (*nscan == *subscan) { continue; } else { goto skip; } } // Got through the whole loop of scanning the needle return (int)(scan - given.data); skip: (void)0; } return -1; } /** * @brief Modify a string by deleting and/or inserting another string. * * @param s The string to modify * @param at The position at which to insert and delete characters * @param del_count The number of characters to delete. Clamped to the string * length. * @param insert The string to insert at `at`. * @return mstr A new string that is the result of the splice */ static inline mstr mstr_splice(mstr_view s, size_t at, size_t del_count, mstr_view insert) { assert(at <= s.len); const size_t remain = s.len - at; if (del_count > remain) { del_count = remain; } /* at this point, it is absolutely necessary that del_count <= s.len */ assert(s.len - del_count <= SIZE_MAX - insert.len); const size_t new_size = s.len - del_count + insert.len; mstr_mut ret = mstr_new(new_size); char *p = ret.raw.data; memcpy(p, s.data, at); p += at; if (insert.data) { memcpy(p, insert.data, insert.len); p += insert.len; } /* 'at <= s.len' was already asserted earlier */ assert(s.len - at >= del_count); memcpy(p, s.data + at + del_count, s.len - at - del_count); return ret.mstr; } /** * @brief Append the given suffix to the given string */ static inline mstr mstr_append(mstr_view s, mstr_view suffix) { return mstr_splice(s, s.len, 0, suffix); } /** * @brief Prepend the given prefix to the given string */ static inline mstr mstr_prepend(mstr_view s, mstr_view prefix) { return mstr_splice(s, 0, 0, prefix); } /** * @brief Insert the given string into another string * * @param s The string to start with * @param at The position in `s` where `infix` will be inserted * @param infix The string to insert into `s` * @return mstr A new string with `infix` inserted */ static inline mstr mstr_insert(mstr_view s, size_t at, mstr_view infix) { return mstr_splice(s, at, 0, infix); } /** * @brief Erase characters from the given string * * @param s The string to start with * @param at The position at which to begin deleting characters * @param count The number of characters to remove * @return mstr A new string with the deletion result. */ static inline mstr mstr_erase(mstr_view s, size_t at, size_t count) { return mstr_splice(s, at, count, mstrv_view_cstr("")); } /** * @brief Erase `len` characters from the beginning of the string */ static inline mstr mstr_remove_prefix(mstr_view s, size_t len) { return mstr_erase(s, 0, len); } /** * @brief Erase `len` characters from the end of the string */ static inline mstr mstr_remove_suffix(mstr_view s, size_t len) { assert(s.len >= len); return mstr_erase(s, s.len - len, len); } /** * @brief Obtain a substring of the given string * * @param s The string to start with * @param at The beginning position of the new string * @param len The number of characters to include. Automatically clamped to the * remaining length. * @return mstr A new string that is a substring of `s` */ static inline mstr mstr_substr(mstr_view s, size_t at, size_t len) { assert(at <= s.len); const size_t remain = s.len - at; if (len > remain) { len = remain; } mstr_mut r = mstr_new(len); memcpy(r.raw.data, s.data + at, len); return r.mstr; } /** * @brief Obtain a view of a substring of another string. * * @param s The string to view * @param at The position at which the new view will begin * @param len The number of characters to view. Automatically clamped to the * remaining length. * @return mstr_view A view of `s`. */ static inline mstr_view mstrv_subview(mstr_view s, size_t at, size_t len) { assert(at <= s.len); const size_t remain = s.len - at; if (len > remain) { len = remain; } return mstrv_view_data(s.data + at, len); } /** * @brief Obtain a view of another string by removing `len` characters from the * front */ static inline mstr_view mstrv_remove_prefix(mstr_view s, size_t len) { return mstrv_subview(s, len, s.len); } /** * @brief Obtain a view of another string by removing `len` characters from the * end. */ static inline mstr_view mstrv_remove_suffix(mstr_view s, size_t len) { assert(s.len >= len); return mstrv_subview(s, 0, s.len - len); } /** * @brief Truncate the given string to `new_len` characters. * * @param s The string to truncate * @param new_len The new length of the string * @return mstr A new string copied from the beginning of `s` */ static inline mstr mstr_trunc(mstr_view s, size_t new_len) { assert(new_len <= s.len); return mstr_remove_suffix(s, s.len - new_len); } /** * @brief Obtain a new string with all occurrences of a string replaced with a * different string * * @param string The string to start with * @param find The substring that will be replaced * @param subst The string to insert in place of `find` * @return mstr A new string modified from `string` * * @note If `find` is empty, returns a copy of `string` */ static inline mstr mstr_replace(const mstr_view string, const mstr_view find, const mstr_view subst) { if (find.len == 0) { // Finding an empty string would loop forever return mstr_copy(string); } // First copy the string mstr ret = mstr_copy(string); // Keep an index of how far we have processed size_t whence = 0; for (;;) { // Chop off the front that has already been processed mstr_view tail = mstrv_subview(ret.view, whence, SIZE_MAX); // Find where in that tail is the next needle int pos = mstr_find(tail, find); if (pos == -1) { // We're done break; } // Do the replacement assert(whence <= SIZE_MAX - (size_t)pos); mstr_assign(&ret, mstr_splice(ret.view, (size_t)pos + whence, find.len, subst)); // Advance our position by how many chars we skipped and how many we // inserted whence += (size_t)pos + subst.len; } return ret; } /** * @brief Determine whether two strings are equivalent. */ static inline bool mstr_eq(mstr_view left, mstr_view right) { if (left.len != right.len) { return false; } return memcmp(left.data, right.data, left.len) == 0; } /** * @brief Determine whether two strings are equivalent ignoring case. */ static inline bool mstr_eq_ignore_case(mstr_view left, mstr_view right) { #ifdef _WIN32 #define _mstr_strncasecmp _strnicmp #else #define _mstr_strncasecmp strncasecmp #endif if (left.len != right.len) { return false; } return _mstr_strncasecmp(left.data, right.data, left.len) == 0; #undef _mstr_strncasecmp } /// Determine whether the given character is an printable ASCII codepoint static inline bool mstr_is_printable(char c) { return (c >= ' ' && c <= '~'); } /// Write the given string to `out`, rendering non-printable characters as hex /// escapes static inline void _mstr_write_str_repr_(FILE *out, mstr_view s) { for (const char *it = s.data; it != s.data + s.len; ++it) { if (mstr_is_printable(*it)) { fputc(*it, out); } else { fprintf(out, "\\x%.2x", (unsigned)(unsigned char)*it); } } } static inline void _mstr_assert_fail_(mstr_view left, const char *predicate, mstr_view right, const char *file, int line) { fprintf(stderr, "%s:%d: ASSERTION FAILED: \"", file, line); _mstr_write_str_repr_(stderr, left); fprintf(stderr, "\" %s \"", predicate); _mstr_write_str_repr_(stderr, right); fprintf(stderr, "\"\n"); abort(); } static inline void _mstr_assert_(mstr_view left, mstr_view right, bool (*pred)(mstr_view left, mstr_view right), bool B, const char *pred_str, const char *file, int line) { if (pred(left, right) != B) { mstr pstr = mstr_copy_cstr(pred_str); if (!B) { mstr_assign(&pstr, mstr_prepend(pstr.view, mstrv_lit("not "))); } _mstr_assert_fail_(left, pstr.raw.data, right, file, line); } } #define MSTR_ASSERT(Bool, Left, Pred, Right) \ (_mstr_assert_((Left), (Right), mstr_##Pred, (Bool), #Pred, __FILE__, __LINE__)) /** * @brief Assert that two strings are equivalent. * * Prints and error message and aborts if they are not */ #define MSTR_ASSERT_EQ(Left, Right) MSTR_ASSERT(true, Left, eq, Right) /** * @brief Determine whether the given string contains the given substring * * @param given A string to search within * @param needle A substring to search for * @return true If `given` contains at least one occurrence of `needle` * @return false Otherwise */ static inline bool mstr_contains(mstr_view given, mstr_view needle) { return mstr_find(given, needle) >= 0; } /** * @brief Determine whether `given` starts with `prefix` */ static inline bool mstr_starts_with(mstr_view given, mstr_view prefix) { given = mstrv_subview(given, 0, prefix.len); return mstr_eq(given, prefix); } /** * @brief Determine whether `given` ends with `suffix` */ static inline bool mstr_ends_with(mstr_view given, mstr_view suffix) { if (suffix.len > given.len) { return false; } given = mstrv_subview(given, given.len - suffix.len, SIZE_MAX); return mstr_eq(given, suffix); } /// Compound in-place version of @ref mstr_splice static inline void mstr_inplace_splice(mstr *s, size_t at, size_t del_count, mstr_view insert) { mstr_assign(s, mstr_splice(s->view, at, del_count, insert)); } /// Compound in-place version of @ref mstr_append static inline void mstr_inplace_append(mstr *s, mstr_view suffix) { mstr_assign(s, mstr_append(s->view, suffix)); } /// Compound in-place version of @ref mstr_prepend static inline void mstr_inplace_prepend(mstr *s, mstr_view prefix) { mstr_assign(s, mstr_prepend(s->view, prefix)); } /// Compound in-place version of @ref mstr_insert static inline void mstr_inplace_insert(mstr *s, size_t at, mstr_view infix) { mstr_assign(s, mstr_insert(s->view, at, infix)); } /// Compound in-place version of @ref mstr_erase static inline void mstr_inplace_erase(mstr *s, size_t at, size_t count) { mstr_assign(s, mstr_erase(s->view, at, count)); } /// Compound in-place version of @ref mstr_remove_prefix static inline void mstr_inplace_remove_prefix(mstr *s, size_t len) { mstr_assign(s, mstr_remove_prefix(s->view, len)); } /// Compound in-place version of @ref mstr_remove_suffix static inline void mstr_inplace_remove_suffix(mstr *s, size_t len) { mstr_assign(s, mstr_remove_suffix(s->view, len)); } /// Compound in-place version of @ref mstr_substr static inline void mstr_inplace_substr(mstr *s, size_t at, size_t count) { mstr_assign(s, mstr_substr(s->view, at, count)); } /// Compound in-place version of @ref mstr_trunc static inline void mstr_inplace_trunc(mstr *s, size_t new_len) { mstr_assign(s, mstr_trunc(s->view, new_len)); } /// Compound in-place version of @ref mstr_replace static inline void mstr_inplace_replace(mstr *s, mstr_view find, mstr_view subst) { mstr_assign(s, mstr_replace(s->view, find, subst)); } #ifdef _WIN32 #include "./windows-lean.h" /** * @brief The result type of mstr_win32_widen */ typedef struct mstr_widen_result { wchar_t *wstring; int error; } mstr_widen_result; /** * @brief Widen a UTF-8 string using Win32 MultiBytetoWideChar * * @param str The UTF-8 string to widen. * @return mstr_widen_result The result of widening, which may contain an error. * * @note The returned @ref mstr_widen_result::wstring must be given to free() */ static inline mstr_widen_result mstr_win32_widen(mstr_view str) { assert(str.len <= INT_MAX); int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data, (int)str.len, NULL, 0); if (length == 0 && str.len != 0) { return MLIB_INIT(mstr_widen_result){NULL, (int)GetLastError()}; } wchar_t *ret = (wchar_t *)calloc(length + 1, sizeof(wchar_t)); assert(length < INT_MAX); int got_length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data, (int)str.len, ret, length + 1); assert(got_length == length); return MLIB_INIT(mstr_widen_result){ret, 0}; } /** * @brief The result type of mstr_win32_narrow */ typedef struct mstr_narrow_result { mstr string; int error; } mstr_narrow_result; /** * @brief Narrow a UTF-16 string to UTF-8 using Win32 WideCharToMultiByte * * @param wstring A null-terminated UTF-16 string to narrow * @return mstr_narrow_result The result of narrowing, which may contain an * error. * * @note The returned @ref mstr_narrow_result::string must be freed with * mstr_free() */ static inline mstr_narrow_result mstr_win32_narrow(const wchar_t *wstring) { // Some older versions of MinGW fail to include the WC_ERR_INVALID_CHARS // flag, so we will specify it manually: DWORD wcflags = 0x80; // WC_ERR_INVALID_CHARS int length = WideCharToMultiByte(CP_UTF8, wcflags, wstring, -1 /* wstring is null-terminated */, NULL, 0, NULL, NULL); if (length == 0 && wstring[0] != 0) { return MLIB_INIT(mstr_narrow_result){MSTR_NULL, (int)GetLastError()}; } // Allocate a new string, not including the null terminator mstr_mut ret = mstr_new((size_t)(length - 1)); int got_len = WideCharToMultiByte(CP_UTF8, wcflags, wstring, -1, ret.raw.data, // Plus one byte for the NUL (int)(ret.raw.len + 1), NULL, NULL); assert(length == got_len); return MLIB_INIT(mstr_narrow_result){ret.mstr, 0}; } #endif /// Iteration state for string splitting struct _mstr_split_iter_ { /// What hasn't been parsed yet mstr_view remaining; /// The string that we split on mstr_view splitter; /// The current part mstr_view part; /// A once-var for the inner loop. Set to 1 by iter_next, then decremented int once; /// The loop state. Starts at zero. Set to one when we part the final split. /// Set to two to break out of the loop. int state; }; /// Hidden function to advance a string-split iterator static inline void _mstr_split_iter_next_(struct _mstr_split_iter_ *iter) { if (iter->once == 1) { // We only get here if the loop body hit a 'break', skipping the decrement // of the 'once'. Break out of the whole loop, as the user expects. iter->state = 2; return; } if (iter->state == 1) { // We just completed the final loop pass. iter->state = 2; return; } // Find the next occurence of the token const int pos = mstr_find(iter->remaining, iter->splitter); if (pos < 0) { // There are no more occurences. yield the remaining string iter->part = iter->remaining; iter->remaining = mstrv_subview(iter->remaining, iter->remaining.len, 0); // Set state to 1 to break on the next pass iter->state = 1; } else { // Advance our parts: iter->part = mstrv_subview(iter->remaining, 0, (size_t)pos); assert(iter->splitter.len <= SIZE_MAX - (size_t)pos); iter->remaining = mstrv_subview(iter->remaining, (size_t)pos + iter->splitter.len, SIZE_MAX); } // Prime the inner "loop" to execute once iter->once = 1; } /// init a new split iterator static inline struct _mstr_split_iter_ _mstr_split_iter_begin_(mstr_view str, mstr_view split) { struct _mstr_split_iter_ iter = {str, split}; _mstr_split_iter_next_(&iter); return iter; } /// Check whether we are done iterating static inline bool _mstr_split_iter_done_(struct _mstr_split_iter_ *iter) { return iter->state == 2; } // clang-format off #define MSTR_ITER_SPLIT(LineVar, String, SplitToken) \ /* Open the main outer loop */ \ for (/* Declare and init the iterator */ \ struct _mstr_split_iter_ _iter_var_ = \ _mstr_split_iter_begin_ ((String), (SplitToken)); \ /* Iterate until it is marked as done */ \ !_mstr_split_iter_done_ (&_iter_var_); \ _mstr_split_iter_next_ (&_iter_var_)) \ /* This inner loop will only execute once, but gives us */ \ /* a point to declare the loop variable: */ \ for (mstr_view const LineVar = _iter_var_.part; \ _iter_var_.once; \ --_iter_var_.once) // clang-format on /** * @brief Equivalent to strlen(), but has a constexpr annotation. */ static mlib_constexpr_fn size_t mlib_strlen(const char *s) { size_t r = 0; for (; *s; ++r, ++s) {} return r; } /** * @brief Copy characters into the destination, guaranteed null-terminated and * bounds checked. * * @param dst Pointer to the beginning of the destination array. * @param dst_bufsize The size of the destination array, in characters. MUST be * greater than zero. * @param src Pointer to the beginning of a null-terminated character array. * @param src_bufsize The size of the array pointed-to by `src`. * @return size_t The number `R` of characters written (NOT including the null * terminator). R is guaranteed to be less than dst_bufsize, and * less-than-or-equal-to src_bufsize. * * @note Characters beyond (dst + R) are unmodified. dst[R] is guaranteed to * be a null terminator. */ static mlib_constexpr_fn size_t mlib_strnmcopy(char *dst, size_t dst_bufsize, const char *src, size_t src_bufsize) { // No empty destination, since we *must* write a null terminator: assert(dst_bufsize > 0); // The maximum number of characters in the dest is one less than the buffer // size, since we need room for the null terminator: const size_t dstlen = dst_bufsize - 1u; // The actual maximum number of characters we can copy is the less of the // source length and the dest length: const size_t minlen = dstlen < src_bufsize ? dstlen : src_bufsize; // Track what we copy: size_t ncopied = 0; while (ncopied != minlen // Stop if we hit our character limit && *src != 0 // Or if we hit the null terminator in the source ) { // Copy: *dst = *src; // Advance: ++dst; ++src; ++ncopied; } // "dst" now points past the final character we copied (if any), and is still // in-bounds. This will be the null terminator. *dst = 0; return ncopied; } MLIB_C_LINKAGE_END // Deliberately using old-style casts for C and C++ compatibility. #if defined(__cplusplus) && defined(__clang__) _Pragma("clang diagnostic pop"); #endif #endif // MONGOCRYPT_STR_PRIVATE_H libmongocrypt-1.19.0/src/mc-mlib/str.test.c000066400000000000000000000107431521103432300205320ustar00rootroot00000000000000#include "./str.h" #define CHECK(Expr) \ ((Expr) ? 0 : ((fprintf(stderr, "%s:%d: Check '%s' failed\n", __FILE__, __LINE__, #Expr), abort()), 0)) #define test_predicate(Bool, Left, Pred, Right) MSTR_ASSERT(Bool, mstrv_lit(Left), Pred, mstrv_lit(Right)) int main(void) { // Test the null-initializers: mstr str = MSTR_NULL; mstr_view null_view = MSTRV_NULL; (void)null_view; str = mstr_copy_cstr("foo"); CHECK(str.raw.len == 3); MSTR_ASSERT_EQ(str.view, mstrv_lit("foo")); CHECK(strncmp(str.raw.data, "foo", 3) == 0); mstr_inplace_append(&str, mstrv_lit("bar")); MSTR_ASSERT_EQ(str.view, mstrv_lit("foobar")); mstr_free(str); str = mstr_copy_cstr("foobar"); mstr_inplace_trunc(&str, 3); MSTR_ASSERT_EQ(str.view, mstrv_lit("foo")); mstr_free(str); int pos = mstr_find(mstrv_lit("foo"), mstrv_lit("bar")); CHECK(pos == -1); pos = mstr_find(mstrv_lit("foo"), mstrv_lit("barbaz")); CHECK(pos == -1); pos = mstr_find(mstrv_lit("foobar"), mstrv_lit("bar")); CHECK(pos == 3); // Simple replacement: str = mstr_copy_cstr("foo bar baz"); mstr str2 = mstr_replace(str.view, mstrv_lit("bar"), mstrv_lit("foo")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("foo foo baz")); mstr_free(str); // Replace multiple instances: mstr_inplace_replace(&str2, mstrv_lit("foo"), mstrv_lit("baz")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("baz baz baz")); // Replace with a string containing the needle: mstr_inplace_replace(&str2, mstrv_lit("baz"), mstrv_lit("foo bar baz")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("foo bar baz foo bar baz foo bar baz")); // Replace with empty string: mstr_inplace_replace(&str2, mstrv_lit("bar "), mstrv_lit("")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("foo baz foo baz foo baz")); // Replacing a string that isn't there: mstr_inplace_replace(&str2, mstrv_lit("quux"), mstrv_lit("nope")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("foo baz foo baz foo baz")); // Replacing an empty string is just a duplication: mstr_inplace_replace(&str2, mstrv_lit(""), mstrv_lit("never")); MSTR_ASSERT_EQ(str2.view, mstrv_lit("foo baz foo baz foo baz")); mstr_free(str2); CHECK(mstrv_view_cstr("foo\000bar").len == 3); CHECK(mstrv_lit("foo\000bar").len == 7); str = mstr_new(0).mstr; MSTR_ITER_SPLIT(part, mstrv_lit("foo bar baz"), mstrv_lit(" ")) { mstr_inplace_append(&str, part); if (mstr_eq(part, mstrv_lit("bar"))) { break; } } MSTR_ASSERT_EQ(str.view, mstrv_lit("foobar")); mstr_free(str); // rfind at the beginning of the string CHECK(mstr_rfind(mstrv_lit("foobar"), mstrv_lit("foo")) == 0); str = mstr_splice(mstrv_lit("foobar"), 1, 2, MSTRV_NULL); MSTR_ASSERT_EQ(str.view, mstrv_lit("fbar")); mstr_free(str); test_predicate(true, "foo", contains, "o"); test_predicate(true, "foo", contains, "oo"); test_predicate(true, "foo", contains, "foo"); test_predicate(true, "foo", contains, "fo"); test_predicate(true, "foo", contains, "f"); test_predicate(true, "foo", contains, ""); test_predicate(false, "foo", contains, "fooo"); test_predicate(false, "foo", contains, "ofo"); test_predicate(false, "foo", contains, "of"); test_predicate(false, "foo", contains, "bar"); test_predicate(true, "foo", starts_with, "f"); test_predicate(true, "foo", starts_with, "fo"); test_predicate(true, "foo", starts_with, "foo"); test_predicate(true, "foo", starts_with, ""); test_predicate(false, "foo", starts_with, "o"); test_predicate(false, "foo", starts_with, "oo"); test_predicate(false, "foo", starts_with, "oof"); test_predicate(false, "foo", starts_with, "bar"); test_predicate(true, "foo", ends_with, "o"); test_predicate(true, "foo", ends_with, "oo"); test_predicate(true, "foo", ends_with, "foo"); test_predicate(true, "foo", ends_with, ""); test_predicate(false, "foo", ends_with, "f"); test_predicate(false, "foo", ends_with, "fo"); test_predicate(false, "foo", ends_with, "oof"); test_predicate(false, "foo", ends_with, "bar"); #ifdef _WIN32 const wchar_t *wide = L"🕴ï¸"; mstr_narrow_result narrow = mstr_win32_narrow(wide); CHECK(narrow.error == 0); MSTR_ASSERT_EQ(narrow.string.view, mstrv_lit("\xc3\xb0\xc5\xb8\xe2\x80\xa2\xc2\xb4\xc3\xaf\xc2\xb8\xc2\x8f")); mstr_free(narrow.string); #endif } libmongocrypt-1.19.0/src/mc-mlib/thread.h000066400000000000000000000043411521103432300202150ustar00rootroot00000000000000#ifndef MLIB_THREAD_H #define MLIB_THREAD_H #include "./user-check.h" #ifdef _MSC_VER #include "./windows-lean.h" #else #include #endif #include /** * @brief A status object for @ref mlib_call_once. */ typedef struct mlib_once_flag { #ifdef _MSC_VER INIT_ONCE _native; #else pthread_once_t _native; #endif } mlib_once_flag; /** * @brief A literal initializer suitable for static initializing an * @ref mlib_once_flag object. Can also be used to dynamically initialize or * "reset" a flag. */ #ifdef _MSC_VER #define MLIB_ONCE_INITIALIZER \ { ._native = INIT_ONCE_STATIC_INIT } #else #define MLIB_ONCE_INITIALIZER \ { ._native = PTHREAD_ONCE_INIT } #endif /** * @brief The type of an mlib_call_once callback function. */ typedef void (*mlib_init_once_fn_t)(void); #if defined(_MSC_VER) /** * An indirection layer for mlib_once on Windows platforms. Do not use directly. */ static inline BOOL WINAPI _mlib_win32_once_callthru(PINIT_ONCE once, PVOID param, PVOID *ctx) { (void)once; (void)ctx; mlib_init_once_fn_t *fn = param; (*fn)(); return TRUE; } #endif /** * @brief Perform thread-safe call-once semantics. * * For each thread that calls with the same given flag, no thread shall return * from this function until the flag is in the "finished" state. If a thread * class this function with a "non-finished" flag object, then that thread MIGHT * execute the passed pointed-to function. Once any thread fully executes the * function for the flag, the flag is marked as "finished". * * @param flag A once-state flag. Should have been initialized by @ref * MLIB_ONCE_INITIALIZER. * @param fn A callback to execute if the flag is not in the "finished" state * @return true on success, false otherwise */ static inline bool mlib_call_once(mlib_once_flag *flag, mlib_init_once_fn_t fn) { #if defined(_MSC_VER) bool okay = InitOnceExecuteOnce(&flag->_native, &_mlib_win32_once_callthru, &fn, NULL); return okay; #else return pthread_once(&flag->_native, fn) == 0; #endif } #endif // MLIB_THREAD_H libmongocrypt-1.19.0/src/mc-mlib/user-check.h000066400000000000000000000004751521103432300210030ustar00rootroot00000000000000#ifndef MLIB_USER #error \ "The file being compiled transitively #include'd a mongo-mlib header, but is not a direct consumer of mlib, which is a private library for MongoDB C driver libraries" #endif libmongocrypt-1.19.0/src/mc-mlib/windows-lean.h000066400000000000000000000010721521103432300213530ustar00rootroot00000000000000/** * This file is simply a wrapper around and ensures that * WIN32_LEAN_AND_MEAN is defined before including it. */ #ifndef MLIB_WINDOWS_LEAN_H #define MLIB_WINDOWS_LEAN_H #ifdef __has_include #if !__has_include() #error " is only available when in available." #endif #endif #pragma push_macro("WIN32_LEAN_AND_MEAN") // Disable macro redefinition warning #undef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #include #pragma pop_macro("WIN32_LEAN_AND_MEAN") #endif // MLIB_WINDOWS_LEAN_H libmongocrypt-1.19.0/src/mc-optional-private.h000066400000000000000000000072351521103432300213250ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_OPTIONAL_PRIVATE_H #define MC_OPTIONAL_PRIVATE_H #include #include #include "./mc-dec128.h" #include "./mc-mlib/int128.h" typedef struct { bool set; bool value; } mc_optional_bool_t; #define OPT_BOOL(val) \ (mc_optional_bool_t) { .set = true, .value = val } #define OPT_BOOL_C(val) \ { .set = true, .value = val } typedef struct { bool set; int32_t value; } mc_optional_int32_t; #define OPT_I32(val) \ (mc_optional_int32_t) { .set = true, .value = val } #define OPT_I32_C(val) \ { .set = true, .value = val } typedef struct { bool set; uint32_t value; } mc_optional_uint32_t; #define OPT_U32(val) \ (mc_optional_uint32_t) { .set = true, .value = val } #define OPT_U32_C(val) \ { .set = true, .value = val } typedef struct { bool set; int64_t value; } mc_optional_int64_t; #define OPT_I64(val) \ (mc_optional_int64_t) { .set = true, .value = val } #define OPT_I64_C(val) \ { .set = true, .value = val } typedef struct { bool set; uint64_t value; } mc_optional_uint64_t; #define OPT_U64(val) \ (mc_optional_uint64_t) { .set = true, .value = val } #define OPT_U64_C(val) \ { .set = true, .value = val } typedef struct { bool set; double value; } mc_optional_double_t; #define OPT_DOUBLE(val) \ (mc_optional_double_t) { .set = true, .value = val } #define OPT_DOUBLE_C(val) \ { .set = true, .value = val } #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() typedef struct { bool set; mc_dec128 value; } mc_optional_dec128_t; #define OPT_MC_DEC128(...) \ (mc_optional_dec128_t) { .set = true, .value = __VA_ARGS__ } #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT #define OPT_NULLOPT \ { .set = false } #endif /* MC_OPTIONAL_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-parse-utils-private.h000066400000000000000000000021141521103432300217370ustar00rootroot00000000000000/* * Copyright 2025-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. */ #ifndef MC_PARSE_UTILS_PRIVATE_H #define MC_PARSE_UTILS_PRIVATE_H #include "mongocrypt-buffer-private.h" /* Validates that the given bson_iter_t points to bindata element, with * the given subtype. If so, it copies the binary data to @out and returns true. * If validation fails, then it returns false and sets an error in @status.*/ bool parse_bindata(bson_subtype_t subtype, bson_iter_t *iter, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); #endif /* MC_PARSE_UTILS_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-parse-utils.c000066400000000000000000000033551521103432300202720ustar00rootroot00000000000000/* * Copyright 2025-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 "mc-parse-utils-private.h" #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" bool parse_bindata(bson_subtype_t subtype, bson_iter_t *iter, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(iter); BSON_ASSERT_PARAM(out); bson_subtype_t parsed_subtype; uint32_t len; const uint8_t *data; const char *field_name = bson_iter_key(iter); if (bson_iter_type(iter) != BSON_TYPE_BINARY) { CLIENT_ERR("Field '%s' expected to be bindata, got: %s", field_name, mc_bson_type_to_string(bson_iter_type(iter))); return false; } bson_iter_binary(iter, &parsed_subtype, &len, &data); if (parsed_subtype != subtype) { CLIENT_ERR("Field '%s' expected to be bindata subtype %d, got: %d", field_name, (int)subtype, (int)parsed_subtype); return false; } if (!_mongocrypt_buffer_copy_from_binary_iter(out, iter)) { CLIENT_ERR("Unable to create mongocrypt buffer for BSON binary field in '%s'", field_name); return false; } return true; } libmongocrypt-1.19.0/src/mc-range-edge-generation-private.h000066400000000000000000000114651521103432300236270ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_RANGE_EDGE_GENERATION_PRIVATE_H #define MC_RANGE_EDGE_GENERATION_PRIVATE_H #include "mc-dec128.h" #include "mc-optional-private.h" #include "mongocrypt-status-private.h" #include #include // size_t #include // mc_edges_t represents a list of edges. typedef struct _mc_edges_t mc_edges_t; // mc_edges_get returns edge at an index. // Returns NULL if `index` is out of range. const char *mc_edges_get(mc_edges_t *edges, size_t index); // mc_edges_len returns the number of represented edges. size_t mc_edges_len(mc_edges_t *edges); // mc_edges_destroys frees `edges`. void mc_edges_destroy(mc_edges_t *edges); // mc_edges_is_leaf returns whether the given edge is the leaf node of the edge set. bool mc_edges_is_leaf(const mc_edges_t *edges, const char *edge); // Return the trimFactor that was used to generate these edges. int32_t mc_edges_get_used_trimFactor(const mc_edges_t *edges); typedef struct { int32_t value; mc_optional_int32_t min; mc_optional_int32_t max; size_t sparsity; mc_optional_int32_t trimFactor; } mc_getEdgesInt32_args_t; // mc_getEdgesInt32 implements the Edge Generation algorithm described in // SERVER-67751 for int32_t. mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status); typedef struct { int64_t value; mc_optional_int64_t min; mc_optional_int64_t max; size_t sparsity; mc_optional_int32_t trimFactor; } mc_getEdgesInt64_args_t; // mc_getEdgesInt64 implements the Edge Generation algorithm described in // SERVER-67751 for int64_t. mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status); typedef struct { double value; size_t sparsity; mc_optional_double_t min; mc_optional_double_t max; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_getEdgesDouble_args_t; // mc_getEdgesDouble implements the Edge Generation algorithm described in // SERVER-67751 for double. mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status); #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() typedef struct { mc_dec128 value; size_t sparsity; mc_optional_dec128_t min, max; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_getEdgesDecimal128_args_t; mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status); #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT BSON_STATIC_ASSERT2(ull_is_u64, sizeof(uint64_t) == sizeof(unsigned long long)); // count_leading_zeros_u64 returns the number of leading 0 bits of `in`. static inline size_t mc_count_leading_zeros_u64(uint64_t in) { #ifdef __has_builtin #if __has_builtin(__builtin_clzll) unsigned long long ull = in; return (size_t)(in ? __builtin_clzll(ull) : 64); #endif #endif uint64_t bit = UINT64_C(1) << 63; size_t count = 0; while ((bit & in) == 0 && bit > 0) { bit >>= 1; ++count; } return count; } // count_leading_zeros_u32 returns the number of leading 0 bits of `in`. static inline size_t mc_count_leading_zeros_u32(uint32_t in) { #ifdef __has_builtin #if __has_builtin(__builtin_clz) // Pointer-cast to ensure we are speaking the right type unsigned int *p = ∈ return (size_t)(in ? __builtin_clz(*p) : 32); #endif #endif uint32_t bit = UINT32_C(1) << 31; int count = 0; while ((bit & in) == 0 && bit > 0) { bit >>= 1; ++count; } return (size_t)count; } static inline size_t mc_count_leading_zeros_u128(mlib_int128 in) { size_t hi = mc_count_leading_zeros_u64(mlib_int128_to_u64(mlib_int128_rshift(in, 64))); size_t lo = mc_count_leading_zeros_u64(mlib_int128_to_u64(in)); return hi + ((hi == 64 ? 1u : 0u) * lo); } typedef struct mc_bitstring { char str[129]; } mc_bitstring; // mc_convert_to_bitstring_u64 returns a 64 character string of 1's and 0's // representing the bits of `in` mc_bitstring mc_convert_to_bitstring_u64(uint64_t in); // mc_convert_to_bitstring_u32 returns a 32 character string of 1's and 0's // representing the bits of `in`. mc_bitstring mc_convert_to_bitstring_u32(uint32_t in); mc_bitstring mc_convert_to_bitstring_u128(mlib_int128 i); #endif /* MC_RANGE_EDGE_GENERATION_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-range-edge-generation.c000066400000000000000000000211231521103432300221420ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-optional-private.h" #include "mc-range-edge-generation-private.h" #include "mc-array-private.h" #include "mc-cmp-private.h" #include "mc-range-encoding-private.h" #include "mongocrypt-private.h" struct _mc_edges_t { size_t sparsity; /* edges is an array of `char*` edge strings. */ mc_array_t edges; char *leaf; int32_t usedTrimFactor; // The `trimFactor` that was used to produce these edges. }; int32_t mc_edges_get_used_trimFactor(const mc_edges_t *edges) { return edges->usedTrimFactor; } static mc_edges_t * mc_edges_new(const char *leaf, size_t sparsity, mc_optional_int32_t opt_trimFactor, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(leaf); if (sparsity < 1) { CLIENT_ERR("sparsity must be 1 or larger"); return NULL; } const size_t leaf_len = strlen(leaf); const int32_t trimFactor = trimFactorDefault(leaf_len, opt_trimFactor); if (trimFactor != 0 && mc_cmp_greater_equal_su(trimFactor, leaf_len)) { // We append a total of leaf_len + 1 (for the root) - trimFactor edges. When this number is equal to 1, we // degenerate into equality, which is not desired, so trimFactor must be less than leaf_len. CLIENT_ERR("trimFactor must be less than the number of bits (%zu) used to represent an element of the domain, " "but got %" PRId32, leaf_len, trimFactor); return NULL; } if (trimFactor < 0) { CLIENT_ERR("trimFactor must be >= 0, but got %" PRId32, trimFactor); return NULL; } mc_edges_t *edges = bson_malloc0(sizeof(mc_edges_t)); edges->usedTrimFactor = trimFactor; edges->sparsity = sparsity; _mc_array_init(&edges->edges, sizeof(char *)); edges->leaf = bson_strdup(leaf); if (trimFactor == 0) { char *root = bson_strdup("root"); _mc_array_append_val(&edges->edges, root); } char *leaf_copy = bson_strdup(leaf); _mc_array_append_val(&edges->edges, leaf_copy); // Start loop at max(trimFactor, 1). The full leaf is unconditionally appended after loop. BSON_ASSERT(mc_in_range_size_t_signed(trimFactor)); size_t trimFactor_sz = (size_t)trimFactor; size_t startLevel = trimFactor > 0 ? trimFactor_sz : 1; for (size_t i = startLevel; i < leaf_len; i++) { if (i % sparsity == 0) { char *edge = bson_malloc(i + 1); bson_strncpy(edge, leaf, i + 1); _mc_array_append_val(&edges->edges, edge); } } return edges; } const char *mc_edges_get(mc_edges_t *edges, size_t index) { BSON_ASSERT_PARAM(edges); if (edges->edges.len == 0 || index > edges->edges.len - 1u) { return NULL; } return _mc_array_index(&edges->edges, char *, index); } size_t mc_edges_len(mc_edges_t *edges) { BSON_ASSERT_PARAM(edges); return edges->edges.len; } void mc_edges_destroy(mc_edges_t *edges) { if (NULL == edges) { return; } for (size_t i = 0; i < edges->edges.len; i++) { char *val = _mc_array_index(&edges->edges, char *, i); bson_free(val); } _mc_array_destroy(&edges->edges); bson_free(edges->leaf); bson_free(edges); } bool mc_edges_is_leaf(const mc_edges_t *edges, const char *edge) { BSON_ASSERT_PARAM(edges); BSON_ASSERT_PARAM(edge); return strcmp(edge, edges->leaf) == 0; } mc_bitstring mc_convert_to_bitstring_u64(uint64_t in) { mc_bitstring ret = {{0}}; char *out = ret.str; uint64_t bit = UINT64_C(1) << 63; int loops = 0; // used to determine a bit shift while (bit > 0) { *out++ = (char)('0' + ((bit & in) >> (63 - loops))); bit >>= 1; loops++; } return ret; } mc_bitstring mc_convert_to_bitstring_u32(uint32_t in) { mc_bitstring ret = {{0}}; char *out = ret.str; uint32_t bit = UINT32_C(1) << 31; while (bit > 0) { if (bit & in) { *out++ = '1'; } else { *out++ = '0'; } bit >>= 1; } return ret; } mc_bitstring mc_convert_to_bitstring_u128(mlib_int128 i) { const uint64_t lo = mlib_int128_to_u64(i); const uint64_t hi = mlib_int128_to_u64(mlib_int128_rshift(i, 64)); mc_bitstring his = mc_convert_to_bitstring_u64(hi); mc_bitstring los = mc_convert_to_bitstring_u64(lo); mc_bitstring ret = {{0}}; const size_t off = mlib_strnmcopy(ret.str, sizeof ret.str, his.str, sizeof his.str); mlib_strnmcopy(ret.str + off, (sizeof ret.str) - off, los.str, sizeof los.str); return ret; } mc_edges_t *mc_getEdgesInt32(mc_getEdgesInt32_args_t args, mongocrypt_status_t *status) { mc_OSTType_Int32 got; if (!mc_getTypeInfo32((mc_getTypeInfo32_args_t){.value = args.value, .min = args.min, .max = args.max}, &got, status)) { return NULL; } // `max` is the domain of values. `max` is used to determine the maximum bit // length. `min` is expected to be zero. The `min` and `max` terms are kept // for consistency with the server implementation. BSON_ASSERT(got.min == 0); mc_bitstring valueBin = mc_convert_to_bitstring_u32(got.value); size_t offset = mc_count_leading_zeros_u32(got.max); const char *leaf = valueBin.str + offset; mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status); return ret; } mc_edges_t *mc_getEdgesInt64(mc_getEdgesInt64_args_t args, mongocrypt_status_t *status) { mc_OSTType_Int64 got; if (!mc_getTypeInfo64((mc_getTypeInfo64_args_t){.value = args.value, .min = args.min, .max = args.max}, &got, status)) { return NULL; } // `max` is the domain of values. `max` is used to determine the maximum bit // length. `min` is expected to be zero. The `min` and `max` terms are kept // for consistency with the server implementation. BSON_ASSERT(got.min == 0); mc_bitstring valueBin = mc_convert_to_bitstring_u64(got.value); size_t offset = mc_count_leading_zeros_u64(got.max); const char *leaf = valueBin.str + offset; mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status); return ret; } mc_edges_t *mc_getEdgesDouble(mc_getEdgesDouble_args_t args, mongocrypt_status_t *status) { mc_OSTType_Double got; if (!mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = args.value, .min = args.min, .max = args.max, .precision = args.precision}, &got, status)) { return NULL; } // `max` is the domain of values. `max` is used to determine the maximum bit // length. `min` is expected to be zero. The `min` and `max` terms are kept // for consistency with the server implementation. BSON_ASSERT(got.min == 0); mc_bitstring valueBin = mc_convert_to_bitstring_u64(got.value); size_t offset = mc_count_leading_zeros_u64(got.max); const char *leaf = valueBin.str + offset; mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status); return ret; } #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() mc_edges_t *mc_getEdgesDecimal128(mc_getEdgesDecimal128_args_t args, mongocrypt_status_t *status) { mc_OSTType_Decimal128 got; if (!mc_getTypeInfoDecimal128( (mc_getTypeInfoDecimal128_args_t){ .value = args.value, .min = args.min, .max = args.max, .precision = args.precision, }, &got, status)) { return NULL; } BSON_ASSERT(mlib_int128_eq(got.min, MLIB_INT128(0))); mc_bitstring bits = mc_convert_to_bitstring_u128(got.value); size_t offset = mc_count_leading_zeros_u128(got.max); const char *leaf = bits.str + offset; mc_edges_t *ret = mc_edges_new(leaf, args.sparsity, args.trimFactor, status); return ret; } #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT libmongocrypt-1.19.0/src/mc-range-encoding-private.h000066400000000000000000000113031521103432300223470ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_RANGE_ENCODING_PRIVATE_H #define MC_RANGE_ENCODING_PRIVATE_H #include "mc-dec128.h" #include "mc-optional-private.h" #include "mongocrypt-status-private.h" #include #include /* mc-range-encoding-private.h has functions to encode numeric types for * Queryable Encryption Range queries. It is a translation from server code: * https://github.com/mongodb/mongo/blob/1364f5c5004ac5503837ac5b315c189625f97269/src/mongo/crypto/fle_crypto.h#L1194-L1196 * "OST" is an abbreviation taken from server code. It stands for "Outsourced * STate". */ /* mc_OSTType_Int32 describes the encoding of a BSON int32. */ typedef struct { uint32_t value; uint32_t min; uint32_t max; } mc_OSTType_Int32; typedef struct { int32_t value; mc_optional_int32_t min; mc_optional_int32_t max; } mc_getTypeInfo32_args_t; /* mc_getTypeInfo32 encodes the int32_t `args.value` into an OSTType_Int32 * `out`. `args.min` and `args.max` may be unset. Returns false and sets * `status` on error. */ bool mc_getTypeInfo32(mc_getTypeInfo32_args_t args, mc_OSTType_Int32 *out, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; /* mc_OSTType_Int64 describes the encoding of a BSON int64. */ typedef struct { uint64_t value; uint64_t min; uint64_t max; } mc_OSTType_Int64; typedef struct { int64_t value; mc_optional_int64_t min; mc_optional_int64_t max; } mc_getTypeInfo64_args_t; /* mc_getTypeInfo64 encodes the int64_t `args.value` into an OSTType_Int64 * `out`. `args.min` and `args.max` may be unset. Returns false and sets * `status` on error. */ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; /* mc_OSTType_Double describes the encoding of a BSON double. */ typedef struct { uint64_t value; uint64_t min; uint64_t max; } mc_OSTType_Double; typedef struct { double value; mc_optional_double_t min; mc_optional_double_t max; mc_optional_int32_t precision; } mc_getTypeInfoDouble_args_t; // `mc_canUsePrecisionModeDouble` returns true if the domain can be represented in fewer than 64 bits. bool mc_canUsePrecisionModeDouble(double min, double max, int32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); /* mc_getTypeInfoDouble encodes the double `args.value` into an OSTType_Double * `out`. Returns false and sets `status` on error. */ bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *out, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() /** * @brief OST-encoding of a Decimal128 */ typedef struct { mlib_int128 value, min, max; } mc_OSTType_Decimal128; typedef struct { mc_dec128 value; mc_optional_dec128_t min, max; mc_optional_int32_t precision; } mc_getTypeInfoDecimal128_args_t; // `mc_canUsePrecisionModeDecimal` returns true if the domain can be represented in fewer than 128 bits. bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, int32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status); /** * @brief Obtain the OST encoding of a finite Decimal128 value. * * @param out Output for the result * @param status Output for status on error * @retval true On success * @retval false Otherwise */ bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args, mc_OSTType_Decimal128 *out, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT extern const int64_t mc_FLERangeSparsityDefault; int32_t trimFactorDefault(size_t maxlen, mc_optional_int32_t trimFactor); #endif /* MC_RANGE_ENCODING_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-range-encoding.c000066400000000000000000000763511521103432300207100ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-cmp-private.h" #include "mc-range-encoding-private.h" #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" // mc_isinf #include // pow /* mc-range-encoding.c assumes integers are encoded with two's complement for * correctness. */ #if (-1 & 3) != 3 #error Error: Twos complement integer representation is required. #endif /** * Encode a signed 32-bit integer as an unsigned 32-bit integer by adding 2^31. * Some documentation references this as making the value "unbiased". */ static uint32_t encodeInt32(int32_t v) { // Shift the int32_t range [-2^31, 2^31 - 1] to the uint32_t range [0, 2^32]. // new_zero is the mapped 0 value. uint32_t new_zero = (UINT32_C(1) << 31); if (v < 0) { // Signed integers have a value that there is no positive equivalent and // must be handled specially if (v == INT32_MIN) { return 0; } int32_t v_pos = v * -1; uint32_t v_u32 = (uint32_t)v_pos; return new_zero - v_u32; } uint32_t v_u32 = (uint32_t)v; return new_zero + v_u32; } bool mc_getTypeInfo32(mc_getTypeInfo32_args_t args, mc_OSTType_Int32 *out, mongocrypt_status_t *status) { if (args.min.set != args.max.set) { CLIENT_ERR("Must specify both a lower and upper bound or no bounds."); return false; } if (!args.min.set) { uint32_t v_u32 = encodeInt32(args.value); *out = (mc_OSTType_Int32){v_u32, 0, UINT32_MAX}; return true; } if (args.min.value >= args.max.value) { CLIENT_ERR("The minimum value must be less than the maximum value, got " "min: %" PRId32 ", max: %" PRId32, args.min.value, args.max.value); return false; } if (args.value > args.max.value || args.value < args.min.value) { CLIENT_ERR("Value must be greater than or equal to the minimum value " "and less than or equal to the maximum value, got min: %" PRId32 ", max: %" PRId32 ", value: %" PRId32, args.min.value, args.max.value, args.value); return false; } // Convert to unbiased uint32. Then subtract the min value. uint32_t v_u32 = encodeInt32(args.value); uint32_t min_u32 = encodeInt32(args.min.value); uint32_t max_u32 = encodeInt32(args.max.value); v_u32 -= min_u32; max_u32 -= min_u32; *out = (mc_OSTType_Int32){v_u32, 0, max_u32}; return true; } /** * Encode a signed 64-bit integer as an unsigned 64-bit integer by adding 2^63. * Some documentation references this as making the value "unbiased". */ static uint64_t encodeInt64(int64_t v) { // Shift the int64_t range [-2^63, 2^63 - 1] to the uint64_t range [0, 2^64]. // new_zero is the mapped 0 value. uint64_t new_zero = (UINT64_C(1) << 63); if (v < 0) { // Signed integers have a value that there is no positive equivalent and // must be handled specially if (v == INT64_MIN) { return 0; } int64_t v_pos = v * -1; uint64_t v_u64 = (uint64_t)v_pos; return new_zero - v_u64; } uint64_t v_u64 = (uint64_t)v; return new_zero + v_u64; } bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongocrypt_status_t *status) { if (args.min.set != args.max.set) { CLIENT_ERR("Must specify both a lower and upper bound or no bounds."); return false; } if (!args.min.set) { uint64_t v_u64 = encodeInt64(args.value); *out = (mc_OSTType_Int64){v_u64, 0, UINT64_MAX}; return true; } if (args.min.value >= args.max.value) { CLIENT_ERR("The minimum value must be less than the maximum value, got " "min: %" PRId64 ", max: %" PRId64, args.min.value, args.max.value); return false; } if (args.value > args.max.value || args.value < args.min.value) { CLIENT_ERR("Value must be greater than or equal to the minimum value " "and less than or equal to the maximum value, got " "min: %" PRId64 ", max: %" PRId64 ", value: %" PRId64, args.min.value, args.max.value, args.value); return false; } // Convert to unbiased uint64. Then subtract the min value. uint64_t v_u64 = encodeInt64(args.value); uint64_t min_u64 = encodeInt64(args.min.value); uint64_t max_u64 = encodeInt64(args.max.value); v_u64 -= min_u64; max_u64 -= min_u64; *out = (mc_OSTType_Int64){v_u64, 0, max_u64}; return true; } #define exp10Double(x) pow(10, x) #define SCALED_DOUBLE_BOUNDS 9007199254740992.0 // 2^53 static uint64_t subtract_int64_t(int64_t max, int64_t min) { BSON_ASSERT(max > min); // If the values have the same sign, then simple subtraction // will work because we know max > min. if ((max > 0 && min > 0) || (max < 0 && min < 0)) { return (uint64_t)(max - min); } // If they are opposite signs, then we can just invert // min to be positive and return the sum. uint64_t u_return = (uint64_t)max; u_return += (uint64_t)(~min + 1); return u_return; } static bool ceil_log2_double(uint64_t i, uint32_t *maxBitsOut, mongocrypt_status_t *status) { if (i == 0) { CLIENT_ERR("Invalid input to ceil_log2_double function. Input cannot be 0."); return false; } uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u64(i); uint32_t bits; if ((i & (i - 1)) == 0) { bits = 64 - clz - 1; } else { bits = 64 - clz; } *maxBitsOut = bits; return true; } bool mc_canUsePrecisionModeDouble(double min, double max, int32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); BSON_ASSERT(precision >= 0); if (min >= max) { CLIENT_ERR("Invalid bounds for double range precision, min must be less than max. min: %g, max: %g", min, max); return false; } const double scaled_prc = exp10Double(precision); const double scaled_max = max * scaled_prc; const double scaled_min = min * scaled_prc; if (scaled_max != trunc(scaled_max)) { CLIENT_ERR("Invalid upper bound for double precision. Fractional digits must be less than the specified " "precision value. max: %g", max); return false; } if (scaled_min != trunc(scaled_min)) { CLIENT_ERR("Invalid lower bound for double precision. Fractional digits must be less than the specified " "precision value. min: %g", min); return false; } if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { CLIENT_ERR( "Invalid upper bound for double precision. Absolute scaled value of max must be less than %g. max: %g", SCALED_DOUBLE_BOUNDS, max); return false; } if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { CLIENT_ERR( "Invalid lower bound for double precision. Absolute scaled value of min must be less than %g. min: %g", SCALED_DOUBLE_BOUNDS, min); return false; } const double t_1 = scaled_max - scaled_min; const double t_4 = (double)UINT64_MAX - t_1; const double t_5 = floor(log10(t_4)) - 1; if ((double)precision > t_5) { CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision); return false; } const int64_t i_1 = (int64_t)(scaled_max); const int64_t i_2 = (int64_t)(scaled_min); const uint64_t range = subtract_int64_t(i_1, i_2); if (((uint64_t)scaled_prc) > UINT64_MAX - range) { CLIENT_ERR("Invalid value for min, max, and precision. The calculated domain size is too large. min: %g, max: " "%g, precision: %" PRId32, min, max, precision); return false; } const uint64_t i_3 = range + (uint64_t)(scaled_prc); if (!ceil_log2_double(i_3, maxBitsOut, status)) { return false; } // Integers between -2^53 and 2^53 can be exactly represented. Outside this range, doubles lose precision by a // multiple of 2^(n-52) where n = #bits. We disallow users from using precision mode when the bounds exceed 2^53 to // prevent the users from being surprised by how floating point math works. if (*maxBitsOut >= 53) { return false; } return true; } bool mc_getTypeInfoDouble(mc_getTypeInfoDouble_args_t args, mc_OSTType_Double *out, mongocrypt_status_t *status) { if (args.min.set != args.max.set || args.min.set != args.precision.set) { CLIENT_ERR("min, max, and precision must all be set or must all be unset"); return false; } if (mc_isinf(args.value) || mc_isnan(args.value)) { CLIENT_ERR("Infinity and NaN double values are not supported."); return false; } if (args.min.set) { if (args.min.value >= args.max.value) { CLIENT_ERR("The minimum value must be less than the maximum value, got " "min: %g, max: %g", args.min.value, args.max.value); return false; } if (args.value > args.max.value || args.value < args.min.value) { CLIENT_ERR("Value must be greater than or equal to the minimum value " "and less than or equal to the maximum value, got " "min: %g, max: %g, value: %g", args.min.value, args.max.value, args.value); return false; } } if (args.precision.set) { if (args.precision.value < 0) { CLIENT_ERR("Precision must be non-negative, but got %" PRId32, args.precision.value); return false; } double scaled = exp10Double(args.precision.value); if (!mc_isfinite(scaled)) { CLIENT_ERR("Precision is too large and cannot be used to calculate the scaled range bounds"); return false; } } const bool is_neg = args.value < 0.0; // Map negative 0 to zero so sign bit is 0. if (args.value == 0.0) { args.value = 0.0; } // When we use precision mode, we try to represent as a double value that // fits in [-2^63, 2^63] (i.e. is a valid int64) // // This check determines if we can represent the precision truncated value as // a 64-bit integer I.e. Is ((ub - lb) * 10^precision) < 64 bits. // bool use_precision_mode = false; uint32_t bits_range; if (args.precision.set) { use_precision_mode = mc_canUsePrecisionModeDouble(args.min.value, args.max.value, args.precision.value, &bits_range, status); if (!use_precision_mode) { if (!mongocrypt_status_ok(status)) { return false; } CLIENT_ERR("The domain of double values specified by the min, max, and precision cannot be represented in " "fewer than 53 bits. min: %g, max: %g, precision: %" PRId32, args.min.value, args.max.value, args.precision.value); return false; } // If we are not in range_v2, then we don't care about the error returned // from canUsePrecisionMode so we can reset the status. _mongocrypt_status_reset(status); } if (use_precision_mode) { // Take a number of xxxx.ppppp and truncate it xxxx.ppp if precision = 3. // We do not change the digits before the decimal place. int64_t v_prime = (int64_t)(trunc(args.value * exp10Double(args.precision.value))); int64_t scaled_min = (int64_t)(args.min.value * exp10Double(args.precision.value)); int64_t v_prime2 = v_prime - scaled_min; BSON_ASSERT(v_prime2 < INT64_MAX && v_prime2 >= 0); uint64_t ret = (uint64_t)v_prime2; // Adjust maximum value to be the max bit range. This will be used by // getEdges/minCover to trim bits. uint64_t max_value = (UINT64_C(1) << bits_range) - 1; BSON_ASSERT(ret <= max_value); *out = (mc_OSTType_Double){ret, 0, max_value}; return true; } // Translate double to uint64 by modifying the bit representation and copying // into a uint64. Double is assumed to be a IEEE 754 Binary 64. // It is bit-encoded as sign, exponent, and fraction: // s eeeeeeee ffffffffffffffffffffffffffffffffffffffffffffffffffff // When we translate the double into "bits", the sign bit means that the // negative numbers get mapped into the higher 63 bits of a 64-bit integer. // We want them to map into the lower 64-bits so we invert the sign bit. args.value *= -1.0; // On Endianness, we support two sets of architectures // 1. Little Endian (ppc64le, x64, aarch64) - in these architectures, int64 // and double are both 64-bits and both arranged in little endian byte order. // 2. Big Endian (s390x) - in these architectures, int64 and double are both // 64-bits and both arranged in big endian byte order. // // Therefore, since the order of bytes on each platform is consistent with // itself, the conversion below converts a double into correct 64-bit integer // that produces the same behavior across plaforms. uint64_t uv; memcpy(&uv, &args.value, sizeof(uint64_t)); if (is_neg) { uint64_t new_zero = UINT64_C(1) << 63; BSON_ASSERT(uv <= new_zero); uv = new_zero - uv; } *out = (mc_OSTType_Double){.min = 0, .max = UINT64_MAX, .value = uv}; return true; } #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() /** * @brief There is no shipped algorithm for creating a full 128-bit integer from * a Decimal128, but it's easy enough to write one of our own. * * @param dec * @return mlib_int128 */ static mlib_int128 dec128_to_uint128(mc_dec128 dec) { // Only normal numbers BSON_ASSERT(mc_dec128_is_finite(dec)); BSON_ASSERT(!mc_dec128_is_nan(dec)); // We don't support negative numbers BSON_ASSERT(!mc_dec128_is_negative(dec)); // There is no fractional part: BSON_ASSERT(mc_dec128_is_zero(mc_dec128_modf(dec).frac)); mlib_int128 ret = mc_dec128_coeff(dec); // Scale the resulting number by a power of ten matching the exponent of the // Decimal128: int32_t exp = ((int32_t)mc_dec128_get_biased_exp(dec)) - MC_DEC128_EXPONENT_BIAS; // We will scale up/down based on whether it is negative: BSON_ASSERT(abs(exp) <= UINT8_MAX); mlib_int128 e1 = mlib_int128_pow10((uint8_t)abs(exp)); if (exp < 0) { ret = mlib_int128_div(ret, e1); } else { ret = mlib_int128_mul(ret, e1); } return ret; } // (2^127 - 1) = the maximum signed 128-bit integer value, as a decimal128 #define INT_128_MAX_AS_DECIMAL mc_dec128_from_string("170141183460469231731687303715884105727") // (2^128 - 1) = the max unsigned 128-bit integer value, as a decimal128 #define UINT_128_MAX_AS_DECIMAL mc_dec128_from_string("340282366920938463463374607431768211455") static mlib_int128 dec128_to_int128(mc_dec128 dec) { BSON_ASSERT(mc_dec128_less(dec, INT_128_MAX_AS_DECIMAL)); bool negative = false; if (mc_dec128_is_negative(dec)) { negative = true; dec = mc_dec128_mul(MC_DEC128(-1), dec); } mlib_int128 ret_val = dec128_to_uint128(dec); if (negative) { ret_val = mlib_int128_mul(MLIB_INT128(-1), ret_val); } return ret_val; } bool ceil_log2_int128(mlib_int128 i, uint32_t *maxBitsOut, mongocrypt_status_t *status) { if (mlib_int128_eq(i, MLIB_INT128(0))) { CLIENT_ERR("Invalid input to ceil_log2_int128 function. Input cannot be 0."); return false; } uint32_t clz = (uint32_t)_mlibCountLeadingZeros_u128(i); uint32_t bits; // if i & (i - 1) == 0 if (mlib_int128_eq((mlib_int128_bitand(i, (mlib_int128_sub(i, MLIB_INT128(1))))), MLIB_INT128(0))) { bits = 128 - clz - 1; } else { bits = 128 - clz; } *maxBitsOut = bits; return true; } bool mc_canUsePrecisionModeDecimal(mc_dec128 min, mc_dec128 max, int32_t precision, uint32_t *maxBitsOut, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(maxBitsOut); BSON_ASSERT(precision >= 0); if (!mc_dec128_is_finite(max)) { CLIENT_ERR("Invalid upper bound for Decimal128 precision. Max is infinite."); return false; } if (!mc_dec128_is_finite(min)) { CLIENT_ERR("Invalid lower bound for Decimal128 precision. Min is infinite."); return false; } if (mc_dec128_greater_equal(min, max)) { CLIENT_ERR("Invalid upper and lower bounds for Decimal128 precision. Min must be strictly less than max. min: " "%s, max: %s", mc_dec128_to_string(min).str, mc_dec128_to_string(max).str); return false; } mc_dec128 scaled_max = mc_dec128_scale(max, precision); mc_dec128 scaled_min = mc_dec128_scale(min, precision); mc_dec128 scaled_max_trunc = mc_dec128_round_integral_ex(scaled_max, MC_DEC128_ROUND_TOWARD_ZERO, NULL); mc_dec128 scaled_min_trunc = mc_dec128_round_integral_ex(scaled_min, MC_DEC128_ROUND_TOWARD_ZERO, NULL); if (mc_dec128_not_equal(scaled_max, scaled_max_trunc)) { CLIENT_ERR("Invalid upper bound for Decimal128 precision. Fractional digits must be less than " "the specified precision value. max: %s, precision: %" PRId32, mc_dec128_to_string(max).str, precision); return false; } if (mc_dec128_not_equal(scaled_min, scaled_min_trunc)) { CLIENT_ERR("Invalid lower bound for Decimal128 precision. Fractional digits must be less than " "the specified precision value. min: %s, precision: %" PRId32, mc_dec128_to_string(min).str, precision); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_max), INT_128_MAX_AS_DECIMAL)) { CLIENT_ERR("Invalid upper bound for Decimal128 precision. Absolute scaled value must be less than " "or equal to %s. max: %s", mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str, mc_dec128_to_string(max).str); return false; } if (mc_dec128_greater(mc_dec128_abs(scaled_min), INT_128_MAX_AS_DECIMAL)) { CLIENT_ERR("Invalid lower bound for Decimal128 precision. Absolute scaled value must be less than " "or equal to %s. min: %s", mc_dec128_to_string(INT_128_MAX_AS_DECIMAL).str, mc_dec128_to_string(min).str); return false; } mc_dec128 t_1 = mc_dec128_sub(scaled_max, scaled_min); mc_dec128 t_4 = mc_dec128_sub(UINT_128_MAX_AS_DECIMAL, t_1); // t_5 = floor(log10(t_4)) - 1; mc_dec128 t_5 = mc_dec128_sub(mc_dec128_round_integral_ex(mc_dec128_log10(t_4), MC_DEC128_ROUND_TOWARD_ZERO, NULL), MC_DEC128(1)); // We convert precision to a double so we can avoid warning C4146 on Windows. mc_dec128 prc_dec = mc_dec128_from_double((double)precision); if (mc_dec128_less(t_5, prc_dec)) { CLIENT_ERR("Invalid value for precision. precision: %" PRId32, precision); return false; } mlib_int128 i_1 = dec128_to_int128(scaled_max); mlib_int128 i_2 = dec128_to_int128(scaled_min); // Because we have guaranteed earlier that max is greater than min, we can // subtract these values and guarantee that taking their unsigned // representation will yield the actual range result. mlib_int128 range128 = mlib_int128_sub(i_1, i_2); if (precision > UINT8_MAX) { CLIENT_ERR("Invalid value for precision. Must be less than 255. precision: %" PRId32, precision); return false; } mlib_int128 i_3 = mlib_int128_add(range128, mlib_int128_pow10((uint8_t)precision)); if (!ceil_log2_int128(i_3, maxBitsOut, status)) { return false; } if (*maxBitsOut >= 128) { return false; } return true; } bool mc_getTypeInfoDecimal128(mc_getTypeInfoDecimal128_args_t args, mc_OSTType_Decimal128 *out, mongocrypt_status_t *status) { /// Basic param checks if (args.min.set != args.max.set || args.min.set != args.precision.set) { CLIENT_ERR("min, max, and precision must all be set or must all be unset"); return false; } if (args.precision.set) { if (args.precision.value < 0) { CLIENT_ERR("Precision must be non-negative, but got %" PRId32, args.precision.value); return false; } mc_dec128 scaled = mc_dec128_scale(MC_DEC128(1), args.precision.value); if (!mc_dec128_is_finite(scaled)) { CLIENT_ERR("Precision is too large and cannot be used to calculate the scaled range bounds"); return false; } } // We only accept normal numbers if (mc_dec128_is_inf(args.value) || mc_dec128_is_nan(args.value)) { CLIENT_ERR("Infinity and Nan Decimal128 values are not supported."); return false; } // Check boundary if a range is set if (args.min.set) { // [min,max] must be valid if (mc_dec128_greater_equal(args.min.value, args.max.value)) { CLIENT_ERR("The minimum value must be less than the maximum value, got " "min: %s, max: %s", mc_dec128_to_string(args.min.value).str, mc_dec128_to_string(args.max.value).str); return false; } // Value must be within [min,max) if (mc_dec128_greater(args.value, args.max.value) || mc_dec128_less(args.value, args.min.value)) { CLIENT_ERR("Value must be greater than or equal to the minimum value " "and less than or equal to the maximum value, got " "min: %s, max: %s, value: %s", mc_dec128_to_string(args.min.value).str, mc_dec128_to_string(args.max.value).str, mc_dec128_to_string(args.value).str); return false; } } // Should we use precision mode? // // When we use precision mode, we try to represent as a decimal128 value that // fits in [-2^127, 2^127] (i.e. is a valid int128) // // This check determines if we can represent any precision-truncated value as // a 128-bit integer I.e. Is ((ub - lb) * 10^precision) < 128 bits. // // It is important that we determine whether a range and its precision would // fit, regardless of the value to be encoded, because the encoding for // precision-truncated-decimal128 is incompatible with the encoding of the // full range. bool use_precision_mode = false; // The number of bits required to hold the result (used for precision mode) uint32_t bits_range = 0; if (args.precision.set) { use_precision_mode = mc_canUsePrecisionModeDecimal(args.min.value, args.max.value, args.precision.value, &bits_range, status); if (!use_precision_mode) { if (!mongocrypt_status_ok(status)) { return false; } CLIENT_ERR("The domain of decimal values specified by the min, max, and precision cannot be represented in " "fewer than 128 bits. min: %s, max: %s, precision: %" PRIu32, mc_dec128_to_string(args.min.value).str, mc_dec128_to_string(args.max.value).str, args.precision.value); return false; } // If we are not in range_v2, then we don't care about the error returned // from canUsePrecisionMode so we can reset the status. _mongocrypt_status_reset(status); } // Constant zero const mlib_int128 i128_zero = MLIB_INT128(0); // Constant 1 const mlib_int128 i128_one = MLIB_INT128(1); // Constant 10 const mlib_int128 i128_ten = MLIB_INT128(10); // Constant: 2^127 const mlib_int128 i128_2pow127 = mlib_int128_lshift(i128_one, 127); // ↑ Coincidentally has the same bit pattern as INT128_SMIN, but we're // treating it as an unsigned number here, so don't get confused! if (use_precision_mode) { BSON_ASSERT(args.precision.set); // Example value: 31.4159 // Example Precision = 2 // Shift the number up // Returns: 3141.9 mc_dec128 valScaled = mc_dec128_scale(args.value, args.precision.value); // Round the number down // Returns 3141.0 mc_dec128 valTrunc = mc_dec128_round_integral_ex(valScaled, MC_DEC128_ROUND_TOWARD_ZERO, NULL); // Shift the number down // Returns: 31.41 mc_dec128 v_prime = mc_dec128_scale(valTrunc, -(int32_t)args.precision.value); // Adjust the number by the lower bound // Make it an integer by scaling the number // // Returns 3141.0 mc_dec128 v_prime2 = mc_dec128_scale(mc_dec128_sub(v_prime, args.min.value), args.precision.value); // Round the number down again. min may have a fractional value with more // decimal places than the precision (e.g. .001). Subtracting min may have // resulted in v_prime2 with a non-zero fraction. v_prime2 is expected to // have no fractional value when converting to int128. v_prime2 = mc_dec128_round_integral_ex(v_prime2, MC_DEC128_ROUND_TOWARD_ZERO, NULL); BSON_ASSERT(mc_dec128_less(mc_dec128_log2(v_prime2), MC_DEC128(128))); BSON_ASSERT(bits_range < 128); // Resulting OST maximum mlib_int128 ost_max = mlib_int128_sub(mlib_int128_pow2((uint8_t)bits_range), i128_one); // Now we need to get the Decimal128 out as a 128-bit integer // But Decimal128 does not support conversion to Int128. // // If we think the Decimal128 fits in the range, based on the maximum // value, we try to convert to int64 directly. if (bits_range < 64) { // Try conversion to int64, it may fail but since it is easy we try // this first. mc_dec128_flagset flags = {0}; int64_t as64 = mc_dec128_to_int64_ex(v_prime2, &flags); if (flags.bits == 0) { // No error. It fits *out = (mc_OSTType_Decimal128){ .value = MLIB_INT128_CAST(as64), .min = i128_zero, .max = ost_max, }; return true; } else { // Conversion failure to 64-bit. Possible overflow, imprecision, // etc. Fallback to slower dec128_to_int128 } } mlib_int128 u_ret = dec128_to_int128(v_prime2); *out = (mc_OSTType_Decimal128){ .value = u_ret, .min = i128_zero, .max = ost_max, }; return true; } // The coefficient of the number, without exponent/sign const mlib_int128 coeff = mc_dec128_coeff(args.value); if (mlib_int128_eq(coeff, i128_zero)) { // If the coefficient is zero, the result is encoded as the midpoint // between zero and 2^128-1 *out = (mc_OSTType_Decimal128){ .value = i128_2pow127, .min = i128_zero, .max = MLIB_INT128_UMAX, }; return true; } // Coefficient is an unsigned value. We'll later scale our answer based on // the sign of the actual Decimal128 const bool isNegative = mc_dec128_is_negative(args.value); // cMax = 10^34 - 1 (The largest integer representable in Decimal128) const mlib_int128 cMax = mlib_int128_sub(mlib_int128_pow10(34), MLIB_INT128_CAST(1)); const mlib_int128 cMax_div_ten = mlib_int128_div(cMax, i128_ten); // The biased exponent from the decimal number. The paper refers to the // expression (e - e_min), which is the value of the biased exponent. const uint32_t exp_biased = mc_dec128_get_biased_exp(args.value); // Ï (rho) is the greatest integer such that: coeff×10^Ï <= cMax unsigned rho = 0; // Keep track of the subexpression coeff×10^Ï rather than recalculating it // time. // Initially: (Ï = 0) -> (10^Ï = 1) -> (coeff×10^Ï = coeff×1 = coeff): mlib_int128 coeff_scaled = coeff; // Calculate Ï: This could be done using a log10 with a division, but that // is far more work than just a few multiplications. // While: coeff×ten^Ï < cMax/10: while (mlib_int128_ucmp(coeff_scaled, cMax_div_ten) < 0) { // Increase rho until we pass cMax/10 rho++; // Scale our computed subexpression rather than fully recomputing it coeff_scaled = mlib_int128_mul(coeff_scaled, i128_ten); } // No multiplication by 10 should ever send us from N < cMax/10 to N > cMax BSON_ASSERT(mlib_int128_ucmp(coeff_scaled, cMax) <= 0); mlib_int128 result; if (rho <= exp_biased) { // Ï is less-than/equal to the exponent with bias. // Diff between the biased exponent and Ï. // Value in paper is spelled "e - e_min - Ï" const uint32_t exp_diff = exp_biased - (uint32_t)rho; // cMax * (exp_diff) const mlib_int128 cmax_scaled = mlib_int128_mul(cMax, MLIB_INT128_CAST(exp_diff)); // coeff * 10^rho * cMax * (exp_biased - rho) result = mlib_int128_add(coeff_scaled, cmax_scaled); } else { const mlib_int128 biased_scale = mlib_int128_pow10((uint8_t)exp_biased); result = mlib_int128_mul(biased_scale, coeff); } // Always add 2^127: result = mlib_int128_add(result, i128_2pow127); if (isNegative) { // We calculated the value of the positive coefficient, but the decimal is // negative. That's okay: Just flip the sign of the encoded result: result = mlib_int128_negate(result); } *out = (mc_OSTType_Decimal128){ .value = result, .min = i128_zero, .max = MLIB_INT128_UMAX, }; return true; } #endif // defined MONGOCRYPT_HAVE_DECIMAL128_SUPPORT const int64_t mc_FLERangeSparsityDefault = 2; static const int32_t mc_FLERangeTrimFactorDefault = 6; int32_t trimFactorDefault(size_t maxlen, mc_optional_int32_t trimFactor) { if (trimFactor.set) { return trimFactor.value; } if (mc_cmp_greater_su(mc_FLERangeTrimFactorDefault, maxlen - 1)) { return (int32_t)(maxlen - 1); } else { return mc_FLERangeTrimFactorDefault; } } libmongocrypt-1.19.0/src/mc-range-mincover-generator.template.h000066400000000000000000000232411521103432300245350ustar00rootroot00000000000000/* * Copyright 2022-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. */ // mc-range-mincover-generator.template.h is meant to be included in another // source file. #if !(defined(UINT_T) && defined(UINT_C) && defined(UINT_FMT_S) && defined(DECORATE_NAME)) #ifdef __INTELLISENSE__ #define UINT_T uint32_t #define UINT_C UINT32_C #define UINT_FMT_S PRIu32 #define DECORATE_NAME(Name) Name##_u32 #else #error All of UINT_T, UINT_C, UINT_FMT_S, UINT_FMT_ARG, and DECORATE_NAME must be defined before #including this file #endif #endif #define BITS (sizeof(UINT_T) * CHAR_BIT) #define ZERO UINT_C(0) // Default for UINT_FMT_ARG #ifndef UINT_FMT_ARG #define UINT_FMT_ARG(X) X #endif // Default comparison #ifndef UINT_LESSTHAN #define UINT_LESSTHAN(A, B) ((A) < (B)) #endif #ifndef MC_UINT_MAX #define MC_UINT_MAX ~(UINT_C(0)) #endif // Default addition #ifndef UINT_ADD #define UINT_ADD(A, B) ((A) + (B)) #endif #ifndef UINT_SUB #define UINT_SUB(A, B) ((A) - (B)) #endif // Default lshift (also handles negatives as right-shift) #ifndef UINT_LSHIFT static inline UINT_T DECORATE_NAME(_mc_default_lshift)(UINT_T lhs, int off) { if (off < 0) { return lhs >> -off; } else { return lhs << off; } } #define UINT_LSHIFT DECORATE_NAME(_mc_default_lshift) #endif #ifndef UINT_BITOR #define UINT_BITOR(A, B) ((A) | (B)) #endif static inline int DECORATE_NAME(_mc_compare)(UINT_T lhs, UINT_T rhs) { if (UINT_LESSTHAN(lhs, rhs)) { return -1; } else if (UINT_LESSTHAN(rhs, lhs)) { return 1; } else { return 0; } } #define UINT_COMPARE DECORATE_NAME(_mc_compare) // MinCoverGenerator models the MinCoverGenerator type added in // SERVER-68600. typedef struct { UINT_T _rangeMin; UINT_T _rangeMax; size_t _sparsity; int32_t _trimFactor; // _maxlen is the maximum bit length of edges in the mincover. size_t _maxlen; } DECORATE_NAME(MinCoverGenerator); static inline DECORATE_NAME(MinCoverGenerator) * DECORATE_NAME(MinCoverGenerator_new)(UINT_T rangeMin, UINT_T rangeMax, UINT_T max, size_t sparsity, mc_optional_int32_t opt_trimFactor, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); if (UINT_COMPARE(rangeMin, rangeMax) > 0) { CLIENT_ERR("Range min (%" UINT_FMT_S ") must be less than or equal to range max (%" UINT_FMT_S ") for range search", UINT_FMT_ARG(rangeMin), UINT_FMT_ARG(rangeMax)); return NULL; } if (UINT_COMPARE(rangeMax, max) > 0) { CLIENT_ERR("Range max (%" UINT_FMT_S ") must be less than or equal to max (%" UINT_FMT_S ") for range search", UINT_FMT_ARG(rangeMax), UINT_FMT_ARG(max)); return NULL; } if (sparsity == 0) { CLIENT_ERR("Sparsity must be > 0"); return NULL; } size_t maxlen = (size_t)BITS - DECORATE_NAME(mc_count_leading_zeros)(max); int32_t trimFactor = trimFactorDefault(maxlen, opt_trimFactor); if (trimFactor != 0 && mc_cmp_greater_equal_su(trimFactor, maxlen)) { CLIENT_ERR("Trim factor must be less than the number of bits (%zu) used to represent an element of the domain, " "but got %" PRId32, maxlen, trimFactor); return NULL; } if (trimFactor < 0) { CLIENT_ERR("Trim factor must be >= 0, but got (%" PRId32 ")", trimFactor); return NULL; } DECORATE_NAME(MinCoverGenerator) *mcg = bson_malloc0(sizeof(DECORATE_NAME(MinCoverGenerator))); mcg->_rangeMin = rangeMin; mcg->_rangeMax = rangeMax; mcg->_maxlen = (size_t)BITS - DECORATE_NAME(mc_count_leading_zeros)(max); mcg->_sparsity = sparsity; mcg->_trimFactor = trimFactor; return mcg; } static inline void DECORATE_NAME(MinCoverGenerator_destroy)(DECORATE_NAME(MinCoverGenerator) * mcg) { bson_free(mcg); } // applyMask applies a mask of 1 bits starting from the right. // Bits 0 to bit-1 are replaced with 1. Other bits are left as-is. static inline UINT_T DECORATE_NAME(applyMask)(UINT_T value, size_t maskedBits) { const UINT_T ones = MC_UINT_MAX; BSON_ASSERT(maskedBits <= (size_t)BITS); BSON_ASSERT(maskedBits >= 0); if (maskedBits == 0) { return value; } const size_t shift = ((size_t)BITS - maskedBits); const UINT_T mask = UINT_LSHIFT(ones, -(int)shift); return UINT_BITOR(value, mask); } static inline bool DECORATE_NAME(MinCoverGenerator_isLevelStored)(DECORATE_NAME(MinCoverGenerator) * mcg, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); size_t level = mcg->_maxlen - maskedBits; BSON_ASSERT(mc_in_range_size_t_signed(mcg->_trimFactor)); size_t trimFactor_sz = (size_t)mcg->_trimFactor; return 0 == maskedBits || (level >= trimFactor_sz && 0 == (level % mcg->_sparsity)); } static char * DECORATE_NAME(MinCoverGenerator_toString)(DECORATE_NAME(MinCoverGenerator) * mcg, UINT_T start, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); BSON_ASSERT(maskedBits <= mcg->_maxlen); BSON_ASSERT(maskedBits <= (size_t)BITS); BSON_ASSERT(maskedBits >= 0); if (maskedBits == mcg->_maxlen) { return bson_strdup("root"); } UINT_T shifted = UINT_LSHIFT(start, -(int)maskedBits); mc_bitstring valueBin = DECORATE_NAME(mc_convert_to_bitstring)(shifted); char *ret = bson_strndup(valueBin.str + ((size_t)BITS - mcg->_maxlen + maskedBits), mcg->_maxlen + maskedBits); return ret; } static inline void DECORATE_NAME(MinCoverGenerator_minCoverRec)(DECORATE_NAME(MinCoverGenerator) * mcg, mc_array_t *c, UINT_T blockStart, size_t maskedBits) { BSON_ASSERT_PARAM(mcg); BSON_ASSERT_PARAM(c); const UINT_T blockEnd = DECORATE_NAME(applyMask)(blockStart, maskedBits); if (UINT_COMPARE(blockEnd, mcg->_rangeMin) < 0 || UINT_COMPARE(blockStart, mcg->_rangeMax) > 0) { return; } if (UINT_COMPARE(blockStart, mcg->_rangeMin) >= 0 && UINT_COMPARE(blockEnd, mcg->_rangeMax) <= 0 && DECORATE_NAME(MinCoverGenerator_isLevelStored)(mcg, maskedBits)) { char *edge = DECORATE_NAME(MinCoverGenerator_toString)(mcg, blockStart, maskedBits); _mc_array_append_val(c, edge); return; } BSON_ASSERT(maskedBits > 0); const size_t newBits = maskedBits - 1u; DECORATE_NAME(MinCoverGenerator_minCoverRec)(mcg, c, blockStart, newBits); DECORATE_NAME(MinCoverGenerator_minCoverRec) (mcg, c, UINT_BITOR(blockStart, UINT_LSHIFT(UINT_C(1), (int)newBits)), newBits); } static inline mc_mincover_t *DECORATE_NAME(MinCoverGenerator_minCover)(DECORATE_NAME(MinCoverGenerator) * mcg) { BSON_ASSERT_PARAM(mcg); mc_mincover_t *mc = mc_mincover_new(); DECORATE_NAME(MinCoverGenerator_minCoverRec) (mcg, &mc->mincover, ZERO, mcg->_maxlen); return mc; } static inline int32_t DECORATE_NAME(MinCoverGenerator_usedTrimFactor)(DECORATE_NAME(MinCoverGenerator) * mcg) { BSON_ASSERT_PARAM(mcg); return mcg->_trimFactor; } // adjustBounds increments *lowerBound if includeLowerBound is false and // decrements *upperBound if includeUpperBound is false. // lowerBound, min, upperBound, and max are expected to come from the result // of mc_getTypeInfo. static bool DECORATE_NAME(adjustBounds)(UINT_T *lowerBound, bool includeLowerBound, UINT_T min, UINT_T *upperBound, bool includeUpperBound, UINT_T max, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(lowerBound); BSON_ASSERT_PARAM(upperBound); if (!includeLowerBound) { if (UINT_COMPARE(*lowerBound, max) >= 0) { CLIENT_ERR("Lower bound (%" UINT_FMT_S ") must be less than the range maximum (%" UINT_FMT_S ") if lower bound is excluded from range.", UINT_FMT_ARG(*lowerBound), UINT_FMT_ARG(max)); return false; } *lowerBound = UINT_ADD(*lowerBound, UINT_C(1)); } if (!includeUpperBound) { if (UINT_COMPARE(*upperBound, min) <= 0) { CLIENT_ERR("Upper bound (%" UINT_FMT_S ") must be greater than the range minimum (%" UINT_FMT_S ") if upper bound is excluded from range.", UINT_FMT_ARG(*upperBound), UINT_FMT_ARG(min)); return false; } *upperBound = UINT_SUB(*upperBound, UINT_C(1)); } return true; } #undef UINT_T #undef UINT_C #undef UINT_FMT_S #undef UINT_FMT_ARG #undef DECORATE_NAME #undef BITS #undef UINT_COMPARE #undef UINT_ADD #undef UINT_SUB #undef UINT_LSHIFT #undef UINT_BITOR #undef MC_UINT_MAX #undef ZERO #undef UINT_LESSTHAN libmongocrypt-1.19.0/src/mc-range-mincover-private.h000066400000000000000000000073651521103432300224200ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_RANGE_MINCOVER_PRIVATE_H #define MC_RANGE_MINCOVER_PRIVATE_H #include "mc-dec128.h" #include "mc-optional-private.h" #include "mongocrypt-status-private.h" #include // size_t #include // mc_mincover_t represents the results of the mincover algorithm. typedef struct _mc_mincover_t mc_mincover_t; // mc_mincover_get returns edge at an index. // Returns NULL if `index` is out of range. const char *mc_mincover_get(mc_mincover_t *mincover, size_t index); // mc_mincover_len returns the number of represented mincover. size_t mc_mincover_len(mc_mincover_t *mincover); // Return the trimFactor that was used to generate this mincover. int32_t mc_mincover_get_used_trimFactor(const mc_mincover_t *mincover); // mc_mincover_destroys frees `mincover`. void mc_mincover_destroy(mc_mincover_t *mincover); typedef struct { int32_t lowerBound; bool includeLowerBound; int32_t upperBound; bool includeUpperBound; mc_optional_int32_t min; mc_optional_int32_t max; size_t sparsity; mc_optional_int32_t trimFactor; } mc_getMincoverInt32_args_t; // mc_getMincoverInt32 implements the Mincover Generation algorithm described in // SERVER-68600 for int32_t. mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; typedef struct { int64_t lowerBound; bool includeLowerBound; int64_t upperBound; bool includeUpperBound; mc_optional_int64_t min; mc_optional_int64_t max; size_t sparsity; mc_optional_int32_t trimFactor; } mc_getMincoverInt64_args_t; // mc_getMincoverInt64 implements the Mincover Generation algorithm described in // SERVER-68600 for int64_t. mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; typedef struct { double lowerBound; bool includeLowerBound; double upperBound; bool includeUpperBound; size_t sparsity; mc_optional_double_t min; mc_optional_double_t max; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_getMincoverDouble_args_t; // mc_getMincoverDouble implements the Mincover Generation algorithm described // in SERVER-68600 for double. mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() typedef struct { mc_dec128 lowerBound; bool includeLowerBound; mc_dec128 upperBound; bool includeUpperBound; size_t sparsity; mc_optional_dec128_t min, max; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_getMincoverDecimal128_args_t; // mc_getMincoverDecimal128 implements the Mincover Generation algorithm // described in SERVER-68600 for Decimal128 (as mc_dec128). mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT #endif /* MC_RANGE_MINCOVER_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-range-mincover.c000066400000000000000000000320171521103432300207330ustar00rootroot00000000000000/* * Copyright 2022-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 #include "mc-array-private.h" #include "mc-cmp-private.h" #include "mc-range-edge-generation-private.h" // mc_count_leading_zeros_u32 #include "mc-range-encoding-private.h" // mc_getTypeInfo32, trimFactorDefault #include "mc-range-mincover-private.h" #include "mongocrypt-private.h" struct _mc_mincover_t { /* mincover is an array of `char*` edge strings. */ mc_array_t mincover; int32_t usedTrimFactor; // The `trimFactor` that was used to produce this mincover. }; static mc_mincover_t *mc_mincover_new(void) { mc_mincover_t *mincover = bson_malloc0(sizeof(mc_mincover_t)); _mc_array_init(&mincover->mincover, sizeof(char *)); return mincover; } const char *mc_mincover_get(mc_mincover_t *mincover, size_t index) { BSON_ASSERT_PARAM(mincover); if (mincover->mincover.len == 0 || index > mincover->mincover.len - 1u) { return NULL; } return _mc_array_index(&mincover->mincover, char *, index); } size_t mc_mincover_len(mc_mincover_t *mincover) { BSON_ASSERT_PARAM(mincover); return mincover->mincover.len; } int32_t mc_mincover_get_used_trimFactor(const mc_mincover_t *mincover) { return mincover->usedTrimFactor; } void mc_mincover_destroy(mc_mincover_t *mincover) { if (NULL == mincover) { return; } for (size_t i = 0; i < mincover->mincover.len; i++) { char *val = _mc_array_index(&mincover->mincover, char *, i); bson_free(val); } _mc_array_destroy(&mincover->mincover); bson_free(mincover); } #define UINT_T uint32_t #define UINT_C UINT32_C #define UINT_FMT_S PRIu32 #define DECORATE_NAME(N) N##_u32 #include "mc-range-mincover-generator.template.h" #define UINT_T uint64_t #define UINT_C UINT64_C #define UINT_FMT_S PRIu64 #define DECORATE_NAME(N) N##_u64 #include "mc-range-mincover-generator.template.h" // The 128-bit version is only required for Decimal128, otherwise generates // unused-fn warnings #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() #define UINT_T mlib_int128 #define UINT_C MLIB_INT128 #define UINT_FMT_S "s" #define UINT_FMT_ARG(X) (mlib_int128_format(X).str) #define DECORATE_NAME(N) N##_u128 #define UINT_LESSTHAN(L, R) (mlib_int128_ucmp(L, R) < 0) #define UINT_ADD mlib_int128_add #define UINT_SUB mlib_int128_sub #define UINT_LSHIFT mlib_int128_lshift #define MC_UINT_MAX MLIB_INT128_UMAX #define UINT_BITOR mlib_int128_bitor #include "mc-range-mincover-generator.template.h" #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT // Check bounds and return an error message including the original inputs. #define IDENTITY(X) X #define LESSTHAN(L, R) ((L) < (R)) #define CHECK_BOUNDS(args, FMT, FormatArg, LessThan) \ if (1) { \ if ((args).min.set) { \ if (LessThan((args).upperBound, (args).min.value)) { \ CLIENT_ERR("Upper bound (%" FMT ") must be greater than or equal to the range minimum (%" FMT ")", \ FormatArg((args).upperBound), \ FormatArg((args).min.value)); \ return false; \ } \ if (!(args).includeUpperBound && !LessThan((args.min.value), (args.upperBound))) { \ CLIENT_ERR("Upper bound (%" FMT ") must be greater than the range minimum (%" FMT \ ") if upper bound is excluded from range", \ FormatArg((args).upperBound), \ FormatArg((args).min.value)); \ return false; \ } \ } \ if ((args).max.set) { \ if (LessThan((args).max.value, (args).lowerBound)) { \ CLIENT_ERR("Lower bound (%" FMT ") must be less than or equal to the range maximum (%" FMT ")", \ FormatArg((args).lowerBound), \ FormatArg((args).max.value)); \ return false; \ } \ if (!(args).includeLowerBound && !LessThan((args).lowerBound, (args).max.value)) { \ CLIENT_ERR("Lower bound (%" FMT ") must be less than the range maximum (%" FMT \ ") if lower bound is excluded from range", \ FormatArg((args).lowerBound), \ FormatArg((args).max.value)); \ return false; \ } \ } \ } else \ (void)0 mc_mincover_t *mc_getMincoverInt32(mc_getMincoverInt32_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); CHECK_BOUNDS(args, PRId32, IDENTITY, LESSTHAN); mc_OSTType_Int32 a, b; if (!mc_getTypeInfo32((mc_getTypeInfo32_args_t){.min = args.min, .max = args.max, .value = args.lowerBound}, &a, status)) { return NULL; } if (!mc_getTypeInfo32((mc_getTypeInfo32_args_t){.min = args.min, .max = args.max, .value = args.upperBound}, &b, status)) { return NULL; } BSON_ASSERT(a.min == b.min); BSON_ASSERT(a.max == b.max); if (!adjustBounds_u32(&a.value, args.includeLowerBound, a.min, &b.value, args.includeUpperBound, b.max, status)) { return NULL; } MinCoverGenerator_u32 *mcg = MinCoverGenerator_new_u32(a.value, b.value, a.max, args.sparsity, args.trimFactor, status); if (!mcg) { return NULL; } mc_mincover_t *mc = MinCoverGenerator_minCover_u32(mcg); mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u32(mcg); MinCoverGenerator_destroy_u32(mcg); return mc; } mc_mincover_t *mc_getMincoverInt64(mc_getMincoverInt64_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); CHECK_BOUNDS(args, PRId64, IDENTITY, LESSTHAN); mc_OSTType_Int64 a, b; if (!mc_getTypeInfo64((mc_getTypeInfo64_args_t){.min = args.min, .max = args.max, .value = args.lowerBound}, &a, status)) { return NULL; } if (!mc_getTypeInfo64((mc_getTypeInfo64_args_t){.min = args.min, .max = args.max, .value = args.upperBound}, &b, status)) { return NULL; } BSON_ASSERT(a.min == b.min); BSON_ASSERT(a.max == b.max); if (!adjustBounds_u64(&a.value, args.includeLowerBound, a.min, &b.value, args.includeUpperBound, b.max, status)) { return NULL; } MinCoverGenerator_u64 *mcg = MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, args.trimFactor, status); if (!mcg) { return NULL; } mc_mincover_t *mc = MinCoverGenerator_minCover_u64(mcg); mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u64(mcg); MinCoverGenerator_destroy_u64(mcg); return mc; } // mc_getMincoverDouble implements the Mincover Generation algorithm described // in SERVER-68600 for double. mc_mincover_t *mc_getMincoverDouble(mc_getMincoverDouble_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); CHECK_BOUNDS(args, "g", IDENTITY, LESSTHAN); mc_OSTType_Double a, b; if (!mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = args.lowerBound, .min = args.min, .max = args.max, .precision = args.precision}, &a, status)) { return NULL; } if (!mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = args.upperBound, .min = args.min, .max = args.max, .precision = args.precision}, &b, status)) { return NULL; } BSON_ASSERT(a.min == b.min); BSON_ASSERT(a.max == b.max); if (!adjustBounds_u64(&a.value, args.includeLowerBound, a.min, &b.value, args.includeUpperBound, b.max, status)) { return NULL; } MinCoverGenerator_u64 *mcg = MinCoverGenerator_new_u64(a.value, b.value, a.max, args.sparsity, args.trimFactor, status); if (!mcg) { return NULL; } mc_mincover_t *mc = MinCoverGenerator_minCover_u64(mcg); mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u64(mcg); MinCoverGenerator_destroy_u64(mcg); return mc; } #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() mc_mincover_t *mc_getMincoverDecimal128(mc_getMincoverDecimal128_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(status); #define ToString(Dec) (mc_dec128_to_string(Dec).str) CHECK_BOUNDS(args, "s", ToString, mc_dec128_less); mc_OSTType_Decimal128 a, b; if (!mc_getTypeInfoDecimal128((mc_getTypeInfoDecimal128_args_t){.value = args.lowerBound, .min = args.min, .max = args.max, .precision = args.precision}, &a, status)) { return NULL; } if (!mc_getTypeInfoDecimal128((mc_getTypeInfoDecimal128_args_t){.value = args.upperBound, .min = args.min, .max = args.max, .precision = args.precision}, &b, status)) { return NULL; } BSON_ASSERT(mlib_int128_eq(a.min, b.min)); BSON_ASSERT(mlib_int128_eq(a.max, b.max)); if (!adjustBounds_u128(&a.value, args.includeLowerBound, a.min, &b.value, args.includeUpperBound, b.max, status)) { return NULL; } MinCoverGenerator_u128 *mcg = MinCoverGenerator_new_u128(a.value, b.value, a.max, args.sparsity, args.trimFactor, status); if (!mcg) { return NULL; } mc_mincover_t *mc = MinCoverGenerator_minCover_u128(mcg); mc->usedTrimFactor = MinCoverGenerator_usedTrimFactor_u128(mcg); MinCoverGenerator_destroy_u128(mcg); return mc; } #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT libmongocrypt-1.19.0/src/mc-rangeopts-private.h000066400000000000000000000077421521103432300215050ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MC_RANGEOPTS_PRIVATE_H #define MC_RANGEOPTS_PRIVATE_H #include #include "mc-optional-private.h" #include "mongocrypt-status-private.h" typedef struct { bson_t *bson; struct { bson_iter_t value; bool set; } min; struct { bson_iter_t value; bool set; } max; int64_t sparsity; mc_optional_int32_t precision; mc_optional_int32_t trimFactor; } mc_RangeOpts_t; // `mc_RangeOpts_t` inherits extended alignment from libbson. To dynamically allocate, use // aligned allocation (e.g. BSON_ALIGNED_ALLOC) BSON_STATIC_ASSERT2(alignof_mc_RangeOpts_t, BSON_ALIGNOF(mc_RangeOpts_t) >= BSON_MAX(BSON_ALIGNOF(bson_t), BSON_ALIGNOF(bson_iter_t))); /* mc_RangeOpts_parse parses a BSON document into mc_RangeOpts_t. * The document is expected to have the form: * { * "min": BSON value, * "max": BSON value, * "sparsity": Optional, * "precision": Optional, * "trimFactor": Optional, * } */ bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_t *status); /* * mc_RangeOpts_to_FLE2RangeInsertSpec creates a placeholder value to be * encrypted. It is only expected to be called when query_type is unset. The * output FLE2RangeInsertSpec is a BSON document of the form: * { * "v": BSON value to encrypt, * "min": BSON value, * "max": BSON value, * "precision": Optional * } * * v is expect to be a BSON document of the form: * { "v": BSON value to encrypt }. * * Preconditions: out must be initialized by caller. */ bool mc_RangeOpts_to_FLE2RangeInsertSpec(const mc_RangeOpts_t *ro, const bson_t *v, bson_t *out, mongocrypt_status_t *status); /* mc_RangeOpts_appendMin appends the minimum value of the range for a given * type. If `ro->min` is unset, uses the lowest representable value of the value * type. Errors if `valueType` does not match the type of `ro->min`. */ bool mc_RangeOpts_appendMin(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status); /* mc_RangeOpts_appendMax appends the maximum value of the range for a given * type. If `ro->max` is unset, uses the highest representable value of the * value type. Errors if `valueType` does not match the type of `ro->max`. */ bool mc_RangeOpts_appendMax(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status); /* mc_RangeOpts_appendTrimFactor appends the trim factor of the field. If `ro->trimFactor` is unset, * defaults to 0. Errors if `ro->trimFactor` is out of bounds based on the size of the domain * computed from `valueType`, `ro->min` and `ro->max`. */ bool mc_RangeOpts_appendTrimFactor(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status); void mc_RangeOpts_cleanup(mc_RangeOpts_t *ro); #endif // MC_RANGEOPTS_PRIVATE_H libmongocrypt-1.19.0/src/mc-rangeopts.c000066400000000000000000000475631521103432300200350ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-fle2-encryption-placeholder-private.h" #include "mc-rangeopts-private.h" #include "mc-cmp-private.h" #include "mc-range-edge-generation-private.h" // mc_count_leading_zeros_XX #include "mc-range-encoding-private.h" // mc_getTypeInfoXX #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" // mc_bson_type_to_string #include // DBL_MAX // Common logic for testing field name, tracking duplication, and presence. #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR(ERROR_PREFIX "Unexpected duplicate field '" #Name "'"); \ return false; \ } \ has_##Name = true; \ ((void)0) #define END_IF_FIELD \ continue; \ } \ else((void)0) #define ERROR_PREFIX "Error parsing RangeOpts: " bool mc_RangeOpts_parse(mc_RangeOpts_t *ro, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter = {0}; bool has_min = false, has_max = false, has_sparsity = false, has_precision = false, has_trimFactor = false; BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(in); BSON_ASSERT(status || true); *ro = (mc_RangeOpts_t){0}; ro->bson = bson_copy(in); if (!bson_iter_init(&iter, ro->bson)) { CLIENT_ERR(ERROR_PREFIX "Invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); BSON_ASSERT(field); IF_FIELD(min); { ro->min.set = true; ro->min.value = iter; } END_IF_FIELD; IF_FIELD(max); { ro->max.set = true; ro->max.value = iter; } END_IF_FIELD; IF_FIELD(sparsity); { if (!BSON_ITER_HOLDS_INT64(&iter)) { CLIENT_ERR(ERROR_PREFIX "Expected int64 for sparsity, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } ro->sparsity = bson_iter_int64(&iter); if (!mc_validate_sparsity(ro->sparsity, status)) { return false; } } END_IF_FIELD; IF_FIELD(precision); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "'precision' must be an int32"); return false; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'precision' must be non-negative"); return false; } ro->precision = OPT_I32(val); } END_IF_FIELD; IF_FIELD(trimFactor); { if (!BSON_ITER_HOLDS_INT32(&iter)) { CLIENT_ERR(ERROR_PREFIX "Expected int32 for trimFactor, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } int32_t val = bson_iter_int32(&iter); if (val < 0) { CLIENT_ERR(ERROR_PREFIX "'trimFactor' must be non-negative"); return false; } ro->trimFactor = OPT_I32(val); } END_IF_FIELD; CLIENT_ERR(ERROR_PREFIX "Unrecognized field: '%s'", field); return false; } // Do not error if min/max are not present. min/max are optional. // Do not error if precision is not present. Precision is optional and only // applies to double/decimal128. // Do not error if trimFactor is not present. It is optional. if (!has_sparsity) { ro->sparsity = mc_FLERangeSparsityDefault; } // Expect precision only to be set for double or decimal128. if (has_precision) { if (!ro->min.set) { CLIENT_ERR(ERROR_PREFIX "setting precision requires min"); return false; } bson_type_t minType = bson_iter_type(&ro->min.value); if (minType != BSON_TYPE_DOUBLE && minType != BSON_TYPE_DECIMAL128) { CLIENT_ERR(ERROR_PREFIX "expected 'precision' to be set with double or decimal128 " "index, but got: %s min", mc_bson_type_to_string(minType)); return false; } if (!ro->max.set) { CLIENT_ERR(ERROR_PREFIX "setting precision requires max"); return false; } bson_type_t maxType = bson_iter_type(&ro->max.value); if (maxType != BSON_TYPE_DOUBLE && maxType != BSON_TYPE_DECIMAL128) { CLIENT_ERR(ERROR_PREFIX "expected 'precision' to be set with double or decimal128 " "index, but got: %s max", mc_bson_type_to_string(maxType)); return false; } } // Expect min and max to match types. if (ro->min.set && ro->max.set) { bson_type_t minType = bson_iter_type(&ro->min.value), maxType = bson_iter_type(&ro->max.value); if (minType != maxType) { CLIENT_ERR(ERROR_PREFIX "expected 'min' and 'max' to be same type, but got: %s " "min and %s max", mc_bson_type_to_string(minType), mc_bson_type_to_string(maxType)); return false; } } if (ro->min.set || ro->max.set) { // Setting min/max without precision is error for double and decimal128. if (ro->min.set) { bson_type_t minType = bson_iter_type(&ro->min.value); if (minType == BSON_TYPE_DOUBLE || minType == BSON_TYPE_DECIMAL128) { if (!has_precision) { CLIENT_ERR(ERROR_PREFIX "expected 'precision' to be set with 'min' for %s", mc_bson_type_to_string(minType)); return false; } } } if (ro->max.set) { bson_type_t maxType = bson_iter_type(&ro->max.value); if (maxType == BSON_TYPE_DOUBLE || maxType == BSON_TYPE_DECIMAL128) { if (!has_precision) { CLIENT_ERR(ERROR_PREFIX "expected 'precision' to be set with 'max' for %s", mc_bson_type_to_string(maxType)); return false; } } } } if (ro->trimFactor.set) { // At this point, we do not know the type of the field if min and max are unspecified. Wait to // validate the value of trimFactor. } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error making FLE2RangeInsertSpec: " bool mc_RangeOpts_to_FLE2RangeInsertSpec(const mc_RangeOpts_t *ro, const bson_t *v, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(v); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bson_iter_t v_iter; if (!bson_iter_init_find(&v_iter, v, "v")) { CLIENT_ERR(ERROR_PREFIX "Unable to find 'v' in input"); return false; } bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "v", &child)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!bson_append_iter(&child, "v", 1, &v_iter)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!mc_RangeOpts_appendMin(ro, bson_iter_type(&v_iter), "min", &child, status)) { return false; } if (!mc_RangeOpts_appendMax(ro, bson_iter_type(&v_iter), "max", &child, status)) { return false; } if (ro->precision.set) { BSON_ASSERT(ro->precision.value <= INT32_MAX); if (!BSON_APPEND_INT32(&child, "precision", (int32_t)ro->precision.value)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } } if (!mc_RangeOpts_appendTrimFactor(ro, bson_iter_type(&v_iter), "trimFactor", &child, status)) { return false; } if (!bson_append_document_end(out, &child)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error appending min to FLE2RangeInsertSpec: " bool mc_RangeOpts_appendMin(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(fieldName); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); if (ro->min.set) { if (bson_iter_type(&ro->min.value) != valueType) { CLIENT_ERR(ERROR_PREFIX "expected matching 'min' and value type. Got range option " "'min' of type %s and value of type %s", mc_bson_type_to_string(bson_iter_type(&ro->min.value)), mc_bson_type_to_string(valueType)); return false; } if (!bson_append_iter(out, fieldName, -1, &ro->min.value)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } return true; } if (valueType == BSON_TYPE_INT32 || valueType == BSON_TYPE_INT64 || valueType == BSON_TYPE_DATE_TIME) { CLIENT_ERR(ERROR_PREFIX "Range option 'min' is required for type: %s", mc_bson_type_to_string(valueType)); return false; } else if (valueType == BSON_TYPE_DOUBLE) { if (!BSON_APPEND_DOUBLE(out, fieldName, -DBL_MAX)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } } else if (valueType == BSON_TYPE_DECIMAL128) { #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() const bson_decimal128_t min = mc_dec128_to_bson_decimal128(MC_DEC128_LARGEST_NEGATIVE); if (!BSON_APPEND_DECIMAL128(out, fieldName, &min)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } #else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓ CLIENT_ERR(ERROR_PREFIX "unsupported BSON type (Decimal128) for range: libmongocrypt " "was built without extended Decimal128 support"); return false; #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT } else { CLIENT_ERR(ERROR_PREFIX "unsupported BSON type: %s for range", mc_bson_type_to_string(valueType)); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error appending max to FLE2RangeInsertSpec: " bool mc_RangeOpts_appendMax(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(fieldName); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); if (ro->max.set) { if (bson_iter_type(&ro->max.value) != valueType) { CLIENT_ERR(ERROR_PREFIX "expected matching 'max' and value type. Got range option " "'max' of type %s and value of type %s", mc_bson_type_to_string(bson_iter_type(&ro->max.value)), mc_bson_type_to_string(valueType)); return false; } if (!bson_append_iter(out, fieldName, -1, &ro->max.value)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } return true; } if (valueType == BSON_TYPE_INT32 || valueType == BSON_TYPE_INT64 || valueType == BSON_TYPE_DATE_TIME) { CLIENT_ERR(ERROR_PREFIX "Range option 'max' is required for type: %s", mc_bson_type_to_string(valueType)); return false; } else if (valueType == BSON_TYPE_DOUBLE) { if (!BSON_APPEND_DOUBLE(out, fieldName, DBL_MAX)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } } else if (valueType == BSON_TYPE_DECIMAL128) { #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() const bson_decimal128_t max = mc_dec128_to_bson_decimal128(MC_DEC128_LARGEST_POSITIVE); if (!BSON_APPEND_DECIMAL128(out, fieldName, &max)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } #else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓ CLIENT_ERR(ERROR_PREFIX "unsupported BSON type (Decimal128) for range: libmongocrypt " "was built without extended Decimal128 support"); return false; #endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT } else { CLIENT_ERR(ERROR_PREFIX "unsupported BSON type: %s for range", mc_bson_type_to_string(valueType)); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error in getNumberOfBits: " // Used to calculate max trim factor. Returns the number of bits required to represent any number in // the domain. static bool mc_getNumberOfBits(const mc_RangeOpts_t *ro, bson_type_t valueType, uint32_t *bitsOut, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(bitsOut); // For each type, we use getTypeInfo to get the total number of values in the domain (-1) // which tells us how many bits are needed to represent the whole domain. // note - can't use a switch statement because of -Werror=switch-enum if (valueType == BSON_TYPE_INT32) { int32_t value = 0; mc_optional_int32_t rmin = {false, 0}, rmax = {false, 0}; if (ro->min.set) { BSON_ASSERT(ro->max.set); value = bson_iter_int32(&ro->min.value); rmin = OPT_I32(value); rmax = OPT_I32(bson_iter_int32(&ro->max.value)); } mc_getTypeInfo32_args_t args = {value, rmin, rmax}; mc_OSTType_Int32 out; if (!mc_getTypeInfo32(args, &out, status)) { return false; } *bitsOut = 32 - (uint32_t)mc_count_leading_zeros_u32(out.max); return true; } else if (valueType == BSON_TYPE_INT64) { int64_t value = 0; mc_optional_int64_t rmin = {false, 0}, rmax = {false, 0}; if (ro->min.set) { BSON_ASSERT(ro->max.set); value = bson_iter_int64(&ro->min.value); rmin = OPT_I64(value); rmax = OPT_I64(bson_iter_int64(&ro->max.value)); } mc_getTypeInfo64_args_t args = {value, rmin, rmax}; mc_OSTType_Int64 out; if (!mc_getTypeInfo64(args, &out, status)) { return false; } *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max); return true; } else if (valueType == BSON_TYPE_DATE_TIME) { int64_t value = 0; mc_optional_int64_t rmin = {false, 0}, rmax = {false, 0}; if (ro->min.set) { BSON_ASSERT(ro->max.set); value = bson_iter_date_time(&ro->min.value); rmin = OPT_I64(value); rmax = OPT_I64(bson_iter_date_time(&ro->max.value)); } mc_getTypeInfo64_args_t args = {value, rmin, rmax}; mc_OSTType_Int64 out; if (!mc_getTypeInfo64(args, &out, status)) { return false; } *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max); return true; } else if (valueType == BSON_TYPE_DOUBLE) { double value = 0; mc_optional_double_t rmin = {false, 0}, rmax = {false, 0}; mc_optional_int32_t prec = ro->precision; if (ro->min.set) { BSON_ASSERT(ro->max.set); value = bson_iter_double(&ro->min.value); rmin = OPT_DOUBLE(value); rmax = OPT_DOUBLE(bson_iter_double(&ro->max.value)); } mc_getTypeInfoDouble_args_t args = {value, rmin, rmax, prec}; mc_OSTType_Double out; if (!mc_getTypeInfoDouble(args, &out, status)) { return false; } *bitsOut = 64 - (uint32_t)mc_count_leading_zeros_u64(out.max); return true; } #if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT() else if (valueType == BSON_TYPE_DECIMAL128) { mc_dec128 value = MC_DEC128_ZERO; mc_optional_dec128_t rmin = {false, MC_DEC128_ZERO}, rmax = {false, MC_DEC128_ZERO}; mc_optional_int32_t prec = ro->precision; if (ro->min.set) { BSON_ASSERT(ro->max.set); value = mc_dec128_from_bson_iter(&ro->min.value); rmin = OPT_MC_DEC128(value); rmax = OPT_MC_DEC128(mc_dec128_from_bson_iter(&ro->max.value)); } mc_getTypeInfoDecimal128_args_t args = {value, rmin, rmax, prec}; mc_OSTType_Decimal128 out; if (!mc_getTypeInfoDecimal128(args, &out, status)) { return false; } *bitsOut = 128 - (uint32_t)mc_count_leading_zeros_u128(out.max); return true; } #endif CLIENT_ERR(ERROR_PREFIX "unsupported BSON type: %s for range", mc_bson_type_to_string(valueType)); return false; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error appending trim factor to FLE2RangeInsertSpec: " bool mc_RangeOpts_appendTrimFactor(const mc_RangeOpts_t *ro, bson_type_t valueType, const char *fieldName, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(ro); BSON_ASSERT_PARAM(fieldName); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); if (!ro->trimFactor.set) { // A default `trimFactor` will be selected later with `trimFactorDefault` return true; } uint32_t nbits; if (!mc_getNumberOfBits(ro, valueType, &nbits, status)) { return false; } // if nbits = 0, we want to allow trim factor = 0. uint32_t test = nbits ? nbits : 1; if (mc_cmp_greater_equal_su(ro->trimFactor.value, test)) { CLIENT_ERR(ERROR_PREFIX "Trim factor (%" PRId32 ") must be less than the total number of bits (%" PRIu32 ") used to represent any element in the domain.", ro->trimFactor.value, nbits); return false; } if (!BSON_APPEND_INT32(out, fieldName, ro->trimFactor.value)) { CLIENT_ERR(ERROR_PREFIX "failed to append BSON"); return false; } return true; } #undef ERROR_PREFIX void mc_RangeOpts_cleanup(mc_RangeOpts_t *ro) { if (!ro) { return; } bson_destroy(ro->bson); } libmongocrypt-1.19.0/src/mc-reader-private.h000066400000000000000000000061241521103432300207360ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MONGOCRYPT_READER_PRIVATE_H #define MONGOCRYPT_READER_PRIVATE_H #include "mongocrypt-buffer-private.h" #include #include /** * A non-owning forward-only cursor api to read a buffer. * * Tracks length of buffer and current position of buffer. parser_name is * typically __func__ to provide useful error messages automatically. * * All numbers are read as little endian. * * Functions return false on error and set mongocrypt_status_t to an error. * * Example: * * const _mongocrypt_buffer_t *buf; * mongocrypt_status_t *status * * mc_reader_t reader; * mc_reader_init_from_buffer (&reader, buf, __func__); * * _mongocrypt_buffer_t in; * if (!mc_reader_read_uuid_buffer (&reader, &in, status)) { * abort (); * } * * uint8_t value; * if (!mc_reader_read_u8 (&reader, value, status)) { * abort (); * } */ struct _mc_reader_t { const uint8_t *ptr; uint64_t pos; uint64_t len; const char *parser_name; }; typedef struct _mc_reader_t mc_reader_t; void mc_reader_init(mc_reader_t *reader, const uint8_t *ptr, uint64_t len, const char *parser_name); void mc_reader_init_from_buffer(mc_reader_t *reader, const _mongocrypt_buffer_t *buf, const char *parser_name); mc_reader_t *mc_reader_new(const uint8_t *ptr, uint64_t len, const char *parser_name); void mc_reader_destroy(mc_reader_t *reader); bool mc_reader_has_data(const mc_reader_t *reader); uint64_t mc_reader_get_remaining_length(const mc_reader_t *reader); uint64_t mc_reader_get_consumed_length(const mc_reader_t *reader); bool mc_reader_read_u8(mc_reader_t *reader, uint8_t *value, mongocrypt_status_t *status); bool mc_reader_read_u32(mc_reader_t *reader, uint32_t *value, mongocrypt_status_t *status); bool mc_reader_read_u64(mc_reader_t *reader, uint64_t *value, mongocrypt_status_t *status); bool mc_reader_read_bytes(mc_reader_t *reader, const uint8_t **ptr, uint64_t length, mongocrypt_status_t *status); bool mc_reader_read_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, uint64_t length, mongocrypt_status_t *status); bool mc_reader_read_uuid_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_reader_read_prfblock_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_reader_read_buffer_to_end(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); #endif /* MONGOCRYPT_READER_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-reader.c000066400000000000000000000146401521103432300172630ustar00rootroot00000000000000/* * Copyright 2022-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-private.h" #include "mc-reader-private.h" #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) #define CHECK_REMAINING_BUFFER_AND_RET(read_size) \ if ((reader->pos + (read_size)) > reader->len) { \ CLIENT_ERR("%s expected byte " \ "length >= %" PRIu64 " got: %" PRIu64, \ reader->parser_name, \ reader->pos + (read_size), \ reader->len); \ return false; \ } else \ ((void)0) void mc_reader_init(mc_reader_t *reader, const uint8_t *ptr, uint64_t len, const char *parser_name) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(ptr); BSON_ASSERT_PARAM(parser_name); *reader = (mc_reader_t){.pos = 0u, .ptr = ptr, .len = len, .parser_name = parser_name}; } void mc_reader_init_from_buffer(mc_reader_t *reader, const _mongocrypt_buffer_t *buf, const char *parser_name) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(parser_name); mc_reader_init(reader, buf->data, buf->len, parser_name); } mc_reader_t *mc_reader_new(const uint8_t *ptr, uint64_t len, const char *parser_name) { BSON_ASSERT_PARAM(ptr); BSON_ASSERT_PARAM(parser_name); mc_reader_t *reader = bson_malloc(sizeof(mc_reader_t)); mc_reader_init(reader, ptr, len, parser_name); return reader; } void mc_reader_destroy(mc_reader_t *reader) { bson_free(reader); } bool mc_reader_has_data(const mc_reader_t *reader) { BSON_ASSERT_PARAM(reader); return reader->pos < reader->len; } uint64_t mc_reader_get_remaining_length(const mc_reader_t *reader) { BSON_ASSERT_PARAM(reader); return reader->len - reader->pos; } uint64_t mc_reader_get_consumed_length(const mc_reader_t *reader) { BSON_ASSERT_PARAM(reader); return reader->pos; } bool mc_reader_read_u8(mc_reader_t *reader, uint8_t *value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(value); CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint8_t)); *value = *(reader->ptr + reader->pos); reader->pos += sizeof(uint8_t); return true; } bool mc_reader_read_u32(mc_reader_t *reader, uint32_t *value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(value); CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint32_t)); uint32_t temp; memcpy(&temp, reader->ptr + reader->pos, sizeof(uint32_t)); *value = BSON_UINT32_FROM_LE(temp); reader->pos += sizeof(uint32_t); return true; } bool mc_reader_read_u64(mc_reader_t *reader, uint64_t *value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(value); CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint64_t)); uint64_t temp; memcpy(&temp, reader->ptr + reader->pos, sizeof(uint64_t)); *value = BSON_UINT64_FROM_LE(temp); reader->pos += sizeof(uint64_t); return true; } bool mc_reader_read_bytes(mc_reader_t *reader, const uint8_t **ptr, uint64_t length, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(ptr); CHECK_REMAINING_BUFFER_AND_RET(length); *ptr = reader->ptr + reader->pos; reader->pos += length; return true; } bool mc_reader_read_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, uint64_t length, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(buf); const uint8_t *ptr; CHECK_AND_RETURN(mc_reader_read_bytes(reader, &ptr, length, status)); if (length > SIZE_MAX || !_mongocrypt_buffer_copy_from_data_and_size(buf, ptr, (size_t)length)) { CLIENT_ERR("%s failed to copy " "data of length %" PRIu64, reader->parser_name, length); return false; } return true; } bool mc_reader_read_uuid_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(buf); CHECK_AND_RETURN(mc_reader_read_buffer(reader, buf, 16, status)); buf->subtype = BSON_SUBTYPE_UUID; return true; } bool mc_reader_read_prfblock_buffer(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(buf); CHECK_AND_RETURN(mc_reader_read_buffer(reader, buf, 32, status)); buf->subtype = BSON_SUBTYPE_ENCRYPTED; return true; } bool mc_reader_read_buffer_to_end(mc_reader_t *reader, _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(reader); BSON_ASSERT_PARAM(buf); uint64_t length = reader->len - reader->pos; return mc_reader_read_buffer(reader, buf, length, status); } libmongocrypt-1.19.0/src/mc-schema-broker-private.h000066400000000000000000000165211521103432300222200ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef MC_SCHEMA_BROKER_PRIVATE_H #define MC_SCHEMA_BROKER_PRIVATE_H #include "mc-efc-private.h" // mc_EncryptedFieldConfig_t #include "mongocrypt-cache-collinfo-private.h" #include "mongocrypt-key-broker-private.h" #include "mongocrypt.h" #include // mc_schema_broker_t manages schemas for an auto encryption operation. // // A schema is either: // - a JSON Schema for CSFLE operations, or // - an encryptedFields document for QE operations. // typedef struct mc_schema_broker_t mc_schema_broker_t; mc_schema_broker_t *mc_schema_broker_new(void); void mc_schema_broker_destroy(mc_schema_broker_t *sb); void mc_schema_broker_support_mixing_schemas(mc_schema_broker_t *sb); // mc_schema_broker_request requests a schema for a collection. Ignores duplicates. // Returns error if two requests have different databases (not-yet supported). bool mc_schema_broker_request(mc_schema_broker_t *sb, const char *db, const char *coll, mongocrypt_status_t *status); // mc_schema_broker_has_multiple_requests returns true if there are requests for multiple unique collections bool mc_schema_broker_has_multiple_requests(const mc_schema_broker_t *sb); // mc_schema_broker_append_listCollections_filter appends a filter to use with the listCollections command. // Example: { "name": { "$in": [ "coll1", "coll2" ] } } // The filter matches all not-yet-satisfied collections. bool mc_schema_broker_append_listCollections_filter(const mc_schema_broker_t *sb, bson_t *out, mongocrypt_status_t *status); // mc_schema_broker_satisfy_from_collinfo satisfies a schema request with a result from listCollections. // The result is cached. bool mc_schema_broker_satisfy_from_collinfo(mc_schema_broker_t *sb, const bson_t *collinfo, _mongocrypt_cache_t *collinfo_cache, mongocrypt_status_t *status); // mc_schema_broker_satisfy_from_schemaMap tries to satisfy schema requests with a schemaMap. bool mc_schema_broker_satisfy_from_schemaMap(mc_schema_broker_t *sb, const bson_t *schema_map, mongocrypt_status_t *status); // mc_schema_broker_satisfy_from_encryptedFieldsMap tries to satisfy schema requests with an encryptedFieldsMap. bool mc_schema_broker_satisfy_from_encryptedFieldsMap(mc_schema_broker_t *sb, const bson_t *ef_map, mongocrypt_status_t *status); // mc_schema_broker_satisfy_from_cache tries to satisfy schema requests with the cache of listCollections results. bool mc_schema_broker_satisfy_from_cache(mc_schema_broker_t *sb, _mongocrypt_cache_t *collinfo_cache, mongocrypt_status_t *status); // mc_schema_broker_satisfy_from_create_or_collMod tries to satisfy a schema request by inspecting the outgoing // create/collMod commands. bool mc_schema_broker_satisfy_from_create_or_collMod(mc_schema_broker_t *sb, const bson_t *cmd, mongocrypt_status_t *status); // mc_schema_broker_satisfy_remaining_from_empty_schemas is called when a driver signals all listCollection results // have been fed. Assumes remaining collections have no schema. If collinfo_cache is passed, the empty result is cached. bool mc_schema_broker_satisfy_remaining_with_empty_schemas(mc_schema_broker_t *sb, _mongocrypt_cache_t *collinfo_cache /* may be NULL */, mongocrypt_status_t *status); // mc_schema_broker_has_any_qe_schemas returns true if any collection has encryptedFields. // // Aborts if any unsatisfied schema requests. `mc_schema_broker_need_more_schemas(sb)` must be false. // bool mc_schema_broker_has_any_qe_schemas(const mc_schema_broker_t *sb); // mc_schema_broker_need_more_schemas returns true if there are unsatisfied schema requests. bool mc_schema_broker_need_more_schemas(const mc_schema_broker_t *sb); // mc_schema_broker_get_encryptedFields returns encryptedFields for a collection. // // Returns NULL and sets error if `coll` is not found or has no encryptedFields. // // Aborts if any unsatisfied schema requests. `mc_schema_broker_need_more_schemas(sb)` must be false. // const mc_EncryptedFieldConfig_t * mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status); // mc_schema_broker_maybe_get_encryptedFields returns encryptedFields for a collection if any exists. // // Returns NULL if none is found. const mc_EncryptedFieldConfig_t * mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status); typedef enum { MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, // target the crypt_shared library. MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, // target mongocryptd process. MC_CMD_SCHEMAS_FOR_SERVER // target the server (mongod/mongos). } mc_cmd_target_t; // mc_schema_broker_add_schemas_to_cmd adds schema information to a command. // // Aborts if any unsatisfied schema requests. `mc_schema_broker_need_more_schemas(sb)` must be false. // // Schemas are added with the fields: // - jsonSchema: for CSFLE with one schema. // - csfleEncryptionSchemas: for CSFLE with multiple schemas. // - encryptionInformation: for QE. // // Set cmd_target to the intended command destination. This impacts if/how schema information is added. bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb, _mongocrypt_key_broker_t *kb, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status); // mc_translate_fields_keyAltName_to_keyId processes a "fields" array from encryptedFields, // translating keyAltName to keyId for each field document. // // @param fields_bson The fields array to process // @param kb The key broker to use for keyAltName to keyId translation // @param out The output array to append translated fields to // @param status Output status // @return -1 on error, 0 if keyAltName was not found, 1 on success int mc_translate_fields_keyAltName_to_keyId(const bson_t *fields_bson, _mongocrypt_key_broker_t *kb, bson_t *out, mongocrypt_status_t *status); #endif // MC_SCHEMA_BROKER_PRIVATE_H libmongocrypt-1.19.0/src/mc-schema-broker.c000066400000000000000000001276271521103432300205550ustar00rootroot00000000000000/* * Copyright 2018-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 "mc-schema-broker-private.h" #include "mc-efc-private.h" // mc_EncryptedFieldConfig_t #include "mongocrypt-buffer-private.h" #include "mongocrypt-cache-collinfo-private.h" #include "mongocrypt-key-broker-private.h" #include "mongocrypt-private.h" #include "mongocrypt-status-private.h" #include "mongocrypt-util-private.h" #include typedef struct mc_schema_entry_t { char *coll; struct { bool set; _mongocrypt_buffer_t buf; // Owns document. bson_t bson; // Non-owning view into buf. bool is_remote; } jsonSchema; struct { bool set; _mongocrypt_buffer_t buf; // Owns document. bson_t bson; // Non-owning view into buf. mc_EncryptedFieldConfig_t efc; } encryptedFields; struct mc_schema_entry_t *next; bool satisfied; // true once a schema is found and applied, or an empty schema is applied. } mc_schema_entry_t; // mc_schema_broker_t stores schemas for an auto encryption operation. struct mc_schema_broker_t { char *db; // Database shared by all schemas. mc_schema_entry_t *ll; size_t ll_len; bool schema_mixing_is_supported; }; mc_schema_broker_t *mc_schema_broker_new(void) { return bson_malloc0(sizeof(mc_schema_broker_t)); } bool mc_schema_broker_request(mc_schema_broker_t *sb, const char *db, const char *coll, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(db); BSON_ASSERT_PARAM(coll); if (sb->db && 0 != strcmp(sb->db, db)) { CLIENT_ERR("Cannot request schemas for different databases. Requested schemas for '%s' and '%s'.", sb->db, db); return false; } // Check for duplicates. Keep pointer to last node. mc_schema_entry_t *last = NULL; for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (0 == strcmp(it->coll, coll)) { return true; } last = it; } mc_schema_entry_t *se = bson_malloc0(sizeof *se); se->coll = bson_strdup(coll); if (NULL == last) { sb->ll = se; sb->db = bson_strdup(db); } else { last->next = se; } sb->ll_len++; return true; } bool mc_schema_broker_has_multiple_requests(const mc_schema_broker_t *sb) { BSON_ASSERT_PARAM(sb); return sb->ll_len > 1; } void mc_schema_broker_support_mixing_schemas(mc_schema_broker_t *sb) { BSON_ASSERT_PARAM(sb); sb->schema_mixing_is_supported = true; } void mc_schema_broker_destroy(mc_schema_broker_t *sb) { if (!sb) { return; } mc_schema_entry_t *it = sb->ll; while (it != NULL) { bson_free(it->coll); // Always clean it->encryptedFields and it->jsonSchema. May be partially set. mc_EncryptedFieldConfig_cleanup(&it->encryptedFields.efc); _mongocrypt_buffer_cleanup(&it->encryptedFields.buf); _mongocrypt_buffer_cleanup(&it->jsonSchema.buf); mc_schema_entry_t *tmp = it->next; bson_free(it); it = tmp; } bson_free(sb->db); bson_free(sb); return; } bool mc_schema_broker_has_any_qe_schemas(const mc_schema_broker_t *sb) { BSON_ASSERT_PARAM(sb); for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { BSON_ASSERT(se->satisfied); if (se->encryptedFields.set) { return true; } } return false; } // TRY_BSON_OR is a checks a BSON operation and sets a generic error status on failure. #define TRY_BSON_OR(stmt) if (!try_or(stmt, "BSON failure", BSON_FUNC, status)) static bool try_or(bool val, const char *msg, const char *func, mongocrypt_status_t *status) { if (!val) { CLIENT_ERR("[%s] statement failed: %s", func, msg); } return val; } bool mc_schema_broker_append_listCollections_filter(const mc_schema_broker_t *sb, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(out); size_t num_unsatisfied = 0; for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { if (!se->satisfied) { num_unsatisfied++; } } if (num_unsatisfied == 0) { CLIENT_ERR("Unexpected: attempting to create listCollections filter but no schemas needed"); return false; } else if (num_unsatisfied == 1) { // One request. Append as: { "name": } for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { if (se->satisfied) { continue; } TRY_BSON_OR(BSON_APPEND_UTF8(out, "name", se->coll)) { return false; } break; } } else { // Multiple requests. Append as: { "name": { "$in": [ , , ... ] } } bson_t in, in_array; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(out, "name", &in)) { return false; } TRY_BSON_OR(BSON_APPEND_ARRAY_UNSAFE_BEGIN(&in, "$in", &in_array)) { return false; } size_t idx = 0; for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { if (se->satisfied) { continue; } char idx_str[32]; int ret = bson_snprintf(idx_str, sizeof idx_str, "%zu", idx); BSON_ASSERT(ret > 0 && ret < (int)sizeof idx_str); TRY_BSON_OR(BSON_APPEND_UTF8(&in_array, idx_str, se->coll)) { return false; } idx++; } TRY_BSON_OR(bson_append_array_end(&in, &in_array)) { return false; } TRY_BSON_OR(bson_append_document_end(out, &in)) { return false; } } return true; } static inline bool mc_schema_entry_satisfy_from_collinfo(mc_schema_entry_t *se, const bson_t *collinfo, const char *coll, const char *db, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(se); BSON_ASSERT_PARAM(collinfo); BSON_ASSERT_PARAM(coll); BSON_ASSERT_PARAM(db); if (se->satisfied) { CLIENT_ERR("unexpected duplicate collinfo result for collection: %s.%s", db, coll); return false; } bson_iter_t collinfo_iter; if (!bson_iter_init(&collinfo_iter, collinfo)) { CLIENT_ERR("failed to iterate collinfo for collection: %s.%s", db, coll); return false; } // Disallow views. bson_iter_t type_iter = collinfo_iter; if (bson_iter_find(&type_iter, "type") && BSON_ITER_HOLDS_UTF8(&type_iter) && bson_iter_utf8(&type_iter, NULL) && 0 == strcmp("view", bson_iter_utf8(&type_iter, NULL))) { CLIENT_ERR("cannot auto encrypt a view: %s.%s", db, coll); return false; } // Check if collection is configured for QE. bson_iter_t encryptedFields_iter = collinfo_iter; if (bson_iter_find_descendant(&encryptedFields_iter, "options.encryptedFields", &encryptedFields_iter)) { if (!BSON_ITER_HOLDS_DOCUMENT(&encryptedFields_iter)) { CLIENT_ERR("expected document for `options.encryptedFields` but got %s for collection %s.%s", mc_bson_type_to_string(bson_iter_type(&encryptedFields_iter)), db, coll); return false; } if (!_mongocrypt_buffer_copy_from_document_iter(&se->encryptedFields.buf, &encryptedFields_iter)) { CLIENT_ERR("failed to copy `options.encryptedFields` for collection: %s.%s", db, coll); return false; } if (!_mongocrypt_buffer_to_bson(&se->encryptedFields.buf, &se->encryptedFields.bson)) { CLIENT_ERR("unable to create BSON from `options.encryptedFields` for collection: %s.%s", db, coll); return false; } if (!mc_EncryptedFieldConfig_parse(&se->encryptedFields.efc, &se->encryptedFields.bson, status)) { return false; } se->encryptedFields.set = true; } // Check if collection is configured for CSFLE. bool found_jsonSchema = false; bson_iter_t validator_iter = collinfo_iter; if (bson_iter_find_descendant(&validator_iter, "options.validator", &validator_iter) && BSON_ITER_HOLDS_DOCUMENT(&validator_iter)) { if (!bson_iter_recurse(&validator_iter, &validator_iter)) { CLIENT_ERR("failed to iterate `options.validator` for collection: %s.%s", db, coll); return false; } while (bson_iter_next(&validator_iter)) { const char *key = bson_iter_key(&validator_iter); if (0 == strcmp("$jsonSchema", key)) { if (found_jsonSchema) { CLIENT_ERR("duplicate `$jsonSchema` fields found for collection: %s.%s", db, coll); return false; } if (!_mongocrypt_buffer_copy_from_document_iter(&se->jsonSchema.buf, &validator_iter)) { CLIENT_ERR("unable to copy `$jsonSchema` for collection: %s.%s", db, coll); return false; } if (!_mongocrypt_buffer_to_bson(&se->jsonSchema.buf, &se->jsonSchema.bson)) { CLIENT_ERR("unable to create BSON from `$jsonSchema` for collection: %s.%s", db, coll); return false; } found_jsonSchema = true; BSON_ASSERT(!se->jsonSchema.set); se->jsonSchema.set = true; se->jsonSchema.is_remote = true; } } } se->satisfied = true; return true; } bool mc_schema_broker_satisfy_from_collinfo(mc_schema_broker_t *sb, const bson_t *collinfo, _mongocrypt_cache_t *collinfo_cache, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(collinfo); BSON_ASSERT_PARAM(collinfo_cache); bson_iter_t collinfo_iter; if (!bson_iter_init(&collinfo_iter, collinfo)) { CLIENT_ERR("failed to iterate collinfo in database: %s", sb->db); return false; } // Parse the collection from the `collinfo`. const char *coll; { bson_iter_t name_iter = collinfo_iter; if (!bson_iter_find(&name_iter, "name") || !BSON_ITER_HOLDS_UTF8(&name_iter)) { CLIENT_ERR("failed to find 'name' in collinfo in database: %s", sb->db); return false; } coll = bson_iter_utf8(&name_iter, NULL); } // Cache the received collinfo. { char *ns = bson_strdup_printf("%s.%s", sb->db, coll); if (!_mongocrypt_cache_add_copy(collinfo_cache, ns, (void *)collinfo, status)) { bson_free(ns); return false; } bson_free(ns); } // Find matching entry. mc_schema_entry_t *se = NULL; { for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (0 == strcmp(it->coll, coll)) { se = it; break; } } if (!se) { CLIENT_ERR("got unexpected collinfo result for collection: %s.%s", sb->db, coll); return false; } } if (!mc_schema_entry_satisfy_from_collinfo(se, collinfo, coll, sb->db, status)) { return false; } return true; } bool mc_schema_broker_satisfy_from_schemaMap(mc_schema_broker_t *sb, const bson_t *schema_map, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(schema_map); for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (it->satisfied) { continue; } bool loop_ok = false; char *ns = bson_strdup_printf("%s.%s", sb->db, it->coll); bson_iter_t iter; if (bson_iter_init_find(&iter, schema_map, ns)) { if (!_mongocrypt_buffer_copy_from_document_iter(&it->jsonSchema.buf, &iter)) { CLIENT_ERR("failed to read schema from schema map for collection: %s", ns); goto loop_fail; } if (!_mongocrypt_buffer_to_bson(&it->jsonSchema.buf, &it->jsonSchema.bson)) { CLIENT_ERR("unable to create BSON from schema map for collection: %s", ns); goto loop_fail; } BSON_ASSERT(!it->jsonSchema.set); it->jsonSchema.set = true; it->jsonSchema.is_remote = false; it->satisfied = true; } loop_ok = true; loop_fail: bson_free(ns); if (!loop_ok) { return false; } } return true; } bool mc_schema_broker_satisfy_from_encryptedFieldsMap(mc_schema_broker_t *sb, const bson_t *ef_map, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(ef_map); for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (it->satisfied) { continue; } bool loop_ok = false; char *ns = bson_strdup_printf("%s.%s", sb->db, it->coll); bson_iter_t iter; if (bson_iter_init_find(&iter, ef_map, ns)) { if (!_mongocrypt_buffer_copy_from_document_iter(&it->encryptedFields.buf, &iter)) { CLIENT_ERR("failed to read encryptedFields from encryptedFields map for collection: %s", ns); goto loop_fail; } if (!_mongocrypt_buffer_to_bson(&it->encryptedFields.buf, &it->encryptedFields.bson)) { CLIENT_ERR("failed to create BSON from encryptedFields map for collection: %s", ns); goto loop_fail; } if (!mc_EncryptedFieldConfig_parse(&it->encryptedFields.efc, &it->encryptedFields.bson, status)) { goto loop_fail; } BSON_ASSERT(!it->encryptedFields.set); it->encryptedFields.set = true; it->satisfied = true; } loop_ok = true; loop_fail: bson_free(ns); if (!loop_ok) { return false; } } return true; } bool mc_schema_broker_satisfy_from_cache(mc_schema_broker_t *sb, _mongocrypt_cache_t *listCollections_cache, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(listCollections_cache); for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (it->satisfied) { continue; } char *ns = bson_strdup_printf("%s.%s", sb->db, it->coll); // Check if there is a listCollections result cached. bool loop_ok = false; bson_t *collinfo = NULL; if (!_mongocrypt_cache_get(listCollections_cache, ns, (void **)&collinfo)) { CLIENT_ERR("failed to retrieve from listCollections cache for entry: %s", ns); goto loop_fail; } if (!collinfo) { goto loop_skip; } if (!mc_schema_entry_satisfy_from_collinfo(it, collinfo, sb->db, it->coll, status)) { goto loop_fail; } loop_skip: loop_ok = true; loop_fail: bson_destroy(collinfo); bson_free(ns); if (!loop_ok) { return false; } } return true; } bool mc_schema_broker_satisfy_from_create_or_collMod(mc_schema_broker_t *sb, const bson_t *cmd, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(cmd); bson_iter_t iter; if (!bson_iter_init(&iter, cmd) || !bson_iter_next(&iter)) { CLIENT_ERR("Failed to get command name"); return false; } const char *cmd_name = bson_iter_key(&iter); if (0 != strcmp(cmd_name, "create") && 0 != strcmp(cmd_name, "collMod")) { // Ignore other commands. return true; } if (!BSON_ITER_HOLDS_UTF8(&iter)) { CLIENT_ERR("Failed to get collection name from command"); return false; } const char *coll = bson_iter_utf8(&iter, NULL); // Check if schema was requested. mc_schema_entry_t *found = NULL; for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (0 == strcmp(it->coll, coll)) { found = it; break; } } if (!found) { // Command is for a collection that is not needed. return true; } if (found->satisfied) { return true; } if (bson_iter_find_descendant(&iter, "validator.$jsonSchema", &iter)) { if (!_mongocrypt_buffer_copy_from_document_iter(&found->jsonSchema.buf, &iter)) { CLIENT_ERR("failed to read schema from schema map for collection: %s", coll); return false; } if (!_mongocrypt_buffer_to_bson(&found->jsonSchema.buf, &found->jsonSchema.bson)) { CLIENT_ERR("unable to create BSON from schema map for collection: %s", coll); return false; } found->jsonSchema.set = true; found->jsonSchema.is_remote = true; // Mark remote. Schema may have non-CSFLE validators. found->satisfied = true; return true; } if (bson_iter_init_find(&iter, cmd, "encryptedFields")) { if (!_mongocrypt_buffer_copy_from_document_iter(&found->encryptedFields.buf, &iter)) { CLIENT_ERR("failed to read encryptedFields from command: %s", cmd_name); return false; } if (!_mongocrypt_buffer_to_bson(&found->encryptedFields.buf, &found->encryptedFields.bson)) { CLIENT_ERR("unable to create BSON from encryptedFields for command: %s", cmd_name); return false; } if (!mc_EncryptedFieldConfig_parse(&found->encryptedFields.efc, &found->encryptedFields.bson, status)) { return false; } found->encryptedFields.set = true; found->satisfied = true; return true; } // Command does not have a schema. Not an error. return true; } bool mc_schema_broker_satisfy_remaining_with_empty_schemas(mc_schema_broker_t *sb, _mongocrypt_cache_t *collinfo_cache /* may be NULL */, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (it->satisfied) { continue; } // Cache the received collinfo. if (collinfo_cache) { char *ns = bson_strdup_printf("%s.%s", sb->db, it->coll); bson_t empty = BSON_INITIALIZER; if (!_mongocrypt_cache_add_copy(collinfo_cache, ns, &empty, status)) { bson_destroy(&empty); bson_free(ns); return false; } bson_destroy(&empty); bson_free(ns); } it->satisfied = true; } return true; } bool mc_schema_broker_need_more_schemas(const mc_schema_broker_t *sb) { BSON_ASSERT_PARAM(sb); for (const mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (!it->satisfied) { return true; } } return false; } const mc_EncryptedFieldConfig_t * mc_schema_broker_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(coll); for (const mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { BSON_ASSERT(it->satisfied); if (0 != strcmp(it->coll, coll)) { continue; } if (!it->encryptedFields.set) { CLIENT_ERR("Expected encryptedFields for '%s', but none set", coll); return NULL; } return &it->encryptedFields.efc; } CLIENT_ERR("Expected encryptedFields for '%s', but did not find entry", coll); return NULL; } const mc_EncryptedFieldConfig_t *mc_schema_broker_maybe_get_encryptedFields(const mc_schema_broker_t *sb, const char *coll, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(coll); for (const mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { if (0 != strcmp(it->coll, coll)) { continue; } if (!it->encryptedFields.set) { return NULL; } return &it->encryptedFields.efc; } return NULL; } // translate_field_keyAltName_to_keyId translates one field. Returns false on error. static bool translate_field_keyAltName_to_keyId(const bson_t *old_doc, bson_t *new_doc, _mongocrypt_key_broker_t *kb, int *found_keyAltName, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(old_doc); BSON_ASSERT_PARAM(new_doc); BSON_ASSERT_PARAM(found_keyAltName); BSON_ASSERT_PARAM(kb); _mongocrypt_buffer_t unused = {0}, key_id_out = {0}; bson_value_t key_alt_name_v = {0}; bool ret = false; // Copy excluding "keyAltName" bson_copy_to_excluding_noinit(old_doc, new_doc, "keyAltName", NULL); // Translate keyAltName (if present) to keyId bson_iter_t keyAltName_iter; if (bson_iter_init_find(&keyAltName_iter, old_doc, "keyAltName") && BSON_ITER_HOLDS_UTF8(&keyAltName_iter)) { *found_keyAltName = 1; _bson_value_from_string(bson_iter_utf8(&keyAltName_iter, NULL), &key_alt_name_v); if (!_mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name_v, &unused, &key_id_out)) { _mongocrypt_key_broker_status(kb, status); goto fail; } TRY_BSON_OR(bson_append_binary(new_doc, "keyId", -1, key_id_out.subtype, key_id_out.data, key_id_out.len)) { goto fail; } } ret = true; fail: _mongocrypt_buffer_cleanup(&unused); _mongocrypt_buffer_cleanup(&key_id_out); bson_value_destroy(&key_alt_name_v); return ret; } int mc_translate_fields_keyAltName_to_keyId(const bson_t *fields_bson, _mongocrypt_key_broker_t *kb, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(fields_bson); BSON_ASSERT_PARAM(kb); BSON_ASSERT_PARAM(out); int found_keyAltName = 0; bson_iter_t arr_it; if (!bson_iter_init(&arr_it, fields_bson)) { CLIENT_ERR("failed to iterate 'fields' array"); return -1; } while (bson_iter_next(&arr_it)) { const char *idx_str = bson_iter_key(&arr_it); if (BSON_ITER_HOLDS_DOCUMENT(&arr_it)) { bson_t elem_doc; if (!mc_iter_document_as_bson(&arr_it, &elem_doc, status)) { return -1; } bson_t new_doc = BSON_INITIALIZER; if (!translate_field_keyAltName_to_keyId(&elem_doc, &new_doc, kb, &found_keyAltName, status)) { bson_destroy(&new_doc); return -1; } TRY_BSON_OR(bson_append_document(out, idx_str, -1, &new_doc)) { bson_destroy(&new_doc); return -1; } bson_destroy(&new_doc); } else { /* Non-document elements: copy as-is. */ if (!BSON_APPEND_VALUE(out, idx_str, bson_iter_value(&arr_it))) { CLIENT_ERR("failed to append field value"); return -1; } } } return found_keyAltName; } static bool append_encryptedFields(const bson_t *encryptedFields, _mongocrypt_key_broker_t *kb, const char *coll, uint8_t default_strEncodeVersion, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(encryptedFields); // May be an empty BSON document. BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(coll); bool ok = false; bool has_escCollection = false; bool has_ecocCollection = false; bool has_fields = false; bool has_strEncodeVersion = false; char *default_escCollection = NULL; char *default_ecocCollection = NULL; bson_iter_t iter; TRY_BSON_OR(bson_iter_init(&iter, encryptedFields)) { goto fail; } // Copy all values. Check if state collections are present. while (bson_iter_next(&iter)) { const char *iter_key = bson_iter_key(&iter); if (strcmp(iter_key, "escCollection") == 0) { has_escCollection = true; } if (strcmp(bson_iter_key(&iter), "ecocCollection") == 0) { has_ecocCollection = true; } if (strcmp(bson_iter_key(&iter), "fields") == 0) { has_fields = true; } if (strcmp(bson_iter_key(&iter), "strEncodeVersion") == 0) { has_strEncodeVersion = true; } /* Special-case the "fields" array: translate any "keyAltName" to "keyId" */ if (0 == strcmp(iter_key, "fields") && BSON_ITER_HOLDS_ARRAY(&iter) && kb) { uint32_t array_len = 0; const uint8_t *array_data = NULL; bson_t array_bson; bson_iter_array(&iter, &array_len, &array_data); TRY_BSON_OR(bson_init_static(&array_bson, array_data, array_len)) { goto fail; } bson_t new_array; TRY_BSON_OR(BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, "fields", &new_array)) { goto fail; } const int translated_keyAltName = mc_translate_fields_keyAltName_to_keyId(&array_bson, kb, &new_array, status); if (translated_keyAltName == -1) { bson_append_array_end(out, &new_array); goto fail; } TRY_BSON_OR(bson_append_array_end(out, &new_array)) { goto fail; } } else { TRY_BSON_OR(BSON_APPEND_VALUE(out, iter_key, bson_iter_value(&iter))) { goto fail; } } } if (!has_escCollection) { default_escCollection = bson_strdup_printf("enxcol_.%s.esc", coll); TRY_BSON_OR(BSON_APPEND_UTF8(out, "escCollection", default_escCollection)) { goto fail; } } if (!has_ecocCollection) { default_ecocCollection = bson_strdup_printf("enxcol_.%s.ecoc", coll); TRY_BSON_OR(BSON_APPEND_UTF8(out, "ecocCollection", default_ecocCollection)) { goto fail; } } if (!has_fields) { bson_t empty = BSON_INITIALIZER; TRY_BSON_OR(BSON_APPEND_ARRAY(out, "fields", &empty)) { goto fail; } } if (!has_strEncodeVersion && default_strEncodeVersion != 0) { // 0 indicates encryptedFields has no text fields and no set strEncodeVersion. // Only add strEncodeVersion if needed since older server components do not recognize it. TRY_BSON_OR(BSON_APPEND_INT32(out, "strEncodeVersion", (int32_t)default_strEncodeVersion)) { goto fail; } } ok = true; fail: bson_free(default_escCollection); bson_free(default_ecocCollection); return ok; } static bool append_encryptionInformation(const mc_schema_broker_t *sb, _mongocrypt_key_broker_t *kb, const char *cmd_name, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(cmd_name); BSON_ASSERT_PARAM(out); bson_t encryption_information_bson; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(out, "encryptionInformation", &encryption_information_bson)) { return false; } TRY_BSON_OR(BSON_APPEND_INT32(&encryption_information_bson, "type", 1)) { return false; } bson_t schema_bson; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(&encryption_information_bson, "schema", &schema_bson)) { return false; } for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { // encryptedFields is preferred over jsonSchema if (!se->encryptedFields.set && se->jsonSchema.set) { continue; } BSON_ASSERT(se->satisfied); bool loop_ok = false; char *ns = bson_strdup_printf("%s.%s", sb->db, se->coll); bson_t ns_to_schema_bson; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(&schema_bson, ns, &ns_to_schema_bson)) { goto loop_fail; } bson_t empty_encryptedFields = BSON_INITIALIZER; // Use empty encryptedFields if none set. const bson_t *encryptedFields = &empty_encryptedFields; uint8_t default_strEncodeVersion = 0; if (se->encryptedFields.set) { encryptedFields = &se->encryptedFields.bson; default_strEncodeVersion = se->encryptedFields.efc.str_encode_version; } if (!append_encryptedFields(encryptedFields, kb, se->coll, default_strEncodeVersion, &ns_to_schema_bson, status)) { goto loop_fail; } TRY_BSON_OR(bson_append_document_end(&schema_bson, &ns_to_schema_bson)) { goto loop_fail; } loop_ok = true; loop_fail: bson_free(ns); bson_destroy(&empty_encryptedFields); if (!loop_ok) { return false; } } TRY_BSON_OR(bson_append_document_end(&encryption_information_bson, &schema_bson)) { return false; } TRY_BSON_OR(bson_append_document_end(out, &encryption_information_bson)) { return false; } return true; } static const char *get_cmd_name(const bson_t *cmd, mongocrypt_status_t *status) { bson_iter_t iter; const char *cmd_name; BSON_ASSERT_PARAM(cmd); if (!bson_iter_init(&iter, cmd)) { CLIENT_ERR("unable to iterate over command BSON"); return NULL; } /* The command name is the first key. */ if (!bson_iter_next(&iter)) { CLIENT_ERR("unexpected empty BSON for command"); return NULL; } cmd_name = bson_iter_key(&iter); if (!cmd_name) { CLIENT_ERR("unable to get command name from BSON"); return NULL; } return cmd_name; } static bool insert_encryptionInformation(const mc_schema_broker_t *sb, _mongocrypt_key_broker_t *kb, const char *cmd_name, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(cmd_name); BSON_ASSERT_PARAM(cmd); bson_t out = BSON_INITIALIZER; bson_t explain = BSON_INITIALIZER; bson_iter_t iter; bool ok = false; // For `bulkWrite`, append `encryptionInformation` inside the `nsInfo.0` document. if (0 == strcmp(cmd_name, "bulkWrite")) { // Get the single `nsInfo` document from the input command. bson_t nsInfo; // Non-owning. { bson_iter_t nsInfo_iter; if (!bson_iter_init(&nsInfo_iter, cmd)) { CLIENT_ERR("failed to iterate command"); goto fail; } if (!bson_iter_find_descendant(&nsInfo_iter, "nsInfo.0", &nsInfo_iter)) { CLIENT_ERR("expected one namespace in `bulkWrite`, but found zero."); goto fail; } if (bson_has_field(cmd, "nsInfo.1")) { CLIENT_ERR("expected one namespace in `bulkWrite`, but found more than one. Only one namespace is " "supported."); goto fail; } if (!mc_iter_document_as_bson(&nsInfo_iter, &nsInfo, status)) { goto fail; } // Ensure `nsInfo` does not already have an `encryptionInformation` field. if (bson_has_field(&nsInfo, "encryptionInformation")) { CLIENT_ERR("unexpected `encryptionInformation` present in input `nsInfo`."); goto fail; } } // Copy input and append `encryptionInformation` to `nsInfo`. { // Append everything from input except `nsInfo`. bson_copy_to_excluding_noinit(cmd, &out, "nsInfo", NULL); // Append `nsInfo` array. bson_t nsInfo_array; if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(&out, "nsInfo", &nsInfo_array)) { CLIENT_ERR("unable to begin appending 'nsInfo' array"); goto fail; } bson_t nsInfo_array_0; if (!BSON_APPEND_DOCUMENT_BEGIN(&nsInfo_array, "0", &nsInfo_array_0)) { CLIENT_ERR("unable to append 'nsInfo.0' document"); goto fail; } // Copy everything from input `nsInfo`. if (!bson_concat(&nsInfo_array_0, &nsInfo)) { CLIENT_ERR("unable to concat to 'nsInfo.0' document"); goto fail; } // And append `encryptionInformation`. if (!append_encryptionInformation(sb, kb, cmd_name, &nsInfo_array_0, status)) { goto fail; } if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) { CLIENT_ERR("unable to end appending 'nsInfo' document in array"); } if (!bson_append_array_end(&out, &nsInfo_array)) { CLIENT_ERR("unable to end appending 'nsInfo' array"); goto fail; } // Overwrite `cmd`. bson_destroy(cmd); if (!bson_steal(cmd, &out)) { CLIENT_ERR("failed to steal BSON with encryptionInformation"); goto fail; } } goto success; } if (0 == strcmp(cmd_name, "explain") && (cmd_target == MC_CMD_SCHEMAS_FOR_CRYPT_SHARED || cmd_target == MC_CMD_SCHEMAS_FOR_SERVER)) { // The "explain" command is a special case when sent to crypt_shared or the server, which expect // "encryptionInformation" nested in the "explain" document instead of at top-level. Example: // { // "explain": { // "find": "to-crypt_shared-or-server" // "encryptionInformation": {} // } // } if (!bson_iter_init_find(&iter, cmd, "explain")) { CLIENT_ERR("expected to find 'explain' field"); goto fail; } if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) { CLIENT_ERR("expected 'explain' to be document"); goto fail; } { bson_t tmp; if (!mc_iter_document_as_bson(&iter, &tmp, status)) { goto fail; } bson_destroy(&explain); bson_copy_to(&tmp, &explain); } if (!append_encryptionInformation(sb, kb, cmd_name, &explain, status)) { goto fail; } if (!BSON_APPEND_DOCUMENT(&out, "explain", &explain)) { CLIENT_ERR("unable to append 'explain' document"); goto fail; } bson_copy_to_excluding_noinit(cmd, &out, "explain", NULL); bson_destroy(cmd); if (!bson_steal(cmd, &out)) { CLIENT_ERR("failed to steal BSON with encryptionInformation"); goto fail; } goto success; } // Default case: append "encryptionInformation" at top-level. Example: // { // "": { ... } // "encryptionInformation": {} // } if (!append_encryptionInformation(sb, kb, cmd_name, cmd, status)) { goto fail; } success: ok = true; fail: bson_destroy(&explain); if (!ok) { bson_destroy(&out); } return ok; } static bool any_entry_includes_encryptedFields(mc_schema_entry_t *head) { for (mc_schema_entry_t *se = head; se != NULL; se = se->next) { if (se->encryptedFields.set) { return true; } } return false; } // insert_csfleEncryptionSchemas appends schema information to a command for CSFLE. // Only consumed by query analysis (mongocryptd/crypt_shared). // For one JSON schema, use `jsonSchema` for backwards compatibility. // For multiple JSON schemas, use `csfleEncryptionSchemas` (added in server 8.1). static bool insert_csfleEncryptionSchemas(const mc_schema_broker_t *sb, bson_t *cmd /* in/out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(cmd); if (cmd_target != MC_CMD_SCHEMAS_FOR_CRYPT_SHARED && cmd_target != MC_CMD_SCHEMAS_FOR_MONGOCRYPTD) { // CSFLE schemas are only used for query analysis. return true; } if (sb->ll_len == 1) { // Append the only jsonSchema with the "jsonSchema" field. const mc_schema_entry_t *se = sb->ll; BSON_ASSERT(se); BSON_ASSERT(!se->next); BSON_ASSERT(se->satisfied); if (se->jsonSchema.set) { TRY_BSON_OR(BSON_APPEND_DOCUMENT(cmd, "jsonSchema", &se->jsonSchema.bson)) { return false; } TRY_BSON_OR(BSON_APPEND_BOOL(cmd, "isRemoteSchema", se->jsonSchema.is_remote)) { return false; } } else { bson_t empty = BSON_INITIALIZER; TRY_BSON_OR(BSON_APPEND_DOCUMENT(cmd, "jsonSchema", &empty)) { return false; } // Append isRemoteSchema:true to preserve existing value. But I expect it can/should be false. TRY_BSON_OR(BSON_APPEND_BOOL(cmd, "isRemoteSchema", true)) { return false; } } return true; } // Append multiple schemas as "csfleEncryptionSchemas" bson_t csfleEncryptionSchemas; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(cmd, "csfleEncryptionSchemas", &csfleEncryptionSchemas)) { return false; } const bool skip_empty_schemas = any_entry_includes_encryptedFields(sb->ll); for (mc_schema_entry_t *se = sb->ll; se != NULL; se = se->next) { if (se->encryptedFields.set || (!se->jsonSchema.set && skip_empty_schemas)) { continue; } BSON_ASSERT(se->satisfied); char *ns = bson_strdup_printf("%s.%s", sb->db, se->coll); bson_t ns_to_doc; TRY_BSON_OR(BSON_APPEND_DOCUMENT_BEGIN(&csfleEncryptionSchemas, ns, &ns_to_doc)) { bson_free(ns); return false; } bson_free(ns); if (!se->jsonSchema.set) { // Append as an empty document. bson_t empty = BSON_INITIALIZER; TRY_BSON_OR(BSON_APPEND_DOCUMENT(&ns_to_doc, "jsonSchema", &empty)) { return false; } TRY_BSON_OR(BSON_APPEND_BOOL(&ns_to_doc, "isRemoteSchema", false)) { return false; } } else { TRY_BSON_OR(BSON_APPEND_DOCUMENT(&ns_to_doc, "jsonSchema", &se->jsonSchema.bson)) { return false; } TRY_BSON_OR(BSON_APPEND_BOOL(&ns_to_doc, "isRemoteSchema", se->jsonSchema.is_remote)) { return false; } } TRY_BSON_OR(bson_append_document_end(&csfleEncryptionSchemas, &ns_to_doc)) { return false; } } TRY_BSON_OR(bson_append_document_end(cmd, &csfleEncryptionSchemas)) { return false; } return true; } bool mc_schema_broker_add_schemas_to_cmd(mc_schema_broker_t *sb, _mongocrypt_key_broker_t *kb, bson_t *cmd /* in and out */, mc_cmd_target_t cmd_target, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(sb); BSON_ASSERT_PARAM(cmd); const char *cmd_name = get_cmd_name(cmd, status); if (!cmd_name) { return false; } bool has_encryptedFields = false; bool has_jsonSchema = false; const char *coll_with_encryptedFields = NULL; const char *coll_with_jsonSchema = NULL; for (mc_schema_entry_t *it = sb->ll; it != NULL; it = it->next) { BSON_ASSERT(it->satisfied); if (it->encryptedFields.set) { has_encryptedFields = true; coll_with_encryptedFields = it->coll; for (mc_EncryptedField_t *f = it->encryptedFields.efc.fields; f != NULL; f = f->next) { if (f->keyAltName && _mongocrypt_buffer_empty(&f->keyId)) { bson_value_t key_alt_name; _mongocrypt_buffer_t unused; _bson_value_from_string(f->keyAltName, &key_alt_name); const bool r = _mongocrypt_key_broker_decrypted_key_by_name(kb, &key_alt_name, &unused, &f->keyId); bson_value_destroy(&key_alt_name); _mongocrypt_buffer_cleanup(&unused); if (!r) { CLIENT_ERR("Could not find key by keyAltName: %s", f->keyAltName); return false; } } } } else if (it->jsonSchema.set) { has_jsonSchema = true; coll_with_jsonSchema = it->coll; } } if (has_encryptedFields && has_jsonSchema) { if (sb->schema_mixing_is_supported) { return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status) && insert_csfleEncryptionSchemas(sb, cmd, cmd_target, status); } CLIENT_ERR("Collection '%s' has an encryptedFields configured, but collection '%s' has a JSON schema " "configured. This is not supported on mongocryptd/crypt_shared versions below 8.2. To ignore the " "JSON schema, add an empty entry for '%s' to AutoEncryptionOpts.encryptedFieldsMap: \"%s\": { " "\"fields\": [] }", coll_with_encryptedFields, coll_with_jsonSchema, coll_with_jsonSchema, coll_with_jsonSchema); return false; } if (has_encryptedFields) { // Use encryptionInformation. return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status); } if (has_jsonSchema) { // Use csfleEncryptionSchemas / jsonSchema only. return insert_csfleEncryptionSchemas(sb, cmd, cmd_target, status); } // Collections have no QE or CSFLE schemas. if (0 == strcmp(cmd_name, "bulkWrite")) { // "bulkWrite" does not support the jsonSchema field. Use encryptionInformation with empty schemas. return insert_encryptionInformation(sb, kb, cmd_name, cmd, cmd_target, status); } // Use csfleEncryptionSchemas / jsonSchema with empty schemas. return insert_csfleEncryptionSchemas(sb, cmd, cmd_target, status); } libmongocrypt-1.19.0/src/mc-str-encode-string-sets-private.h000066400000000000000000000100601521103432300240110ustar00rootroot00000000000000/* * Copyright 2024-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. */ #ifndef MONGOCRYPT_STR_ENCODE_STRING_SETS_PRIVATE_H #define MONGOCRYPT_STR_ENCODE_STRING_SETS_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" // Represents a valid unicode string with the bad character 0xFF appended to the end. This is our base string which // we build substring trees on. Stores all the valid code points in the string, plus one code point for 0xFF. // Exposed for testing. typedef struct { _mongocrypt_buffer_t buf; uint32_t *codepoint_offsets; uint32_t codepoint_len; } mc_utf8_string_with_bad_char_t; // Initialize by copying buffer into data and adding the bad character. mc_utf8_string_with_bad_char_t *mc_utf8_string_with_bad_char_from_buffer(const char *buf, uint32_t len); void mc_utf8_string_with_bad_char_destroy(mc_utf8_string_with_bad_char_t *utf8); // Set of affixes of a shared base string. Does not do any duplicate prevention. typedef struct _mc_affix_set_t mc_affix_set_t; // Initialize affix set from base string and number of entries (this must be known as a prior). mc_affix_set_t *mc_affix_set_new(const mc_utf8_string_with_bad_char_t *base_string, uint32_t n_indices); void mc_affix_set_destroy(mc_affix_set_t *set); // Insert affix into set. base_start/end_idx are codepoint indices. base_end_idx is exclusive. Returns true if // inserted, false otherwise. bool mc_affix_set_insert(mc_affix_set_t *set, uint32_t base_start_idx, uint32_t base_end_idx); // Insert the base string count times into the set. Treated as a special case, since this is the only affix that // will appear multiple times. Returns true if inserted, false otherwise. bool mc_affix_set_insert_base_string(mc_affix_set_t *set, uint32_t count); // Iterator on affix set. typedef struct { mc_affix_set_t *set; uint32_t cur_idx; } mc_affix_set_iter_t; // Point the iterator to the first affix of the given set. void mc_affix_set_iter_init(mc_affix_set_iter_t *it, mc_affix_set_t *set); // Get the next affix, its length in bytes, and its count. Returns false if the set does not have a next element, true // otherwise. bool mc_affix_set_iter_next(mc_affix_set_iter_t *it, const char **str, uint32_t *byte_len, uint32_t *count); // Set of substrings of a shared base string. Prevents duplicates. typedef struct _mc_substring_set_t mc_substring_set_t; mc_substring_set_t *mc_substring_set_new(const mc_utf8_string_with_bad_char_t *base_string); void mc_substring_set_destroy(mc_substring_set_t *set); // Insert the base string count times into the set. Treated as a special case, since this is the only substring that // will appear multiple times. Always inserts successfully. void mc_substring_set_increment_fake_string(mc_substring_set_t *set, uint32_t count); // Insert substring into set. base_start/end_idx are codepoint indices. base_end_idx is exclusive. Returns true if // inserted, false otherwise. bool mc_substring_set_insert(mc_substring_set_t *set, uint32_t base_start_idx, uint32_t base_end_idx); // Iterator on substring set. typedef struct { mc_substring_set_t *set; void *cur_node; uint32_t cur_idx; } mc_substring_set_iter_t; // Point the iterator to the first substring of the given set. void mc_substring_set_iter_init(mc_substring_set_iter_t *it, mc_substring_set_t *set); // Get the next substring, its length in bytes, and its count. Returns false if the set does not have a next element, // true otherwise. bool mc_substring_set_iter_next(mc_substring_set_iter_t *it, const char **str, uint32_t *byte_len, uint32_t *count); #endif libmongocrypt-1.19.0/src/mc-str-encode-string-sets.c000066400000000000000000000245751521103432300223540ustar00rootroot00000000000000/* * Copyright 2024-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 "mc-str-encode-string-sets-private.h" #include "mongocrypt-buffer-private.h" #include #include #define BAD_CHAR ((uint8_t)0xFF) // Input must be pre-validated by bson_utf8_validate(). mc_utf8_string_with_bad_char_t *mc_utf8_string_with_bad_char_from_buffer(const char *buf, uint32_t len) { BSON_ASSERT_PARAM(buf); mc_utf8_string_with_bad_char_t *ret = bson_malloc0(sizeof(mc_utf8_string_with_bad_char_t)); _mongocrypt_buffer_init_size(&ret->buf, len + 1); memcpy(ret->buf.data, buf, len); ret->buf.data[len] = BAD_CHAR; // max # offsets is the total length ret->codepoint_offsets = bson_malloc0(sizeof(uint32_t) * (len + 1)); const char *cur = buf; const char *end = buf + len; ret->codepoint_len = 0; while (cur < end) { ret->codepoint_offsets[ret->codepoint_len++] = (uint32_t)(cur - buf); cur = bson_utf8_next_char(cur); } // last codepoint points at the 0xFF at the end of the string ret->codepoint_offsets[ret->codepoint_len++] = (uint32_t)(end - buf); // realloc to save some space ret->codepoint_offsets = bson_realloc(ret->codepoint_offsets, sizeof(uint32_t) * ret->codepoint_len); return ret; } void mc_utf8_string_with_bad_char_destroy(mc_utf8_string_with_bad_char_t *utf8) { if (!utf8) { return; } bson_free(utf8->codepoint_offsets); _mongocrypt_buffer_cleanup(&utf8->buf); bson_free(utf8); } struct _mc_affix_set_t { // base_string is not owned const mc_utf8_string_with_bad_char_t *base_string; uint32_t *start_indices; uint32_t *end_indices; // Store counts per substring. As we expect heavy duplication of the padding value, this will save some time when we // hash later. uint32_t *substring_counts; uint32_t n_indices; uint32_t cur_idx; }; mc_affix_set_t *mc_affix_set_new(const mc_utf8_string_with_bad_char_t *base_string, uint32_t n_indices) { BSON_ASSERT_PARAM(base_string); mc_affix_set_t *set = (mc_affix_set_t *)bson_malloc0(sizeof(mc_affix_set_t)); set->base_string = base_string; set->start_indices = (uint32_t *)bson_malloc0(sizeof(uint32_t) * n_indices); set->end_indices = (uint32_t *)bson_malloc0(sizeof(uint32_t) * n_indices); set->substring_counts = (uint32_t *)bson_malloc0(sizeof(uint32_t) * n_indices); set->n_indices = n_indices; return set; } void mc_affix_set_destroy(mc_affix_set_t *set) { if (!set) { return; } bson_free(set->start_indices); bson_free(set->end_indices); bson_free(set->substring_counts); bson_free(set); } bool mc_affix_set_insert(mc_affix_set_t *set, uint32_t base_start_idx, uint32_t base_end_idx) { BSON_ASSERT_PARAM(set); if (base_start_idx > base_end_idx || base_end_idx >= set->base_string->codepoint_len || set->cur_idx >= set->n_indices) { return false; } uint32_t idx = set->cur_idx++; set->start_indices[idx] = base_start_idx; set->end_indices[idx] = base_end_idx; set->substring_counts[idx] = 1; return true; } bool mc_affix_set_insert_base_string(mc_affix_set_t *set, uint32_t count) { BSON_ASSERT_PARAM(set); if (count == 0 || set->cur_idx >= set->n_indices) { return false; } uint32_t idx = set->cur_idx++; set->start_indices[idx] = 0; set->end_indices[idx] = set->base_string->codepoint_len; set->substring_counts[idx] = count; return true; } void mc_affix_set_iter_init(mc_affix_set_iter_t *it, mc_affix_set_t *set) { BSON_ASSERT_PARAM(it); BSON_ASSERT_PARAM(set); it->set = set; it->cur_idx = 0; } bool mc_affix_set_iter_next(mc_affix_set_iter_t *it, const char **str, uint32_t *byte_len, uint32_t *count) { BSON_ASSERT_PARAM(it); if (it->cur_idx >= it->set->n_indices) { return false; } uint32_t idx = it->cur_idx++; uint32_t start_idx = it->set->start_indices[idx]; uint32_t end_idx = it->set->end_indices[idx]; uint32_t start_byte_offset = it->set->base_string->codepoint_offsets[start_idx]; // Pointing to the end of the codepoints represents the end of the string. uint32_t end_byte_offset = it->set->base_string->buf.len; if (end_idx != it->set->base_string->codepoint_len) { end_byte_offset = it->set->base_string->codepoint_offsets[end_idx]; } if (str) { *str = (const char *)it->set->base_string->buf.data + start_byte_offset; } if (byte_len) { *byte_len = end_byte_offset - start_byte_offset; } if (count) { *count = it->set->substring_counts[idx]; } return true; } // Linked list node in the hashset. typedef struct _mc_substring_set_node_t { uint32_t start_offset; uint32_t byte_len; struct _mc_substring_set_node_t *next; } mc_substring_set_node_t; static mc_substring_set_node_t *new_ssnode(uint32_t start_byte_offset, uint32_t byte_len) { mc_substring_set_node_t *ret = (mc_substring_set_node_t *)bson_malloc0(sizeof(mc_substring_set_node_t)); ret->start_offset = start_byte_offset; ret->byte_len = byte_len; return ret; } static void mc_substring_set_node_destroy(mc_substring_set_node_t *node) { if (!node) { return; } bson_free(node); } // FNV-1a hash function static const uint32_t FNV1APRIME = 16777619; static const uint32_t FNV1ABASIS = 2166136261; static uint32_t fnv1a(const uint8_t *data, uint32_t len) { BSON_ASSERT_PARAM(data); uint32_t hash = FNV1ABASIS; const uint8_t *ptr = data; while (ptr != data + len) { hash = (hash ^ (uint32_t)(*ptr++)) * FNV1APRIME; } return hash; } // A reasonable default, balancing space with speed #define HASHSET_SIZE 4096 struct _mc_substring_set_t { // base_string is not owned const mc_utf8_string_with_bad_char_t *base_string; mc_substring_set_node_t *set[HASHSET_SIZE]; uint32_t base_string_count; }; mc_substring_set_t *mc_substring_set_new(const mc_utf8_string_with_bad_char_t *base_string) { BSON_ASSERT_PARAM(base_string); mc_substring_set_t *set = (mc_substring_set_t *)bson_malloc0(sizeof(mc_substring_set_t)); set->base_string = base_string; return set; } void mc_substring_set_destroy(mc_substring_set_t *set) { if (!set) { return; } for (int i = 0; i < HASHSET_SIZE; i++) { mc_substring_set_node_t *node = set->set[i]; while (node) { mc_substring_set_node_t *to_destroy = node; node = node->next; mc_substring_set_node_destroy(to_destroy); } } bson_free(set); } void mc_substring_set_increment_fake_string(mc_substring_set_t *set, uint32_t count) { BSON_ASSERT_PARAM(set); set->base_string_count += count; } bool mc_substring_set_insert(mc_substring_set_t *set, uint32_t base_start_idx, uint32_t base_end_idx) { BSON_ASSERT_PARAM(set); BSON_ASSERT(base_start_idx <= base_end_idx); BSON_ASSERT(base_end_idx <= set->base_string->codepoint_len); uint32_t start_byte_offset = set->base_string->codepoint_offsets[base_start_idx]; uint32_t end_byte_offset = (base_end_idx == set->base_string->codepoint_len) ? set->base_string->buf.len : set->base_string->codepoint_offsets[base_end_idx]; const uint8_t *start = set->base_string->buf.data + start_byte_offset; uint32_t len = end_byte_offset - start_byte_offset; uint32_t hash = fnv1a(start, len); uint32_t idx = hash % HASHSET_SIZE; mc_substring_set_node_t *node = set->set[idx]; if (node) { // Traverse linked list to find match; if no match, insert at end of linked list. mc_substring_set_node_t *prev; while (node) { prev = node; if (len == node->byte_len && memcmp(start, set->base_string->buf.data + node->start_offset, len) == 0) { // Match, no insertion return false; } node = node->next; } // No matches, insert prev->next = new_ssnode(start_byte_offset, len); } else { // Create new node and put it in hashset set->set[idx] = new_ssnode(start_byte_offset, len); } return true; } void mc_substring_set_iter_init(mc_substring_set_iter_t *it, mc_substring_set_t *set) { BSON_ASSERT_PARAM(it); BSON_ASSERT_PARAM(set); it->set = set; it->cur_node = set->set[0]; it->cur_idx = 0; } bool mc_substring_set_iter_next(mc_substring_set_iter_t *it, const char **str, uint32_t *byte_len, uint32_t *count) { BSON_ASSERT_PARAM(it); if (it->cur_idx >= HASHSET_SIZE) { // No next. return false; } if (it->cur_node == NULL) { it->cur_idx++; // Next node is at another idx; iterate idx until we find a node. while (it->cur_idx < HASHSET_SIZE && !it->set->set[it->cur_idx]) { it->cur_idx++; } if (it->cur_idx >= HASHSET_SIZE) { // Almost done with iteration; return base string if count is not 0. if (it->set->base_string_count) { if (count) { *count = it->set->base_string_count; } if (str) { *str = (const char *)it->set->base_string->buf.data; } if (byte_len) { *byte_len = it->set->base_string->buf.len; } return true; } return false; } // Otherwise, we found a node; iterate to it. it->cur_node = it->set->set[it->cur_idx]; } mc_substring_set_node_t *cur = (mc_substring_set_node_t *)(it->cur_node); // Count is always 1 for substrings in the hashset if (count) { *count = 1; } if (str) { *str = (const char *)it->set->base_string->buf.data + cur->start_offset; } if (byte_len) { *byte_len = cur->byte_len; } it->cur_node = (void *)cur->next; return true; } libmongocrypt-1.19.0/src/mc-text-search-str-encode-private.h000066400000000000000000000042771521103432300237730ustar00rootroot00000000000000/* * Copyright 2024-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. */ #ifndef MONGOCRYPT_TEXT_SEARCH_STR_ENCODE_PRIVATE_H #define MONGOCRYPT_TEXT_SEARCH_STR_ENCODE_PRIVATE_H #include "mc-fle2-encryption-placeholder-private.h" #include "mc-str-encode-string-sets-private.h" #include "mongocrypt-status-private.h" #include "mongocrypt.h" // Result of a StrEncode. Contains the computed prefix, suffix, and substring trees, or NULL if empty, as well as the // exact string. typedef struct { // Base string which the substring sets point to. mc_utf8_string_with_bad_char_t *base_string; // Set of encoded suffixes. mc_affix_set_t *suffix_set; // Set of encoded prefixes. mc_affix_set_t *prefix_set; // Set of encoded substrings. mc_substring_set_t *substring_set; // Encoded exact string. _mongocrypt_buffer_t exact; // Total number of tags over all the sets and the exact string. uint32_t msize; } mc_str_encode_sets_t; // Run StrEncode with the given spec. mc_str_encode_sets_t *mc_text_search_str_encode(const mc_FLE2TextSearchInsertSpec_t *spec, mongocrypt_status_t *status); void mc_str_encode_sets_destroy(mc_str_encode_sets_t *sets); // Applies case/diacritic folding to the string value in spec (if applicable), and returns // the resulting string as a BSON string element in *out. Returns false and an error if the string // is not valid UTF-8 or is unsuitable per the query parameters in the spec. bool mc_text_search_str_query(const mc_FLE2TextSearchInsertSpec_t *spec, _mongocrypt_buffer_t *out, mongocrypt_status_t *status); #endif /* MONGOCRYPT_TEXT_SEARCH_STR_ENCODE_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-text-search-str-encode.c000066400000000000000000000356251521103432300223170ustar00rootroot00000000000000/* * Copyright 2024-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 "mc-fle2-encryption-placeholder-private.h" #include "mc-str-encode-string-sets-private.h" #include "mc-text-search-str-encode-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" #include "mongocrypt.h" #include "unicode/fold.h" #include #include // 16MiB - maximum length in bytes of a string to be encoded. #define MAX_ENCODE_BYTE_LEN 16777216 // Number of bytes which are added to the base string before encryption. #define OVERHEAD_BYTES 5 static mc_affix_set_t *generate_prefix_or_suffix_tree(const mc_utf8_string_with_bad_char_t *base_str, uint32_t unfolded_byte_len, uint32_t lb, uint32_t ub, bool is_prefix, uint32_t *out_msize) { BSON_ASSERT_PARAM(base_str); BSON_ASSERT_PARAM(out_msize); // We encrypt (unfolded string + 5 bytes of extra BSON info) with a 16-byte block cipher. // PKCS7 adds an extra 16-byte padding for 16-byte aligned plaintext lengths. uint32_t encrypted_len = 16 * (uint32_t)((unfolded_byte_len + OVERHEAD_BYTES + 16) / 16); // Max len of a string that has this encrypted len. uint32_t padded_len = encrypted_len - OVERHEAD_BYTES; if (padded_len < lb) { // No valid substrings, return empty tree return NULL; } // Total number of substrings uint32_t msize = BSON_MIN(padded_len, ub) - lb + 1; uint32_t folded_codepoint_len = base_str->codepoint_len - 1; // remove one codepoint for 0xFF uint32_t real_max_len = BSON_MIN(folded_codepoint_len, ub); // Number of actual substrings, excluding padding uint32_t real_substrings = real_max_len >= lb ? real_max_len - lb + 1 : 0; // If real_substrings and msize differ, we need to insert padding, so allocate one extra slot. uint32_t set_size = real_substrings == msize ? real_substrings : real_substrings + 1; mc_affix_set_t *set = mc_affix_set_new(base_str, set_size); uint32_t n_inserted = 0; for (uint32_t i = lb; i < real_max_len + 1; i++, n_inserted++) { if (is_prefix) { // [0, lb), [0, lb + 1), ..., [0, min(len, ub)) BSON_ASSERT(mc_affix_set_insert(set, 0, i)); } else { // [len - lb, len), [len - lb - 1, len), ..., [max(0, len - ub), len) BSON_ASSERT(mc_affix_set_insert(set, folded_codepoint_len - i, folded_codepoint_len)); } } if (msize != real_substrings) { // Insert padding to get to msize BSON_ASSERT(mc_affix_set_insert_base_string(set, msize - real_substrings)); n_inserted++; } BSON_ASSERT(n_inserted == set_size); *out_msize += msize; return set; } static mc_affix_set_t *generate_suffix_tree(const mc_utf8_string_with_bad_char_t *base_str, uint32_t unfolded_byte_len, const mc_FLE2SuffixInsertSpec_t *spec, uint32_t *out_msize) { BSON_ASSERT_PARAM(spec); return generate_prefix_or_suffix_tree(base_str, unfolded_byte_len, spec->lb, spec->ub, false, out_msize); } static mc_affix_set_t *generate_prefix_tree(const mc_utf8_string_with_bad_char_t *base_str, uint32_t unfolded_byte_len, const mc_FLE2PrefixInsertSpec_t *spec, uint32_t *out_msize) { BSON_ASSERT_PARAM(spec); return generate_prefix_or_suffix_tree(base_str, unfolded_byte_len, spec->lb, spec->ub, true, out_msize); } static uint32_t calc_number_of_substrings(uint32_t strlen, uint32_t lb, uint32_t ub) { // There are len - i + 1 substrings of length i in a length len string. // Therefore, the total number of substrings with length between lb and ub // is the sum of the integers inclusive between A = len - ub + 1 and B = len - lb + 1, // A <= B. This has a closed form: (A + B)(B - A + 1)/2. if (lb > strlen) { return 0; } uint32_t largest_substr = BSON_MIN(strlen, ub); uint32_t largest_substr_count = strlen - largest_substr + 1; uint32_t smallest_substr_count = strlen - lb + 1; return (largest_substr_count + smallest_substr_count) * (smallest_substr_count - largest_substr_count + 1) / 2; } static mc_substring_set_t *generate_substring_tree(const mc_utf8_string_with_bad_char_t *base_str, uint32_t unfolded_byte_len, const mc_FLE2SubstringInsertSpec_t *spec, uint32_t *out_msize) { BSON_ASSERT_PARAM(base_str); BSON_ASSERT_PARAM(spec); BSON_ASSERT_PARAM(out_msize); // We encrypt (unfolded string + 5 bytes of extra BSON info) with a 16-byte block cipher. // PKCS7 adds an extra 16-byte padding for 16-byte aligned plaintext lengths. uint32_t encrypted_len = 16 * (uint32_t)((unfolded_byte_len + OVERHEAD_BYTES + 16) / 16); // Max len of a string that has this encrypted len. uint32_t padded_len = encrypted_len - OVERHEAD_BYTES; if (padded_len < spec->lb) { // No valid substrings, return empty tree return NULL; } // If you are following along with the OST paper, a slightly different calculation of msize is used. The following // justifies why that calculation and this calculation are equivalent. // At this point, it is established that: // beta <= mlen // lb <= padded_len // lb <= ub <= mlen // // So, the following formula for msize in the OST paper: // maxkgram_1 = sum_(j=lb, ub, (mlen - j + 1)) // maxkgram_2 = sum_(j=lb, min(ub, padded_len), (padded_len - j + 1)) // msize = min(maxkgram_1, maxkgram_2) // can be simplified to: // msize = sum_(j=lb, min(ub, padded_len), (min(mlen, padded_len) - j + 1)) // // because if padded_len <= ub, then it follows that padded_len <= ub <= mlen, and so // maxkgram_1 = sum_(j=lb, ub, (mlen - j + 1)) # as above // maxkgram_2 = sum_(j=lb, padded_len, (padded_len - j + 1)) # less or equal to maxkgram_1 // msize = maxkgram_2 // and if padded_len > ub, then it follows that: // maxkgram_1 = sum_(j=lb, ub, (mlen - j + 1)) # as above // maxkgram_2 = sum_(j=lb, ub, (padded_len - j + 1)) # same sum bounds as maxkgram_1 // msize = sum_(j=lb, ub, (min(mlen, padded_len) - j + 1)) // in both cases, msize can be rewritten as: // msize = sum_(j=lb, min(ub, padded_len), (min(mlen, padded_len) - j + 1)) uint32_t folded_codepoint_len = base_str->codepoint_len - 1; // If mlen < padded_len, we only need to pad to mlen padded_len = BSON_MIN(spec->mlen, padded_len); // Total number of substrings -- i.e. the number of valid substrings IF the string spanned the full padded length uint32_t msize = calc_number_of_substrings(padded_len, spec->lb, spec->ub); uint32_t n_real_substrings = 0; mc_substring_set_t *set = mc_substring_set_new(base_str); // If folded len < LB, there are no real substrings, so we can skip (avoiding underflow via folded len - LB) if (folded_codepoint_len >= spec->lb) { for (uint32_t i = 0; i < folded_codepoint_len - spec->lb + 1; i++) { for (uint32_t j = i + spec->lb; j < BSON_MIN(folded_codepoint_len, i + spec->ub) + 1; j++) { // Only count successful, i.e. non-duplicate inserts if (mc_substring_set_insert(set, i, j)) { n_real_substrings++; } } } } if (msize != n_real_substrings) { // Insert msize - n_real_substrings padding BSON_ASSERT(msize > n_real_substrings); mc_substring_set_increment_fake_string(set, msize - n_real_substrings); } *out_msize += msize; return set; } static uint32_t mc_get_utf8_codepoint_length(const char *buf, uint32_t len) { BSON_ASSERT_PARAM(buf); const char *cur = buf; const char *end = buf + len; uint32_t codepoint_len = 0; while (cur < end) { cur = bson_utf8_next_char(cur); codepoint_len++; } return codepoint_len; } mc_str_encode_sets_t *mc_text_search_str_encode(const mc_FLE2TextSearchInsertSpec_t *spec, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(spec); if (spec->len > MAX_ENCODE_BYTE_LEN) { CLIENT_ERR("StrEncode: String passed in was too long: String was %" PRIu32 " bytes, but max is %d bytes", spec->len, MAX_ENCODE_BYTE_LEN); return NULL; } if (!bson_utf8_validate(spec->v, spec->len, false /* allow_null */)) { CLIENT_ERR("StrEncode: String passed in was not valid UTF-8"); return NULL; } mc_utf8_string_with_bad_char_t *base_string; if (spec->casef || spec->diacf) { char *folded_str; size_t folded_str_bytes_len; if (!unicode_fold(spec->v, spec->len, (spec->casef * kUnicodeFoldToLower) | (spec->diacf * kUnicodeFoldRemoveDiacritics), &folded_str, &folded_str_bytes_len, status)) { return NULL; } base_string = mc_utf8_string_with_bad_char_from_buffer(folded_str, (uint32_t)folded_str_bytes_len); bson_free(folded_str); } else { base_string = mc_utf8_string_with_bad_char_from_buffer(spec->v, spec->len); } mc_str_encode_sets_t *sets = bson_malloc0(sizeof(mc_str_encode_sets_t)); // Base string is the folded string plus the 0xFF character sets->base_string = base_string; // Initialize msize at 1 for the exact string, and grow it for each encoding. sets->msize = 1; if (spec->suffix.set) { sets->suffix_set = generate_suffix_tree(sets->base_string, spec->len, &spec->suffix.value, &sets->msize); } if (spec->prefix.set) { sets->prefix_set = generate_prefix_tree(sets->base_string, spec->len, &spec->prefix.value, &sets->msize); } if (spec->substr.set) { uint32_t unfolded_codepoint_len = mc_get_utf8_codepoint_length(spec->v, spec->len); if (unfolded_codepoint_len > spec->substr.value.mlen) { CLIENT_ERR("StrEncode: String passed in was longer than the maximum length for substring indexing -- " "String len: %u, max len: %u", unfolded_codepoint_len, spec->substr.value.mlen); mc_str_encode_sets_destroy(sets); return NULL; } sets->substring_set = generate_substring_tree(sets->base_string, spec->len, &spec->substr.value, &sets->msize); } // Exact string is always equal to the base string up until the bad character _mongocrypt_buffer_from_data(&sets->exact, sets->base_string->buf.data, (uint32_t)sets->base_string->buf.len - 1); return sets; } void mc_str_encode_sets_destroy(mc_str_encode_sets_t *sets) { if (!sets) { return; } mc_utf8_string_with_bad_char_destroy(sets->base_string); mc_affix_set_destroy(sets->suffix_set); mc_affix_set_destroy(sets->prefix_set); mc_substring_set_destroy(sets->substring_set); bson_free(sets); } bool mc_text_search_str_query(const mc_FLE2TextSearchInsertSpec_t *spec, _mongocrypt_buffer_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(spec); BSON_ASSERT_PARAM(out); if (spec->len > MAX_ENCODE_BYTE_LEN) { CLIENT_ERR("StrQuery: String passed in was too long: String was %" PRIu32 " bytes, but max is %d bytes", spec->len, MAX_ENCODE_BYTE_LEN); return false; } _mongocrypt_buffer_init(out); if (!bson_utf8_validate(spec->v, spec->len, false /* allow_null */)) { CLIENT_ERR("StrQuery: String passed in was not valid UTF-8"); return false; } uint32_t folded_codepoint_len = 0; if (spec->casef || spec->diacf) { char *folded_str; size_t folded_str_bytes_len; if (!unicode_fold(spec->v, spec->len, (spec->casef * kUnicodeFoldToLower) | (spec->diacf * kUnicodeFoldRemoveDiacritics), &folded_str, &folded_str_bytes_len, status)) { return false; } _mongocrypt_buffer_copy_from_string_as_bson_value(out, folded_str, (int)folded_str_bytes_len); folded_codepoint_len = mc_get_utf8_codepoint_length(folded_str, (uint32_t)folded_str_bytes_len); bson_free(folded_str); } else { _mongocrypt_buffer_copy_from_string_as_bson_value(out, spec->v, (int)spec->len); folded_codepoint_len = mc_get_utf8_codepoint_length(spec->v, spec->len); } if (spec->substr.set || spec->suffix.set || spec->prefix.set) { uint32_t min = 0, max = 0; if (spec->substr.set) { min = spec->substr.value.lb; max = spec->substr.value.ub; } else if (spec->suffix.set) { min = spec->suffix.value.lb; max = spec->suffix.value.ub; } else { min = spec->prefix.value.lb; max = spec->prefix.value.ub; } if (folded_codepoint_len == 0) { CLIENT_ERR("StrQuery: string value cannot be empty for substring, suffix, or prefix queries"); return false; } if (folded_codepoint_len > max) { CLIENT_ERR("StrQuery: string value was longer than the maximum query length " "for this field after folding -- folded codepoint len: %u, max query len: %u", folded_codepoint_len, max); return false; } if (folded_codepoint_len < min) { CLIENT_ERR("StrQuery: string value was shorter than the minimum query length " "for this field after folding -- folded codepoint len: %u, min query len: %u", folded_codepoint_len, min); return false; } } return true; } libmongocrypt-1.19.0/src/mc-textopts-private.h000066400000000000000000000056101521103432300213650ustar00rootroot00000000000000/* * Copyright 2025-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. */ #ifndef MC_TEXTOPTS_PRIVATE_H #define MC_TEXTOPTS_PRIVATE_H #include #include "mc-optional-private.h" #include "mongocrypt-private.h" typedef struct { bool set; mc_optional_int32_t strMaxLength; int32_t strMinQueryLength; int32_t strMaxQueryLength; } mc_TextOptsPerIndex_t; typedef struct { mc_TextOptsPerIndex_t substring; mc_TextOptsPerIndex_t prefix; mc_TextOptsPerIndex_t suffix; bool caseSensitive; bool diacriticSensitive; } mc_TextOpts_t; /* mc_TextOpts_parse parses a BSON document into mc_TextOpts_t. * The document is expected to have the form: * { * "caseSensitive": bool, * . "diacriticSensitive": bool, * . "prefix": { * . "strMaxQueryLength": Int32, * . "strMinQueryLength": Int32, * . }, * . "suffix": { * . "strMaxQueryLength": Int32, * . "strMinQueryLength": Int32, * . }, * . "substring": { * . "strMaxLength": Int32, * . "strMaxQueryLength": Int32, * . "strMinQueryLength": Int32, * . }, * } */ bool mc_TextOpts_parse(mc_TextOpts_t *txo, const bson_t *in, mongocrypt_status_t *status); /* * mc_TextOpts_to_FLE2TextSearchInsertSpec creates a placeholder value to be * encrypted. It is only expected to be called when query_type is unset. The * output FLE2TextSearchInsertSpec is a BSON document of the form: * https://github.com/mongodb/mongo/blob/219e90bfad3c712c9642da29ee52229908f06bcd/src/mongo/crypto/fle_field_schema.idl#L689 * * v is expect to be a BSON document of the form: * { "v": BSON value to encrypt }. * * Preconditions: out must be initialized by caller. */ bool mc_TextOpts_to_FLE2TextSearchInsertSpec(const mc_TextOpts_t *txo, const bson_t *v, bson_t *out, mongocrypt_status_t *status); enum _mongocrypt_query_type_t; // Forward declare. bool mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query(const mc_TextOpts_t *txo, const bson_t *v, enum _mongocrypt_query_type_t query_type, bson_t *out, mongocrypt_status_t *status); #endif // MC_TEXTOPTS_PRIVATE_H libmongocrypt-1.19.0/src/mc-textopts.c000066400000000000000000000337301521103432300177140ustar00rootroot00000000000000/* * Copyright 2025-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 "mc-textopts-private.h" #include "mongocrypt-ctx-private.h" // mongocrypt_query_type_t #include "mongocrypt-private.h" #include "mongocrypt-util-private.h" // mc_bson_type_to_string #include "mongocrypt.h" #include // Common logic for testing field name, tracking duplication, and presence. #define IF_FIELD(Name) \ if (0 == strcmp(field, #Name)) { \ if (has_##Name) { \ CLIENT_ERR(ERROR_PREFIX "Unexpected duplicate field '" #Name "'"); \ return false; \ } \ has_##Name = true; \ ((void)0) #define END_IF_FIELD \ continue; \ } \ else((void)0) #define ERROR_PREFIX "Error parsing TextOpts: " bool mc_TextOptsPerIndex_parse(mc_TextOptsPerIndex_t *txio, bson_iter_t *iter, mongocrypt_status_t *status) { *txio = (mc_TextOptsPerIndex_t){0}; txio->set = true; bool has_strMaxLength = false, has_strMinQueryLength = false, has_strMaxQueryLength = false; while (bson_iter_next(iter)) { const char *field = bson_iter_key(iter); BSON_ASSERT(field); IF_FIELD(strMaxLength); { if (!BSON_ITER_HOLDS_INT32(iter)) { CLIENT_ERR(ERROR_PREFIX "'strMaxLength' must be an int32"); return false; } const int32_t val = bson_iter_int32(iter); if (val <= 0) { CLIENT_ERR(ERROR_PREFIX "'strMaxLength' must be greater than zero"); return false; } txio->strMaxLength = OPT_I32(val); } END_IF_FIELD; IF_FIELD(strMinQueryLength); { if (!BSON_ITER_HOLDS_INT32(iter)) { CLIENT_ERR(ERROR_PREFIX "'strMinQueryLength' must be an int32"); return false; } const int32_t val = bson_iter_int32(iter); if (val <= 0) { CLIENT_ERR(ERROR_PREFIX "'strMinQueryLength' must be greater than zero"); return false; } txio->strMinQueryLength = val; } END_IF_FIELD; IF_FIELD(strMaxQueryLength); { if (!BSON_ITER_HOLDS_INT32(iter)) { CLIENT_ERR(ERROR_PREFIX "'strMaxQueryLength' must be an int32"); return false; } const int32_t val = bson_iter_int32(iter); if (val <= 0) { CLIENT_ERR(ERROR_PREFIX "'strMaxQueryLength' must be greater than zero"); return false; } txio->strMaxQueryLength = val; } END_IF_FIELD; CLIENT_ERR(ERROR_PREFIX "Unrecognized field: '%s'", field); return false; } return true; } bool mc_TextOpts_parse(mc_TextOpts_t *txo, const bson_t *in, mongocrypt_status_t *status) { bson_iter_t iter = {0}; BSON_ASSERT_PARAM(txo); BSON_ASSERT_PARAM(in); BSON_ASSERT(status || true); bool has_caseSensitive = false, has_diacriticSensitive = false, has_substring = false, has_prefix = false, has_suffix = false; *txo = (mc_TextOpts_t){{0}}; if (!bson_iter_init(&iter, in)) { CLIENT_ERR(ERROR_PREFIX "Invalid BSON"); return false; } while (bson_iter_next(&iter)) { const char *field = bson_iter_key(&iter); IF_FIELD(caseSensitive); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "Expected bool for caseSensitive, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } txo->caseSensitive = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(diacriticSensitive); { if (!BSON_ITER_HOLDS_BOOL(&iter)) { CLIENT_ERR(ERROR_PREFIX "Expected bool for diacriticSensitive, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } txo->diacriticSensitive = bson_iter_bool(&iter); } END_IF_FIELD; IF_FIELD(substring); { bson_iter_t subdoc; if (!BSON_ITER_HOLDS_DOCUMENT(&iter) || !bson_iter_recurse(&iter, &subdoc)) { CLIENT_ERR(ERROR_PREFIX "Expected document for substring, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } if (!mc_TextOptsPerIndex_parse(&txo->substring, &subdoc, status)) { return false; } if (!txo->substring.strMaxLength.set) { CLIENT_ERR(ERROR_PREFIX "'strMaxLength' must be set for substring"); return false; } } END_IF_FIELD; IF_FIELD(prefix); { bson_iter_t subdoc; if (!BSON_ITER_HOLDS_DOCUMENT(&iter) || !bson_iter_recurse(&iter, &subdoc)) { CLIENT_ERR(ERROR_PREFIX "Expected document for prefix, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } if (!mc_TextOptsPerIndex_parse(&txo->prefix, &subdoc, status)) { return false; } if (txo->prefix.strMaxLength.set) { CLIENT_ERR(ERROR_PREFIX "'strMaxLength' is not allowed in 'prefix'"); return false; } } END_IF_FIELD; IF_FIELD(suffix); { bson_iter_t subdoc; if (!BSON_ITER_HOLDS_DOCUMENT(&iter) || !bson_iter_recurse(&iter, &subdoc)) { CLIENT_ERR(ERROR_PREFIX "Expected document for suffix, got: %s", mc_bson_type_to_string(bson_iter_type(&iter))); return false; } if (!mc_TextOptsPerIndex_parse(&txo->suffix, &subdoc, status)) { return false; } if (txo->suffix.strMaxLength.set) { CLIENT_ERR(ERROR_PREFIX "'strMaxLength' is not allowed in 'suffix'"); return false; } } END_IF_FIELD; CLIENT_ERR(ERROR_PREFIX "Unrecognized field: '%s'", field); return false; } if (!has_caseSensitive) { CLIENT_ERR(ERROR_PREFIX "'caseSensitive' is required"); return false; } if (!has_diacriticSensitive) { CLIENT_ERR(ERROR_PREFIX "'diacriticSensitive' is required"); return false; } if (has_substring && (has_prefix || has_suffix)) { CLIENT_ERR(ERROR_PREFIX "Cannot specify 'substring' with 'prefix' or 'suffix'"); return false; } if (!(has_prefix || has_suffix || has_substring)) { CLIENT_ERR(ERROR_PREFIX "One of 'prefix', 'suffix', or 'substring' is required"); return false; } return true; } #undef ERROR_PREFIX #define ERROR_PREFIX "Error making FLE2RangeInsertSpec: " static bool append_TextOptsPerIndex(const mc_TextOptsPerIndex_t *txio, bson_t *out, mongocrypt_status_t *status) { if (txio->strMaxLength.set) { if (!bson_append_int32(out, "mlen", -1, txio->strMaxLength.value)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } } if (!bson_append_int32(out, "ub", -1, txio->strMaxQueryLength)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!bson_append_int32(out, "lb", -1, txio->strMinQueryLength)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } return true; } static bool TextOpts_to_FLE2TextSearchInsertSpec(const mc_TextOpts_t *txo, const bson_t *v, bool include_prefix, bool include_suffix, bool include_substring, bson_t *out, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(txo); BSON_ASSERT_PARAM(v); BSON_ASSERT_PARAM(out); BSON_ASSERT(status || true); bson_iter_t v_iter; if (!bson_iter_init_find(&v_iter, v, "v")) { CLIENT_ERR(ERROR_PREFIX "Unable to find 'v' in input"); return false; } bson_t child; if (!BSON_APPEND_DOCUMENT_BEGIN(out, "v", &child)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!bson_append_iter(&child, "v", 1, &v_iter)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } // "casef" means "case folding". Case sensitive => not folded. Case insensitive => folded. if (!bson_append_bool(&child, "casef", -1, !txo->caseSensitive)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } // "diacf" means "diacritic folding". Diacritic sensitive => not folded. Diacritic insensitive => folded. if (!bson_append_bool(&child, "diacf", -1, !txo->diacriticSensitive)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (txo->prefix.set && include_prefix) { bson_t insert_spec; if (!BSON_APPEND_DOCUMENT_BEGIN(&child, "prefix", &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!append_TextOptsPerIndex(&txo->prefix, &insert_spec, status)) { return false; } if (!bson_append_document_end(&child, &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } } if (txo->suffix.set && include_suffix) { bson_t insert_spec; if (!BSON_APPEND_DOCUMENT_BEGIN(&child, "suffix", &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!append_TextOptsPerIndex(&txo->suffix, &insert_spec, status)) { return false; } if (!bson_append_document_end(&child, &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } } if (txo->substring.set && include_substring) { bson_t insert_spec; if (!BSON_APPEND_DOCUMENT_BEGIN(&child, "substr", &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } if (!append_TextOptsPerIndex(&txo->substring, &insert_spec, status)) { return false; } if (!bson_append_document_end(&child, &insert_spec)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } } if (!bson_append_document_end(out, &child)) { CLIENT_ERR(ERROR_PREFIX "Error appending to BSON"); return false; } return true; } bool mc_TextOpts_to_FLE2TextSearchInsertSpec(const mc_TextOpts_t *txo, const bson_t *v, bson_t *out, mongocrypt_status_t *status) { // FLE2TextSearchInsertSpec for insert includes all query types specified. return TextOpts_to_FLE2TextSearchInsertSpec(txo, v, true, true, true, out, status); } bool mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query(const mc_TextOpts_t *txo, const bson_t *v, mongocrypt_query_type_t query_type, bson_t *out, mongocrypt_status_t *status) { // FLE2TextSearchInsertSpec for query only includes query type specified. bool include_prefix = false; bool include_suffix = false; bool include_substring = false; switch (query_type) { case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED: case MONGOCRYPT_QUERY_TYPE_RANGE: case MONGOCRYPT_QUERY_TYPE_EQUALITY: default: { CLIENT_ERR("Unexpected query type: %s\n", _mongocrypt_query_type_to_string(query_type)); return false; } case MONGOCRYPT_QUERY_TYPE_PREFIX: { include_prefix = true; break; } case MONGOCRYPT_QUERY_TYPE_SUFFIX: { include_suffix = true; break; } case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW: { include_substring = true; break; } } return TextOpts_to_FLE2TextSearchInsertSpec(txo, v, include_prefix, include_suffix, include_substring, out, status); } #undef ERROR_PREFIX libmongocrypt-1.19.0/src/mc-tokens-private.h000066400000000000000000000342321521103432300210000ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MONGOCRYPT_TOKENS_PRIVATE_H #define MONGOCRYPT_TOKENS_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-crypto-private.h" /* * ======================= Begin: FLE 2 Token Reference ======================= * * v is a BSON value. It is the bytes after "e_name" in "element" in * https://bsonspec.org/spec.html. * u is a "contention factor". It is a uint64_t. * HMAC is the HMAC-SHA-256 function. * Integers are represented as uint64_t in little-endian. * * CollectionsLevel1Token = HMAC(RootKey, 1) * ServerDataEncryptionLevel1Token = HMAC(RootKey, 3) * * EDCToken = HMAC(CollectionsLevel1Token, 1) * ESCToken = HMAC(CollectionsLevel1Token, 2) * ECCToken = HMAC(CollectionsLevel1Token, 3) * ECOCToken = HMAC(CollectionsLevel1Token, 4) * * EDCDerivedFromDataToken = HMAC(EDCToken, v) * ESCDerivedFromDataToken = HMAC(ESCToken, v) * ECCDerivedFromDataToken = HMAC(ECCToken, v) * * EDCDerivedFromDataTokenAndContentionFactor = HMAC(EDCDerivedFromDataToken, u) * ESCDerivedFromDataTokenAndContentionFactor = HMAC(ESCDerivedFromDataToken, u) * ECCDerivedFromDataTokenAndContentionFactor = HMAC(ECCDerivedFromDataToken, u) * * EDCTwiceDerivedToken = HMAC(EDCDerivedFromDataTokenAndContentionFactor, 1) * ESCTwiceDerivedTagToken = HMAC(ESCDerivedFromDataTokenAndContentionFactor, 1) * ESCTwiceDerivedValueToken = HMAC(ESCDerivedFromDataTokenAndContentionFactor, 2) * ECCTwiceDerivedTagToken = HMAC(ECCDerivedFromDataTokenAndContentionFactor, 1) * ECCTwiceDerivedValueToken = HMAC(ECCDerivedFromDataTokenAndContentionFactor, 2) * * Note: ECC related tokens are used in FLE2v1 only. * Further, ECCTwiceDerivedValue(Tag|Token) have been omitted entirely. * The above comment describing derivation is for doc purposes only. * ---------------------------------------------------------------------------- * Added in FLE2v2: * * ServerTokenDerivationLevel1Token = HMAC(RootKey, 2) * ServerDerivedFromDataToken = HMAC(ServerTokenDerivationLevel1Token, v) * * ServerCountAndContentionFactorEncryptionToken = * HMAC(ServerDerivedFromDataToken, 1) * ServerZerosEncryptionToken = HMAC(ServerDerivedFromDataToken, 2) * ---------------------------------------------------------------------------- * Added in Range V2: * * d is a 17-byte blob of zeros. * * AnchorPaddingTokenRoot = HMAC(ESCToken, d) * Server-side: * AnchorPaddingTokenKey = HMAC(AnchorPaddingTokenRoot, 1) * AnchorPaddingTokenValue = HMAC(AnchorPaddingTokenRoot, 2) * ---------------------------------------------------------------------------- * Added in Text Search: * * EDCTextExactToken = HMAC(EDCToken, 1) * EDCTextSubstringToken = HMAC(EDCToken, 2) * EDCTextSuffixToken = HMAC(EDCToken, 3) * EDCTextPrefixToken = HMAC(EDCToken, 4) * * ESCTextExactToken = HMAC(ESCToken, 1) * ESCTextSubstringToken = HMAC(ESCToken, 2) * ESCTextSuffixToken = HMAC(ESCToken, 3) * ESCTextPrefixToken = HMAC(ESCToken, 4) * * ServerTextExactToken = HMAC(ServerTokenDerivationLevel1Token, 1) * ServerTextSubstringToken = HMAC(ServerTokenDerivationLevel1Token, 2) * ServerTextSuffixToken = HMAC(ServerTokenDerivationLevel1Token, 3) * ServerTextPrefixToken = HMAC(ServerTokenDerivationLevel1Token, 4) * * EDCTextExactDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(EDCTextExactToken, v), u) * EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(EDCTextSubstringToken, v), u) * EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(EDCTextSuffixToken, v), u) * EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(EDCTextPrefixToken, v), u) * * ESCTextExactDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(ESCTextExactToken, v), u) * ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(ESCTextSubstringToken, v), u) * ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(ESCTextSuffixToken, v), u) * ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken = * HMAC(HMAC(ESCTextPrefixToken, v), u) * * ServerTextExactDerivedFromDataToken = HMAC(ServerTextExactToken, v) * ServerTextSubstringDerivedFromDataToken = HMAC(ServerTextSubstringToken, v) * ServerTextSuffixDerivedFromDataToken = HMAC(ServerTextSuffixToken, v) * ServerTextPrefixDerivedFromDataToken = HMAC(ServerTextPrefixToken, v) * * ======================== End: FLE 2 Token Reference ======================== */ /// Declare a token type named 'Name', with constructor parameters given by the /// remaining arguments. Each constructor will also have the implicit first /// argument '_mongocrypt_crypto_t* crypto' and a final argument /// 'mongocrypt_status_t* status' #define DECL_TOKEN_TYPE(Name, ...) DECL_TOKEN_TYPE_1(Name, BSON_CONCAT(Name, _t), __VA_ARGS__) #define DECL_TOKEN_TYPE_1(Prefix, T, ...) \ /* Opaque typedef the struct */ \ typedef struct T T; \ /* Data-getter */ \ extern const _mongocrypt_buffer_t *BSON_CONCAT(Prefix, _get)(const T *t); \ /* Destructor */ \ extern void BSON_CONCAT(Prefix, _destroy)(T * t); \ /* Constructor for server to shallow copy tokens from raw buffer */ \ extern T *BSON_CONCAT(Prefix, _new_from_buffer)(const _mongocrypt_buffer_t *buf); \ /* Constructor for server to deep copy tokens from raw buffer */ \ extern T *BSON_CONCAT(Prefix, _new_from_buffer_copy)(const _mongocrypt_buffer_t *buf); \ /* Constructor. Parameter list given as variadic args */ \ extern T *BSON_CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status) DECL_TOKEN_TYPE(mc_CollectionsLevel1Token, const _mongocrypt_buffer_t *); DECL_TOKEN_TYPE(mc_ServerTokenDerivationLevel1Token, const _mongocrypt_buffer_t *); DECL_TOKEN_TYPE(mc_ServerDataEncryptionLevel1Token, const _mongocrypt_buffer_t *); DECL_TOKEN_TYPE(mc_EDCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token); DECL_TOKEN_TYPE(mc_ESCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token); DECL_TOKEN_TYPE(mc_ECCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token); DECL_TOKEN_TYPE(mc_ECOCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token); DECL_TOKEN_TYPE(mc_EDCDerivedFromDataToken, const mc_EDCToken_t *EDCToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ECCDerivedFromDataToken, const mc_ECCToken_t *ECCToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ESCDerivedFromDataToken, const mc_ESCToken_t *ESCToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndContentionFactor, const mc_EDCDerivedFromDataToken_t *EDCDerivedFromDataToken, uint64_t u); DECL_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndContentionFactor, const mc_ESCDerivedFromDataToken_t *ESCDerivedFromDataToken, uint64_t u); DECL_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndContentionFactor, const mc_ECCDerivedFromDataToken_t *ECCDerivedFromDataToken, uint64_t u); DECL_TOKEN_TYPE(mc_EDCTwiceDerivedToken, const mc_EDCDerivedFromDataTokenAndContentionFactor_t *EDCDerivedFromDataTokenAndContentionFactor); DECL_TOKEN_TYPE(mc_ESCTwiceDerivedTagToken, const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor); DECL_TOKEN_TYPE(mc_ESCTwiceDerivedValueToken, const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor); DECL_TOKEN_TYPE(mc_ServerDerivedFromDataToken, const mc_ServerTokenDerivationLevel1Token_t *ServerTokenDerivationToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ServerCountAndContentionFactorEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken); DECL_TOKEN_TYPE(mc_ServerZerosEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken); DECL_TOKEN_TYPE(mc_AnchorPaddingTokenRoot, const mc_ESCToken_t *ESCToken); DECL_TOKEN_TYPE(mc_AnchorPaddingKeyToken, const mc_AnchorPaddingTokenRoot_t *anchorPaddingToken); DECL_TOKEN_TYPE(mc_AnchorPaddingValueToken, const mc_AnchorPaddingTokenRoot_t *anchorPaddingToken); DECL_TOKEN_TYPE(mc_EDCTextExactToken, const mc_EDCToken_t *edcToken); DECL_TOKEN_TYPE(mc_EDCTextSubstringToken, const mc_EDCToken_t *edcToken); DECL_TOKEN_TYPE(mc_EDCTextSuffixToken, const mc_EDCToken_t *edcToken); DECL_TOKEN_TYPE(mc_EDCTextPrefixToken, const mc_EDCToken_t *edcToken); DECL_TOKEN_TYPE(mc_ESCTextExactToken, const mc_ESCToken_t *escToken); DECL_TOKEN_TYPE(mc_ESCTextSubstringToken, const mc_ESCToken_t *escToken); DECL_TOKEN_TYPE(mc_ESCTextSuffixToken, const mc_ESCToken_t *escToken); DECL_TOKEN_TYPE(mc_ESCTextPrefixToken, const mc_ESCToken_t *escToken); DECL_TOKEN_TYPE(mc_ServerTextExactToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token); DECL_TOKEN_TYPE(mc_ServerTextSubstringToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token); DECL_TOKEN_TYPE(mc_ServerTextSuffixToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token); DECL_TOKEN_TYPE(mc_ServerTextPrefixToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token); DECL_TOKEN_TYPE(mc_EDCTextExactDerivedFromDataToken, const mc_EDCTextExactToken_t *edcTextExactToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_EDCTextSubstringDerivedFromDataToken, const mc_EDCTextSubstringToken_t *edcTextSubstringToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_EDCTextSuffixDerivedFromDataToken, const mc_EDCTextSuffixToken_t *edcTextSuffixToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_EDCTextPrefixDerivedFromDataToken, const mc_EDCTextPrefixToken_t *edcTextPrefixToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_EDCTextExactDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextExactDerivedFromDataToken_t *edcTextExactToken, uint64_t u); DECL_TOKEN_TYPE(mc_EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextSubstringDerivedFromDataToken_t *edcTextSubstringToken, uint64_t u); DECL_TOKEN_TYPE(mc_EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextSuffixDerivedFromDataToken_t *edcTextSuffixToken, uint64_t u); DECL_TOKEN_TYPE(mc_EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextPrefixDerivedFromDataToken_t *edcTextPrefixToken, uint64_t u); DECL_TOKEN_TYPE(mc_ESCTextExactDerivedFromDataToken, const mc_ESCTextExactToken_t *escTextExactToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ESCTextSubstringDerivedFromDataToken, const mc_ESCTextSubstringToken_t *escTextSubstringToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ESCTextSuffixDerivedFromDataToken, const mc_ESCTextSuffixToken_t *escTextSuffixToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ESCTextPrefixDerivedFromDataToken, const mc_ESCTextPrefixToken_t *escTextPrefixToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ESCTextExactDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextExactDerivedFromDataToken_t *escTextExactToken, uint64_t u); DECL_TOKEN_TYPE(mc_ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextSubstringDerivedFromDataToken_t *escTextSubstringToken, uint64_t u); DECL_TOKEN_TYPE(mc_ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextSuffixDerivedFromDataToken_t *escTextSuffixToken, uint64_t u); DECL_TOKEN_TYPE(mc_ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextPrefixDerivedFromDataToken_t *escTextPrefixToken, uint64_t u); DECL_TOKEN_TYPE(mc_ServerTextExactDerivedFromDataToken, const mc_ServerTextExactToken_t *serverTextExactToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ServerTextSubstringDerivedFromDataToken, const mc_ServerTextSubstringToken_t *serverTextSubstringToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ServerTextSuffixDerivedFromDataToken, const mc_ServerTextSuffixToken_t *serverTextSuffixToken, const _mongocrypt_buffer_t *v); DECL_TOKEN_TYPE(mc_ServerTextPrefixDerivedFromDataToken, const mc_ServerTextPrefixToken_t *serverTextPrefixToken, const _mongocrypt_buffer_t *v); #undef DECL_TOKEN_TYPE #undef DECL_TOKEN_TYPE_1 #endif /* MONGOCRYPT_TOKENS_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-tokens.c000066400000000000000000000532031521103432300173220ustar00rootroot00000000000000/* * Copyright 2022-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 "mc-tokens-private.h" #include "mongocrypt-buffer-private.h" /// Define a token type of the given name, with constructor parameters given as /// the remaining arguments. This macro usage should be followed by the /// constructor body, with the implicit first argument '_mongocrypt_crypto_t* /// crypto' and final argument 'mongocrypt_status_t* status' #define DEF_TOKEN_TYPE(Name, ...) DEF_TOKEN_TYPE_1(Name, BSON_CONCAT(Name, _t), __VA_ARGS__) #define DEF_TOKEN_TYPE_1(Prefix, T, ...) \ /* Define the struct for the token */ \ struct T { \ _mongocrypt_buffer_t data; \ }; \ /* Data-getter */ \ const _mongocrypt_buffer_t *BSON_CONCAT(Prefix, _get)(const T *self) { return &self->data; } \ /* Destructor */ \ void BSON_CONCAT(Prefix, _destroy)(T * self) { \ if (!self) { \ return; \ } \ _mongocrypt_buffer_cleanup(&self->data); \ bson_free(self); \ } \ /* Constructor. Shallow copy from raw buffer */ \ T *BSON_CONCAT(Prefix, _new_from_buffer)(const _mongocrypt_buffer_t *buf) { \ BSON_ASSERT(buf->len == MONGOCRYPT_HMAC_SHA256_LEN); \ T *t = bson_malloc(sizeof(T)); \ _mongocrypt_buffer_set_to(buf, &t->data); \ return t; \ } \ /* Constructor. Deep copy from raw buffer */ \ T *BSON_CONCAT(Prefix, _new_from_buffer_copy)(const _mongocrypt_buffer_t *buf) { \ BSON_ASSERT(buf->len == MONGOCRYPT_HMAC_SHA256_LEN); \ T *t = bson_malloc(sizeof(T)); \ _mongocrypt_buffer_init(&t->data); \ _mongocrypt_buffer_copy_to(buf, &t->data); \ return t; \ } \ /* Constructor. Parameter list given as variadic args. */ \ T *BSON_CONCAT(Prefix, _new)(_mongocrypt_crypto_t * crypto, __VA_ARGS__, mongocrypt_status_t * status) #define IMPL_TOKEN_NEW_1(Name, Key, Arg, Clean) \ { \ BSON_CONCAT(Name, _t) *t = bson_malloc(sizeof(BSON_CONCAT(Name, _t))); \ _mongocrypt_buffer_init(&t->data); \ _mongocrypt_buffer_resize(&t->data, MONGOCRYPT_HMAC_SHA256_LEN); \ \ if (!_mongocrypt_hmac_sha_256(crypto, Key, Arg, &t->data, status)) { \ BSON_CONCAT(Name, _destroy)(t); \ Clean; \ return NULL; \ } \ Clean; \ return t; \ } // Define the implementation of a token where Arg is a _mongocrypt_buffer_t. #define IMPL_TOKEN_NEW(Name, Key, Arg) IMPL_TOKEN_NEW_1(Name, Key, Arg, (void)0) // Define the implementation of a token where Arg is a uint64_t. #define IMPL_TOKEN_NEW_CONST(Name, Key, Arg) \ { \ _mongocrypt_buffer_t to_hash; \ _mongocrypt_buffer_copy_from_uint64_le(&to_hash, Arg); \ IMPL_TOKEN_NEW_1(Name, Key, &to_hash, _mongocrypt_buffer_cleanup(&to_hash)) \ } DEF_TOKEN_TYPE(mc_CollectionsLevel1Token, const _mongocrypt_buffer_t *RootKey) IMPL_TOKEN_NEW_CONST(mc_CollectionsLevel1Token, RootKey, 1) DEF_TOKEN_TYPE(mc_EDCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token) IMPL_TOKEN_NEW_CONST(mc_EDCToken, mc_CollectionsLevel1Token_get(CollectionsLevel1Token), 1) DEF_TOKEN_TYPE(mc_ESCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ESCToken, mc_CollectionsLevel1Token_get(CollectionsLevel1Token), 2) DEF_TOKEN_TYPE(mc_ECCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ECCToken, mc_CollectionsLevel1Token_get(CollectionsLevel1Token), 3) DEF_TOKEN_TYPE(mc_ECOCToken, const mc_CollectionsLevel1Token_t *CollectionsLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ECOCToken, mc_CollectionsLevel1Token_get(CollectionsLevel1Token), 4) DEF_TOKEN_TYPE(mc_EDCDerivedFromDataToken, const mc_EDCToken_t *EDCToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_EDCDerivedFromDataToken, mc_EDCToken_get(EDCToken), v) DEF_TOKEN_TYPE(mc_ESCDerivedFromDataToken, const mc_ESCToken_t *ESCToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ESCDerivedFromDataToken, mc_ESCToken_get(ESCToken), v) DEF_TOKEN_TYPE(mc_ECCDerivedFromDataToken, const mc_ECCToken_t *ECCToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ECCDerivedFromDataToken, mc_ECCToken_get(ECCToken), v) DEF_TOKEN_TYPE(mc_EDCTwiceDerivedToken, const mc_EDCDerivedFromDataTokenAndContentionFactor_t *EDCDerivedFromDataTokenAndContentionFactor) IMPL_TOKEN_NEW_CONST(mc_EDCTwiceDerivedToken, mc_EDCDerivedFromDataTokenAndContentionFactor_get(EDCDerivedFromDataTokenAndContentionFactor), 1) DEF_TOKEN_TYPE(mc_ESCTwiceDerivedTagToken, const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor) IMPL_TOKEN_NEW_CONST(mc_ESCTwiceDerivedTagToken, mc_ESCDerivedFromDataTokenAndContentionFactor_get(ESCDerivedFromDataTokenAndContentionFactor), 1) DEF_TOKEN_TYPE(mc_ESCTwiceDerivedValueToken, const mc_ESCDerivedFromDataTokenAndContentionFactor_t *ESCDerivedFromDataTokenAndContentionFactor) IMPL_TOKEN_NEW_CONST(mc_ESCTwiceDerivedValueToken, mc_ESCDerivedFromDataTokenAndContentionFactor_get(ESCDerivedFromDataTokenAndContentionFactor), 2) DEF_TOKEN_TYPE(mc_ServerDataEncryptionLevel1Token, const _mongocrypt_buffer_t *RootKey) IMPL_TOKEN_NEW_CONST(mc_ServerDataEncryptionLevel1Token, RootKey, 3) DEF_TOKEN_TYPE(mc_EDCDerivedFromDataTokenAndContentionFactor, const mc_EDCDerivedFromDataToken_t *EDCDerivedFromDataToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_EDCDerivedFromDataTokenAndContentionFactor, mc_EDCDerivedFromDataToken_get(EDCDerivedFromDataToken), u) DEF_TOKEN_TYPE(mc_ESCDerivedFromDataTokenAndContentionFactor, const mc_ESCDerivedFromDataToken_t *ESCDerivedFromDataToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ESCDerivedFromDataTokenAndContentionFactor, mc_ESCDerivedFromDataToken_get(ESCDerivedFromDataToken), u) DEF_TOKEN_TYPE(mc_ECCDerivedFromDataTokenAndContentionFactor, const mc_ECCDerivedFromDataToken_t *ECCDerivedFromDataToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ECCDerivedFromDataTokenAndContentionFactor, mc_ECCDerivedFromDataToken_get(ECCDerivedFromDataToken), u) /* FLE2v2 */ DEF_TOKEN_TYPE(mc_ServerTokenDerivationLevel1Token, const _mongocrypt_buffer_t *RootKey) IMPL_TOKEN_NEW_CONST(mc_ServerTokenDerivationLevel1Token, RootKey, 2) DEF_TOKEN_TYPE(mc_ServerDerivedFromDataToken, const mc_ServerTokenDerivationLevel1Token_t *ServerTokenDerivationToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ServerDerivedFromDataToken, mc_ServerTokenDerivationLevel1Token_get(ServerTokenDerivationToken), v) DEF_TOKEN_TYPE(mc_ServerCountAndContentionFactorEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken) IMPL_TOKEN_NEW_CONST(mc_ServerCountAndContentionFactorEncryptionToken, mc_ServerDerivedFromDataToken_get(serverDerivedFromDataToken), 1) DEF_TOKEN_TYPE(mc_ServerZerosEncryptionToken, const mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken) IMPL_TOKEN_NEW_CONST(mc_ServerZerosEncryptionToken, mc_ServerDerivedFromDataToken_get(serverDerivedFromDataToken), 2) // d = 17 bytes of 0, AnchorPaddingTokenRoot = HMAC(ESCToken, d) #define ANCHOR_PADDING_TOKEN_D_LENGTH 17 static const uint8_t mc_AnchorPaddingTokenDValue[ANCHOR_PADDING_TOKEN_D_LENGTH] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; DEF_TOKEN_TYPE(mc_AnchorPaddingTokenRoot, const mc_ESCToken_t *ESCToken) { _mongocrypt_buffer_t to_hash; if (!_mongocrypt_buffer_copy_from_data_and_size(&to_hash, mc_AnchorPaddingTokenDValue, ANCHOR_PADDING_TOKEN_D_LENGTH)) { return NULL; } IMPL_TOKEN_NEW_1(mc_AnchorPaddingTokenRoot, mc_ESCToken_get(ESCToken), &to_hash, _mongocrypt_buffer_cleanup(&to_hash)) } #undef ANCHOR_PADDING_TOKEN_D_LENGTH DEF_TOKEN_TYPE(mc_AnchorPaddingKeyToken, const mc_AnchorPaddingTokenRoot_t *anchorPaddingToken) IMPL_TOKEN_NEW_CONST(mc_AnchorPaddingKeyToken, mc_AnchorPaddingTokenRoot_get(anchorPaddingToken), 1) DEF_TOKEN_TYPE(mc_AnchorPaddingValueToken, const mc_AnchorPaddingTokenRoot_t *anchorPaddingToken) IMPL_TOKEN_NEW_CONST(mc_AnchorPaddingValueToken, mc_AnchorPaddingTokenRoot_get(anchorPaddingToken), 2) #define TEXT_EXACT_ID 1 #define TEXT_SUBSTRING_ID 2 #define TEXT_SUFFIX_ID 3 #define TEXT_PREFIX_ID 4 DEF_TOKEN_TYPE(mc_EDCTextExactToken, const mc_EDCToken_t *edcToken) IMPL_TOKEN_NEW_CONST(mc_EDCTextExactToken, mc_EDCToken_get(edcToken), TEXT_EXACT_ID) DEF_TOKEN_TYPE(mc_EDCTextSubstringToken, const mc_EDCToken_t *edcToken) IMPL_TOKEN_NEW_CONST(mc_EDCTextSubstringToken, mc_EDCToken_get(edcToken), TEXT_SUBSTRING_ID) DEF_TOKEN_TYPE(mc_EDCTextSuffixToken, const mc_EDCToken_t *edcToken) IMPL_TOKEN_NEW_CONST(mc_EDCTextSuffixToken, mc_EDCToken_get(edcToken), TEXT_SUFFIX_ID) DEF_TOKEN_TYPE(mc_EDCTextPrefixToken, const mc_EDCToken_t *edcToken) IMPL_TOKEN_NEW_CONST(mc_EDCTextPrefixToken, mc_EDCToken_get(edcToken), TEXT_PREFIX_ID) DEF_TOKEN_TYPE(mc_ESCTextExactToken, const mc_ESCToken_t *escToken) IMPL_TOKEN_NEW_CONST(mc_ESCTextExactToken, mc_ESCToken_get(escToken), TEXT_EXACT_ID) DEF_TOKEN_TYPE(mc_ESCTextSubstringToken, const mc_ESCToken_t *escToken) IMPL_TOKEN_NEW_CONST(mc_ESCTextSubstringToken, mc_ESCToken_get(escToken), TEXT_SUBSTRING_ID) DEF_TOKEN_TYPE(mc_ESCTextSuffixToken, const mc_ESCToken_t *escToken) IMPL_TOKEN_NEW_CONST(mc_ESCTextSuffixToken, mc_ESCToken_get(escToken), TEXT_SUFFIX_ID) DEF_TOKEN_TYPE(mc_ESCTextPrefixToken, const mc_ESCToken_t *escToken) IMPL_TOKEN_NEW_CONST(mc_ESCTextPrefixToken, mc_ESCToken_get(escToken), TEXT_PREFIX_ID) DEF_TOKEN_TYPE(mc_ServerTextExactToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ServerTextExactToken, mc_ServerTokenDerivationLevel1Token_get(serverTokenDerivationLevel1Token), TEXT_EXACT_ID) DEF_TOKEN_TYPE(mc_ServerTextSubstringToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ServerTextSubstringToken, mc_ServerTokenDerivationLevel1Token_get(serverTokenDerivationLevel1Token), TEXT_SUBSTRING_ID) DEF_TOKEN_TYPE(mc_ServerTextSuffixToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ServerTextSuffixToken, mc_ServerTokenDerivationLevel1Token_get(serverTokenDerivationLevel1Token), TEXT_SUFFIX_ID) DEF_TOKEN_TYPE(mc_ServerTextPrefixToken, const mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token) IMPL_TOKEN_NEW_CONST(mc_ServerTextPrefixToken, mc_ServerTokenDerivationLevel1Token_get(serverTokenDerivationLevel1Token), TEXT_PREFIX_ID) DEF_TOKEN_TYPE(mc_EDCTextExactDerivedFromDataToken, const mc_EDCTextExactToken_t *edcTextExactToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_EDCTextExactDerivedFromDataToken, mc_EDCTextExactToken_get(edcTextExactToken), v) DEF_TOKEN_TYPE(mc_EDCTextSubstringDerivedFromDataToken, const mc_EDCTextSubstringToken_t *edcTextSubstringToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_EDCTextSubstringDerivedFromDataToken, mc_EDCTextSubstringToken_get(edcTextSubstringToken), v) DEF_TOKEN_TYPE(mc_EDCTextSuffixDerivedFromDataToken, const mc_EDCTextSuffixToken_t *edcTextSuffixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_EDCTextSuffixDerivedFromDataToken, mc_EDCTextSuffixToken_get(edcTextSuffixToken), v) DEF_TOKEN_TYPE(mc_EDCTextPrefixDerivedFromDataToken, const mc_EDCTextPrefixToken_t *edcTextPrefixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_EDCTextPrefixDerivedFromDataToken, mc_EDCTextPrefixToken_get(edcTextPrefixToken), v) DEF_TOKEN_TYPE(mc_EDCTextExactDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextExactDerivedFromDataToken_t *edcTextExactToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_EDCTextExactDerivedFromDataTokenAndContentionFactorToken, mc_EDCTextExactDerivedFromDataToken_get(edcTextExactToken), u) DEF_TOKEN_TYPE(mc_EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextSubstringDerivedFromDataToken_t *edcTextSubstringToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken, mc_EDCTextSubstringDerivedFromDataToken_get(edcTextSubstringToken), u) DEF_TOKEN_TYPE(mc_EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextSuffixDerivedFromDataToken_t *edcTextSuffixToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken, mc_EDCTextSuffixDerivedFromDataToken_get(edcTextSuffixToken), u) DEF_TOKEN_TYPE(mc_EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken, const mc_EDCTextPrefixDerivedFromDataToken_t *edcTextPrefixToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken, mc_EDCTextPrefixDerivedFromDataToken_get(edcTextPrefixToken), u) DEF_TOKEN_TYPE(mc_ESCTextExactDerivedFromDataToken, const mc_ESCTextExactToken_t *escTextExactToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ESCTextExactDerivedFromDataToken, mc_ESCTextExactToken_get(escTextExactToken), v) DEF_TOKEN_TYPE(mc_ESCTextSubstringDerivedFromDataToken, const mc_ESCTextSubstringToken_t *escTextSubstringToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ESCTextSubstringDerivedFromDataToken, mc_ESCTextSubstringToken_get(escTextSubstringToken), v) DEF_TOKEN_TYPE(mc_ESCTextSuffixDerivedFromDataToken, const mc_ESCTextSuffixToken_t *escTextSuffixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ESCTextSuffixDerivedFromDataToken, mc_ESCTextSuffixToken_get(escTextSuffixToken), v) DEF_TOKEN_TYPE(mc_ESCTextPrefixDerivedFromDataToken, const mc_ESCTextPrefixToken_t *escTextPrefixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ESCTextPrefixDerivedFromDataToken, mc_ESCTextPrefixToken_get(escTextPrefixToken), v) DEF_TOKEN_TYPE(mc_ESCTextExactDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextExactDerivedFromDataToken_t *escTextExactToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ESCTextExactDerivedFromDataTokenAndContentionFactorToken, mc_ESCTextExactDerivedFromDataToken_get(escTextExactToken), u) DEF_TOKEN_TYPE(mc_ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextSubstringDerivedFromDataToken_t *escTextSubstringToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken, mc_ESCTextSubstringDerivedFromDataToken_get(escTextSubstringToken), u) DEF_TOKEN_TYPE(mc_ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextSuffixDerivedFromDataToken_t *escTextSuffixToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken, mc_ESCTextSuffixDerivedFromDataToken_get(escTextSuffixToken), u) DEF_TOKEN_TYPE(mc_ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken, const mc_ESCTextPrefixDerivedFromDataToken_t *escTextPrefixToken, uint64_t u) IMPL_TOKEN_NEW_CONST(mc_ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken, mc_ESCTextPrefixDerivedFromDataToken_get(escTextPrefixToken), u) DEF_TOKEN_TYPE(mc_ServerTextExactDerivedFromDataToken, const mc_ServerTextExactToken_t *serverTextExactToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ServerTextExactDerivedFromDataToken, mc_ServerTextExactToken_get(serverTextExactToken), v) DEF_TOKEN_TYPE(mc_ServerTextSubstringDerivedFromDataToken, const mc_ServerTextSubstringToken_t *serverTextSubstringToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ServerTextSubstringDerivedFromDataToken, mc_ServerTextSubstringToken_get(serverTextSubstringToken), v) DEF_TOKEN_TYPE(mc_ServerTextSuffixDerivedFromDataToken, const mc_ServerTextSuffixToken_t *serverTextSuffixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ServerTextSuffixDerivedFromDataToken, mc_ServerTextSuffixToken_get(serverTextSuffixToken), v) DEF_TOKEN_TYPE(mc_ServerTextPrefixDerivedFromDataToken, const mc_ServerTextPrefixToken_t *serverTextPrefixToken, const _mongocrypt_buffer_t *v) IMPL_TOKEN_NEW(mc_ServerTextPrefixDerivedFromDataToken, mc_ServerTextPrefixToken_get(serverTextPrefixToken), v) libmongocrypt-1.19.0/src/mc-writer-private.h000066400000000000000000000043701521103432300210110ustar00rootroot00000000000000/* * Copyright 2022-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. */ #ifndef MONGOCRYPT_WRITER_PRIVATE_H #define MONGOCRYPT_WRITER_PRIVATE_H #include "mongocrypt-buffer-private.h" #include #include /** * A non-owning forward-only cursor api to write to a buffer. * * Tracks length of buffer and current position of buffer. parser_name is * typically __func__ to provide useful error messages automatically. * * All numbers are written as little endian. */ struct _mc_writer_t { uint8_t *ptr; uint64_t pos; uint64_t len; const char *parser_name; }; typedef struct _mc_writer_t mc_writer_t; void mc_writer_init(mc_writer_t *writer, uint8_t *ptr, uint64_t len, const char *parser_name); void mc_writer_init_from_buffer(mc_writer_t *writer, _mongocrypt_buffer_t *buf, const char *parser_name); mc_writer_t *mc_writer_new(uint8_t *ptr, uint64_t len, const char *parser_name); void mc_writer_destroy(mc_writer_t *writer); bool mc_writer_write_u8(mc_writer_t *writer, const uint8_t value, mongocrypt_status_t *status); bool mc_writer_write_u32(mc_writer_t *writer, const uint32_t value, mongocrypt_status_t *status); bool mc_writer_write_u64(mc_writer_t *writer, const uint64_t value, mongocrypt_status_t *status); bool mc_writer_write_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, uint64_t length, mongocrypt_status_t *status); bool mc_writer_write_uuid_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); bool mc_writer_write_prfblock_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); #endif /* MONGOCRYPT_READER_PRIVATE_H */ libmongocrypt-1.19.0/src/mc-writer.c000066400000000000000000000126301521103432300173320ustar00rootroot00000000000000/* * Copyright 2022-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-private.h" #include "mc-writer-private.h" #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } else \ ((void)0) #define CHECK_REMAINING_BUFFER_AND_RET(write_size) \ if ((write_size) > writer->len - writer->pos) { \ CLIENT_ERR("%s expected at most %" PRIu64 " bytes, got: %" PRIu64, \ writer->parser_name, \ (writer->len - writer->pos), \ (uint64_t)(write_size)); \ return false; \ } else \ ((void)0) void mc_writer_init(mc_writer_t *writer, uint8_t *ptr, uint64_t len, const char *parser_name) { BSON_ASSERT_PARAM(writer); BSON_ASSERT_PARAM(ptr); BSON_ASSERT_PARAM(parser_name); *writer = (mc_writer_t){.pos = 0u, .ptr = ptr, .len = len, .parser_name = parser_name}; } void mc_writer_init_from_buffer(mc_writer_t *writer, _mongocrypt_buffer_t *buf, const char *parser_name) { BSON_ASSERT_PARAM(writer); BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(parser_name); mc_writer_init(writer, buf->data, buf->len, parser_name); } mc_writer_t *mc_writer_new(uint8_t *ptr, uint64_t len, const char *parser_name) { BSON_ASSERT_PARAM(ptr); BSON_ASSERT_PARAM(parser_name); mc_writer_t *writer = bson_malloc(sizeof(mc_writer_t)); mc_writer_init(writer, ptr, len, parser_name); return writer; } void mc_writer_destroy(mc_writer_t *writer) { bson_free(writer); } bool mc_writer_write_u8(mc_writer_t *writer, const uint8_t value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(writer); CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint8_t)); memcpy(writer->ptr + writer->pos, &value, sizeof(uint8_t)); writer->pos += sizeof(uint8_t); return true; } bool mc_writer_write_u32(mc_writer_t *writer, const uint32_t value, mongocrypt_status_t *status) { CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint32_t)); uint32_t temp = BSON_UINT32_TO_LE(value); memcpy(writer->ptr + writer->pos, &temp, sizeof(uint32_t)); writer->pos += sizeof(uint32_t); return true; } bool mc_writer_write_u64(mc_writer_t *writer, const uint64_t value, mongocrypt_status_t *status) { CHECK_REMAINING_BUFFER_AND_RET(sizeof(uint64_t)); uint64_t temp = BSON_UINT64_TO_LE(value); memcpy(writer->ptr + writer->pos, &temp, sizeof(uint64_t)); writer->pos += sizeof(uint64_t); return true; } bool mc_writer_write_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, uint64_t length, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(writer); BSON_ASSERT_PARAM(buf); if (length > buf->len) { CLIENT_ERR("%s cannot write %" PRIu64 " bytes from buffer with length %" PRIu32, writer->parser_name, length, buf->len); return false; } CHECK_REMAINING_BUFFER_AND_RET(length); if (length > SIZE_MAX) { CLIENT_ERR("%s failed to copy " "data of length %" PRIu64, writer->parser_name, length); return false; } memcpy(writer->ptr + writer->pos, buf->data, (size_t)length); writer->pos += length; return true; } bool mc_writer_write_uuid_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(writer); BSON_ASSERT_PARAM(buf); CHECK_AND_RETURN(mc_writer_write_buffer(writer, buf, UUID_LEN, status)); return true; } bool mc_writer_write_prfblock_buffer(mc_writer_t *writer, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(writer); BSON_ASSERT_PARAM(buf); CHECK_AND_RETURN(mc_writer_write_buffer(writer, buf, PRF_LEN, status)); return true; } libmongocrypt-1.19.0/src/mongo_crypt-v1.h000066400000000000000000000302751521103432300203170ustar00rootroot00000000000000/** * Copyright (C) 2021-present MongoDB, Inc. */ // clang-format off #ifndef MONGO_CRYPT_SUPPORT_H #define MONGO_CRYPT_SUPPORT_H #include #include #pragma push_macro("MONGO_API_CALL") #undef MONGO_API_CALL #pragma push_macro("MONGO_API_IMPORT") #undef MONGO_API_IMPORT #pragma push_macro("MONGO_API_EXPORT") #undef MONGO_API_EXPORT #pragma push_macro("MONGO_CRYPT_SUPPORT_API") #undef MONGO_CRYPT_SUPPORT_API #if defined(_WIN32) #define MONGO_API_CALL __cdecl #define MONGO_API_IMPORT __declspec(dllimport) #define MONGO_API_EXPORT __declspec(dllexport) #else #define MONGO_API_CALL #define MONGO_API_IMPORT __attribute__((visibility("default"))) #define MONGO_API_EXPORT __attribute__((used, visibility("default"))) #endif #if defined(MONGO_CRYPT_SUPPORT_STATIC) #define MONGO_CRYPT_API #else #if defined(MONGO_CRYPT_SUPPORT_COMPILING) #define MONGO_CRYPT_API MONGO_API_EXPORT #else #define MONGO_CRYPT_API MONGO_API_IMPORT #endif #endif #ifdef __cplusplus extern "C" { #endif /** * An object which describes the details of the failure of an operation. * * The Mongo Crypt Shared Library uses allocated objects of this type to report the details of any * failure, when an operation cannot be completed. Several `mongo_crypt_v1_status` functions are * provided which permit observing the details of these failures. Further a construction function * and a destruction function for these objects are also provided. * * The use of status objects from multiple threads is not thread safe unless all of the threads * accessing a single status object are passing that object as a const-qualified (const * mongo_crypt_v1_status *) pointer. If a single thread is passing a status object to a function * taking it by non-const-qualified (mongo_crypt_v1_status*) pointer, then no other thread may * access the status object. * * The `status` parameter is optional for all `mongo_crypt_v1_` functions that can take a status * pointer. The caller may pass NULL instead of a valid `status` object, in which case the function * will execute normally but will not provide any detailed error information in the caes of a * failure. * * All `mongo_crypt_v1_status` functions can be used before the Mongo Crypt Shared library is * initialized. This facilitates detailed error reporting from all library functions. */ typedef struct mongo_crypt_v1_status mongo_crypt_v1_status; /** * Allocate and construct an API-return-status buffer object. * * Returns NULL when construction of a mongo_crypt_v1_status object fails. * * This function may be called before mongo_crypt_v1_lib_create(). */ MONGO_CRYPT_API mongo_crypt_v1_status* MONGO_API_CALL mongo_crypt_v1_status_create(void); /** * Destroys a valid status object. * * The status object must be a valid mongo_crypt_v1_status object or NULL. * * This function is not thread safe, and it must not execute concurrently with any other function * that accesses the status object being destroyed. It is, however, safe to destroy distinct status * objects on distinct threads. * * This function does not report failures. * * This function may be called before `mongo_crypt_v1_lib_create()`. * * This function causes all storage associated with the specified status object to be released, * including the storage referenced by functions that returned observable storage buffers from this * status, such as strings. */ MONGO_CRYPT_API void MONGO_API_CALL mongo_crypt_v1_status_destroy(mongo_crypt_v1_status* status); /** * The error codes reported by `mongo_crypt_v1` functions will be given the symbolic names as * mapped by this enum. * * When a `mongo_crypt_v1` function fails (and it has been documented to report errors) it will * report that error in the form of an `int` status code. That status code will always be returned * as the type `int`; however, the values in this enum can be used to classify the failure. */ typedef enum { MONGO_CRYPT_V1_ERROR_IN_REPORTING_ERROR = -2, MONGO_CRYPT_V1_ERROR_UNKNOWN = -1, MONGO_CRYPT_V1_SUCCESS = 0, MONGO_CRYPT_V1_ERROR_ENOMEM = 1, MONGO_CRYPT_V1_ERROR_EXCEPTION = 2, MONGO_CRYPT_V1_ERROR_LIBRARY_ALREADY_INITIALIZED = 3, MONGO_CRYPT_V1_ERROR_LIBRARY_NOT_INITIALIZED = 4, MONGO_CRYPT_V1_ERROR_INVALID_LIB_HANDLE = 5, MONGO_CRYPT_V1_ERROR_REENTRANCY_NOT_ALLOWED = 6, } mongo_crypt_v1_error; /** * Gets an error code from a `mongo_crypt_v1_status` object. * * When a `mongo_crypt_v1` function fails (and it has been documented to report errors) it will * report its error in the form of an `int` status code which is stored into a supplied * `mongo_crypt_v1_status` object, if provided. Some of these functions may also report extra * information, which will be reported by other observer functions. Every `mongo_crypt_v1` * function which reports errors will always update the `Error` code stored in a * `mongo_crypt_v1_status` object, even upon success. * * This function does not report its own failures. */ MONGO_CRYPT_API int MONGO_API_CALL mongo_crypt_v1_status_get_error(const mongo_crypt_v1_status* status); /** * Gets a descriptive error message from a `mongo_crypt_v1_status` object. * * For failures where the error is MONGO_CRYPT_V1_ERROR_EXCEPTION, this returns a string * representation of the internal C++ exception. * * The function to which the specified status object was passed must not have returned * MONGO_CRYPT_V1_SUCCESS as its error code. * * The storage for the returned string is associated with the specified status object, and therefore * it will be deallocated when the status is destroyed using mongo_crypt_v1_status_destroy(). * * This function does not report its own failures. */ MONGO_CRYPT_API const char* MONGO_API_CALL mongo_crypt_v1_status_get_explanation(const mongo_crypt_v1_status* status); /** * Gets a status code from a `mongo_crypt_v1_status` object. * * Returns a numeric status code associated with the status parameter which indicates a sub-category * of failure. * * For any status object that does not have MONGO_CRYPT_V1_ERROR_EXCEPTION as its error, the * value of this code is unspecified. * * This function does not report its own failures. */ MONGO_CRYPT_API int MONGO_API_CALL mongo_crypt_v1_status_get_code(const mongo_crypt_v1_status* status); /** * An object which describes the runtime state of the Mongo Crypt Shared Library. * * The Mongo Crypt Shared Library uses allocated objects of this type to indicate the present state * of the library. Some operations which the library provides need access to this object. Further a * construction function and a destruction function for these objects are also provided. No more * than a single object instance of this type may exist at any given time. * * The use of `mongo_crypt_v1_lib` objects from multiple threads is not thread safe unless all of * the threads accessing a single `mongo_crypt_v1_lib` object are not destroying this object. If * a single thread is passing a `mongo_crypt_v1_lib` to its destruction function, then no other * thread may access the `mongo_crypt_v1_lib` object. */ typedef struct mongo_crypt_v1_lib mongo_crypt_v1_lib; /** * Creates a mongo_crypt_v1_lib object, which stores context for the Mongo Crypt Shared library. A * process should only ever have one mongo_crypt_v1_lib instance. * * On failure, returns NULL and populates the 'status' object if it is not NULL. */ MONGO_CRYPT_API mongo_crypt_v1_lib* MONGO_API_CALL mongo_crypt_v1_lib_create(mongo_crypt_v1_status* status); /** * Tears down the state of this library. Existing mongo_crypt_v1_status objects remain valid. * Existing mongo_crypt_v1_query_analyzer objects created from this library MUST BE destroyed * before destroying the library object. * * The 'lib' parameter must be a valid mongo_crypt_v1_lib instance and must not be NULL. * * The 'status' parameter may be NULL, but must be a valid mongo_crypt_v1_status instance if it * is not NULL. * * Returns MONGO_CRYPT_V1_SUCCESS on success. * * Returns MONGO_CRYPT_V1_ERROR_LIBRARY_NOT_INITIALIZED and modifies 'status' if * mongo_crypt_v1_lib_create() has not been called previously. */ MONGO_CRYPT_API int MONGO_API_CALL mongo_crypt_v1_lib_destroy(mongo_crypt_v1_lib* lib, mongo_crypt_v1_status* status); /** * Returns a product version in 64-bit integer in four 16-bit words, from high to low: * * - Major version * - Minor version * - Revision * - Reserved * * For example, version 6.2.1 would be encoded as: 0x0006000200010000 * */ MONGO_CRYPT_API uint64_t MONGO_API_CALL mongo_crypt_v1_get_version(void); /** * Returns a product version as a text string. The string should not be modified or released * * Examples: * Dev Build/patch: mongo_crypt_v1-rhel80-6.2.1-alpha4-284-g8662e7d * Release build: mongo_crypt_v1-rhel80-6.2.1 */ MONGO_CRYPT_API const char* MONGO_API_CALL mongo_crypt_v1_get_version_str(void); /** * A single query analyzer can be used across repeated calls to mongo_crypt_v1_analyze_query. * * It is not safe to simultaneously invoke mongo_crypt_v1_query_analyzer_create on the same * query analyzer from multiple threads * * It is the client's responsibility to call mongo_crypt_v1_query_analyzer_destroy() to free up * resources used by the query analyzer. Once a query analyzer is destroyed, it is not safe to * call mongo_crypt_v1_analyze_query. */ typedef struct mongo_crypt_v1_query_analyzer mongo_crypt_v1_query_analyzer; /** * Creates a mongo_crypt_v1_query_analyzer object, which stores a parsed collation. * * The 'lib' parameter must be a valid mongo_crypt_v1_lib instance and must not be NULL. * * On failure, it returns NULL and populates the 'status' object if it is not NULL. */ MONGO_CRYPT_API mongo_crypt_v1_query_analyzer* MONGO_API_CALL mongo_crypt_v1_query_analyzer_create(mongo_crypt_v1_lib* lib, mongo_crypt_v1_status* status); /** * Destroys a valid mongo_crypt_v1_query_analyzer object. * * This function is not thread safe, and it must not execute concurrently with any other function * that accesses the collation object being destroyed including those that access a matcher, * projection or update object which references the collation object. * * This function does not report failures. */ MONGO_CRYPT_API void MONGO_API_CALL mongo_crypt_v1_query_analyzer_destroy(mongo_crypt_v1_query_analyzer* analyzer); /** * Analyzes the 'documentBSON' input document which includes a JSON schema for namespace 'ns_str' * and returns a new BSON document that replaces fields that need encryption with BinData * (subtype 6, first byte 0) placeholders. * * The input document must be a valid OP_MSG document supports aggregate, count, delete, distinct, * explain, find, findAndModify, insert and update. * In addition, the input document must include two additional top-level fields: * - jsonSchema : document - contains a valid JSON schema * - isRemoteSchema : bool - true if schema comes from MongoD as opposed to client-side * * The 'analyzer' and 'documentBSON' parameters must point to initialized objects of their * respective types. * * When the analysis is successful, this function returns non-null value which points to a valid * BSON document and updates bson_len with the length of the BSON document */ MONGO_CRYPT_API uint8_t* MONGO_API_CALL mongo_crypt_v1_analyze_query(mongo_crypt_v1_query_analyzer* analyzer, const uint8_t* documentBSON, const char* ns_str, uint32_t ns_len, uint32_t* bson_len, mongo_crypt_v1_status* status); /** * Free the memory of a BSON buffer returned by mongo_crypt_v1_analyze_query(). This function can be * safely called on a NULL pointer. * * This function can be called at any time to deallocate a BSON buffer and will not invalidate any * library object. */ MONGO_CRYPT_API void MONGO_API_CALL mongo_crypt_v1_bson_free(uint8_t* bson); #ifdef __cplusplus } // extern "C" #endif #undef MONGO_CRYPT_SUPPORT_API #pragma pop_macro("MONGO_CRYPT_SUPPORT_API") #undef MONGO_API_EXPORT #pragma push_macro("MONGO_API_EXPORT") #undef MONGO_API_IMPORT #pragma push_macro("MONGO_API_IMPORT") #undef MONGO_API_CALL #pragma pop_macro("MONGO_API_CALL") #endif // MONGO_CRYPT_SUPPORT_H libmongocrypt-1.19.0/src/mongocrypt-binary-private.h000066400000000000000000000015511521103432300225610ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef MONGOCRYPT_BINARY_PRIVATE_H #define MONGOCRYPT_BINARY_PRIVATE_H #include #include "mongocrypt.h" bool _mongocrypt_binary_to_bson(mongocrypt_binary_t *binary, bson_t *out) MONGOCRYPT_WARN_UNUSED_RESULT; #endif /* MONGOCRYPT_BINARY_PRIVATE_H */ libmongocrypt-1.19.0/src/mongocrypt-binary.c000066400000000000000000000034111521103432300211010ustar00rootroot00000000000000/* * Copyright 2018-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 #include "mongocrypt-binary-private.h" #include "mongocrypt-buffer-private.h" mongocrypt_binary_t *mongocrypt_binary_new(void) { mongocrypt_binary_t *binary; binary = (mongocrypt_binary_t *)bson_malloc0(sizeof *binary); return binary; } mongocrypt_binary_t *mongocrypt_binary_new_from_data(uint8_t *data, uint32_t len) { mongocrypt_binary_t *binary; BSON_ASSERT_PARAM(data); binary = (mongocrypt_binary_t *)bson_malloc0(sizeof *binary); BSON_ASSERT(binary); binary->data = data; binary->len = len; return binary; } bool _mongocrypt_binary_to_bson(mongocrypt_binary_t *binary, bson_t *out) { BSON_ASSERT_PARAM(binary); BSON_ASSERT_PARAM(out); return bson_init_static(out, binary->data, binary->len); } uint8_t *mongocrypt_binary_data(const mongocrypt_binary_t *binary) { if (!binary) { return NULL; } return binary->data; } uint32_t mongocrypt_binary_len(const mongocrypt_binary_t *binary) { if (!binary) { return 0; } return binary->len; } void mongocrypt_binary_destroy(mongocrypt_binary_t *binary) { if (!binary) { return; } bson_free(binary); } libmongocrypt-1.19.0/src/mongocrypt-buffer-private.h000066400000000000000000000165571521103432300225620ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef MONGOCRYPT_BUFFER_H #define MONGOCRYPT_BUFFER_H #include "mongocrypt-binary-private.h" #include "mongocrypt-compat.h" #include #define UUID_LEN 16 #define PRF_LEN 32 struct _mongocrypt_binary_t; /* An internal struct to make working with binary values more convenient. * - a non-owning buffer can be constructed from a bson_iter_t. * - a non-owning buffer can become an owned buffer by copying. * - a buffer can be appended as a BSON binary in a bson_t. */ typedef struct __mongocrypt_buffer_t { uint8_t *data; uint32_t len; bool owned; bson_subtype_t subtype; mongocrypt_binary_t bin; } _mongocrypt_buffer_t; void _mongocrypt_buffer_init(_mongocrypt_buffer_t *buf); void _mongocrypt_buffer_resize(_mongocrypt_buffer_t *buf, uint32_t len); void _mongocrypt_buffer_init_size(_mongocrypt_buffer_t *buf, uint32_t len); void _mongocrypt_buffer_steal(_mongocrypt_buffer_t *buf, _mongocrypt_buffer_t *src); /* @iter is iterated to a BSON binary value. */ bool _mongocrypt_buffer_copy_from_binary_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; /* @iter is iterated to a BSON binary value. */ bool _mongocrypt_buffer_from_binary_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; /* @iter is iterated to a BSON document value. */ bool _mongocrypt_buffer_from_document_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; /* @iter is iterated to a BSON document value. */ bool _mongocrypt_buffer_copy_from_document_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; void _mongocrypt_buffer_steal_from_bson(_mongocrypt_buffer_t *buf, bson_t *bson); void _mongocrypt_buffer_from_bson(_mongocrypt_buffer_t *buf, const bson_t *bson); bool _mongocrypt_buffer_to_bson(const _mongocrypt_buffer_t *buf, bson_t *bson) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_buffer_append(const _mongocrypt_buffer_t *buf, bson_t *bson, const char *key, int key_len) MONGOCRYPT_WARN_UNUSED_RESULT; void _mongocrypt_buffer_from_binary(_mongocrypt_buffer_t *buf, const struct _mongocrypt_binary_t *binary); void _mongocrypt_buffer_copy_from_binary(_mongocrypt_buffer_t *buf, const struct _mongocrypt_binary_t *binary); void _mongocrypt_buffer_to_binary(const _mongocrypt_buffer_t *buf, struct _mongocrypt_binary_t *binary); void _mongocrypt_buffer_copy_to(const _mongocrypt_buffer_t *src, _mongocrypt_buffer_t *dst); void _mongocrypt_buffer_set_to(const _mongocrypt_buffer_t *src, _mongocrypt_buffer_t *dst); int _mongocrypt_buffer_cmp(const _mongocrypt_buffer_t *a, const _mongocrypt_buffer_t *b); void _mongocrypt_buffer_cleanup(_mongocrypt_buffer_t *buf); bool _mongocrypt_buffer_empty(const _mongocrypt_buffer_t *buf); bool _mongocrypt_buffer_to_bson_value(_mongocrypt_buffer_t *plaintext, uint8_t type, bson_value_t *out) MONGOCRYPT_WARN_UNUSED_RESULT; void _mongocrypt_buffer_from_iter(_mongocrypt_buffer_t *plaintext, bson_iter_t *iter); bool _mongocrypt_buffer_from_uuid_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_buffer_copy_from_uuid_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_buffer_is_uuid(_mongocrypt_buffer_t *buf) MONGOCRYPT_WARN_UNUSED_RESULT; void _mongocrypt_buffer_copy_from_hex(_mongocrypt_buffer_t *buf, const char *hex); int _mongocrypt_buffer_cmp_hex(_mongocrypt_buffer_t *buf, const char *hex); bool _mongocrypt_buffer_concat(_mongocrypt_buffer_t *dst, const _mongocrypt_buffer_t *srcs, uint32_t num_srcs); struct _mongocrypt_binary_t *_mongocrypt_buffer_as_binary(_mongocrypt_buffer_t *buf); /* _mongocrypt_buffer_copy_from_data_and_size initializes @buf and copies @len * bytes from @data. * - Returns false on error. * - Caller must call _mongocrypt_buffer_cleanup. */ bool _mongocrypt_buffer_copy_from_data_and_size(_mongocrypt_buffer_t *buf, const uint8_t *data, size_t len) MONGOCRYPT_WARN_UNUSED_RESULT; /* _mongocrypt_buffer_steal_from_data_and_size initializes @buf from @data and * @len and takes ownership of @data. * - Returns false on error. * - @buf does not take ownership of @str on error. * - Caller must call _mongocrypt_buffer_cleanup. */ bool _mongocrypt_buffer_steal_from_data_and_size(_mongocrypt_buffer_t *buf, uint8_t *data, size_t len) MONGOCRYPT_WARN_UNUSED_RESULT; /* _mongocrypt_buffer_steal_from_string initializes @buf from @str and takes * ownership of @str. * @buf retains a pointer to @str. * @str must be NULL terminated. * - Returns false on error. * - @buf does not take ownership of @str on error. * - Caller must call _mongocrypt_buffer_cleanup. */ bool _mongocrypt_buffer_steal_from_string(_mongocrypt_buffer_t *buf, char *str) MONGOCRYPT_WARN_UNUSED_RESULT; /* _mongocrypt_buffer_from_string initializes @buf from @str. * @buf retains a pointer to @str. * @str must outlive @buf. * @str must be NULL terminated. * - Returns false on error. * - Caller must call _mongocrypt_buffer_cleanup. */ bool _mongocrypt_buffer_from_string(_mongocrypt_buffer_t *buf, const char *str) MONGOCRYPT_WARN_UNUSED_RESULT; /* _mongocrypt_buffer_from_ initializes @buf from @data with length @len. * @buf retains a pointer to @data. * @data must outlive @buf. */ void _mongocrypt_buffer_from_data(_mongocrypt_buffer_t *buf, const uint8_t *data, uint32_t len); /* _mongocrypt_buffer_copy_from_uint64_le initializes @buf from the * little-endian byte representation of @value. Caller must call * _mongocrypt_buffer_cleanup. * @value is expected to be in machine's native endianness. */ void _mongocrypt_buffer_copy_from_uint64_le(_mongocrypt_buffer_t *buf, uint64_t value); /* _mongocrypt_buffer_from_subrange initializes @out as a non-owning buffer to a * range of data from @in specified by @offset and @len. Returns false on error. */ bool _mongocrypt_buffer_from_subrange(_mongocrypt_buffer_t *out, const _mongocrypt_buffer_t *in, uint32_t offset, uint32_t len) MONGOCRYPT_WARN_UNUSED_RESULT; /* _mongocrypt_buffer_copy_from_string_as_bson_value initializes @out, wraps the provided string * into a BSON value, and copies the BSON value to @out. No BSON validation is performed on @str. * Caller must call _mongocrypt_buffer_cleanup. */ void _mongocrypt_buffer_copy_from_string_as_bson_value(_mongocrypt_buffer_t *out, const char *str, int len); #endif /* MONGOCRYPT_BUFFER_H */ libmongocrypt-1.19.0/src/mongocrypt-buffer.c000066400000000000000000000375331521103432300211020ustar00rootroot00000000000000/* * Copyright 2018-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-buffer-private.h" #include "mongocrypt-endian-private.h" #include "mongocrypt-util-private.h" #include // Require libbson 1.16.0 or newer. Fix for CDRIVER-3360 is needed. #if !BSON_CHECK_VERSION(1, 16, 0) #error "libbson 1.16.0 or newer is required." #endif #define INT32_LEN 4 #define TYPE_LEN 1 #define NULL_BYTE_LEN 1 #define NULL_BYTE_VAL 0x00 /* if a buffer is not owned, copy the data and make it owned. */ static void _make_owned(_mongocrypt_buffer_t *buf) { uint8_t *tmp; BSON_ASSERT_PARAM(buf); if (buf->owned) { return; } tmp = buf->data; if (buf->len > 0) { buf->data = bson_malloc(buf->len); BSON_ASSERT(buf->data); memcpy(buf->data, tmp, buf->len); } else { buf->data = NULL; } buf->owned = true; } /* TODO CDRIVER-2990 have buffer operations require initialized buffer to * prevent leaky code. */ void _mongocrypt_buffer_init(_mongocrypt_buffer_t *buf) { BSON_ASSERT_PARAM(buf); memset(buf, 0, sizeof(*buf)); } void _mongocrypt_buffer_resize(_mongocrypt_buffer_t *buf, uint32_t len) { BSON_ASSERT_PARAM(buf); /* Currently this just wipes whatever was in data before, but a fancier implementation could copy over up to 'len' bytes from the old buffer to the new one. */ if (buf->owned) { buf->data = bson_realloc(buf->data, len); buf->len = len; return; } buf->data = bson_malloc(len); BSON_ASSERT(buf->data); buf->len = len; buf->owned = true; } void _mongocrypt_buffer_init_size(_mongocrypt_buffer_t *buf, uint32_t len) { BSON_ASSERT_PARAM(buf); _mongocrypt_buffer_init(buf); _mongocrypt_buffer_resize(buf, len); } void _mongocrypt_buffer_steal(_mongocrypt_buffer_t *buf, _mongocrypt_buffer_t *src) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(src); if (!src->owned) { _mongocrypt_buffer_copy_to(src, buf); _mongocrypt_buffer_init(src); return; } buf->data = src->data; buf->len = src->len; buf->owned = true; _mongocrypt_buffer_init(src); } bool _mongocrypt_buffer_from_binary_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!BSON_ITER_HOLDS_BINARY(iter)) { return false; } _mongocrypt_buffer_init(buf); bson_iter_binary(iter, &buf->subtype, &buf->len, (const uint8_t **)&buf->data); buf->owned = false; return true; } bool _mongocrypt_buffer_copy_from_binary_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!_mongocrypt_buffer_from_binary_iter(buf, iter)) { return false; } _make_owned(buf); return true; } bool _mongocrypt_buffer_from_document_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!BSON_ITER_HOLDS_DOCUMENT(iter)) { return false; } _mongocrypt_buffer_init(buf); bson_iter_document(iter, &buf->len, (const uint8_t **)&buf->data); buf->owned = false; return true; } bool _mongocrypt_buffer_copy_from_document_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!_mongocrypt_buffer_from_document_iter(buf, iter)) { return false; } _make_owned(buf); return true; } void _mongocrypt_buffer_steal_from_bson(_mongocrypt_buffer_t *buf, bson_t *bson) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(bson); _mongocrypt_buffer_init(buf); buf->data = bson_destroy_with_steal(bson, true, &buf->len); buf->owned = true; } void _mongocrypt_buffer_from_bson(_mongocrypt_buffer_t *buf, const bson_t *bson) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(bson); _mongocrypt_buffer_init(buf); buf->data = (uint8_t *)bson_get_data(bson); buf->len = bson->len; buf->owned = false; } bool _mongocrypt_buffer_to_bson(const _mongocrypt_buffer_t *buf, bson_t *bson) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(bson); return bson_init_static(bson, buf->data, buf->len); } bool _mongocrypt_buffer_append(const _mongocrypt_buffer_t *buf, bson_t *bson, const char *key, int key_len) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(bson); BSON_ASSERT_PARAM(key); return bson_append_binary(bson, key, key_len, buf->subtype, buf->data, buf->len); } void _mongocrypt_buffer_from_binary(_mongocrypt_buffer_t *buf, const mongocrypt_binary_t *binary) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(binary); _mongocrypt_buffer_init(buf); buf->data = binary->data; buf->len = binary->len; buf->owned = false; } void _mongocrypt_buffer_copy_from_binary(_mongocrypt_buffer_t *buf, const struct _mongocrypt_binary_t *binary) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(binary); _mongocrypt_buffer_from_binary(buf, binary); _make_owned(buf); } void _mongocrypt_buffer_to_binary(const _mongocrypt_buffer_t *buf, mongocrypt_binary_t *binary) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(binary); binary->data = buf->data; binary->len = buf->len; } void _mongocrypt_buffer_copy_to(const _mongocrypt_buffer_t *src, _mongocrypt_buffer_t *dst) { if (src == dst) { return; } BSON_ASSERT_PARAM(src); BSON_ASSERT_PARAM(dst); _mongocrypt_buffer_cleanup(dst); if (src->len == 0) { return; } dst->data = bson_malloc((size_t)src->len); BSON_ASSERT(dst->data); memcpy(dst->data, src->data, src->len); dst->len = src->len; dst->subtype = src->subtype; dst->owned = true; } void _mongocrypt_buffer_set_to(const _mongocrypt_buffer_t *src, _mongocrypt_buffer_t *dst) { if (src == dst) { return; } BSON_ASSERT_PARAM(src); BSON_ASSERT_PARAM(dst); dst->data = src->data; dst->len = src->len; dst->subtype = src->subtype; dst->owned = false; } int _mongocrypt_buffer_cmp(const _mongocrypt_buffer_t *a, const _mongocrypt_buffer_t *b) { BSON_ASSERT_PARAM(a); BSON_ASSERT_PARAM(b); if (a->len != b->len) { return a->len > b->len ? 1 : -1; } if (0 == a->len) { return 0; } return memcmp(a->data, b->data, a->len); } void _mongocrypt_buffer_cleanup(_mongocrypt_buffer_t *buf) { if (buf && buf->owned) { bson_free(buf->data); } } bool _mongocrypt_buffer_empty(const _mongocrypt_buffer_t *buf) { BSON_ASSERT_PARAM(buf); return buf->data == NULL; } bool _mongocrypt_buffer_to_bson_value(_mongocrypt_buffer_t *plaintext, uint8_t type, bson_value_t *out) { bool ret = false; bson_iter_t iter; bson_t wrapper; uint32_t data_len; uint32_t le_data_len; uint8_t *data; uint8_t data_prefix; BSON_ASSERT_PARAM(plaintext); BSON_ASSERT_PARAM(out); data_prefix = INT32_LEN /* adds document size */ + TYPE_LEN /* element type */ + NULL_BYTE_LEN; /* and doc's null byte terminator */ BSON_ASSERT(plaintext->len <= UINT32_MAX - data_prefix - NULL_BYTE_LEN); data_len = (plaintext->len + data_prefix + NULL_BYTE_LEN); le_data_len = BSON_UINT32_TO_LE(data_len); data = bson_malloc0(data_len); BSON_ASSERT(data); memcpy(data + data_prefix, plaintext->data, plaintext->len); memcpy(data, &le_data_len, INT32_LEN); memcpy(data + INT32_LEN, &type, TYPE_LEN); data[data_len - 1] = NULL_BYTE_VAL; if (!bson_init_static(&wrapper, data, data_len)) { goto fail; } if (!bson_validate(&wrapper, BSON_VALIDATE_NONE, NULL)) { goto fail; } if (!bson_iter_init_find(&iter, &wrapper, "")) { goto fail; } bson_value_copy(bson_iter_value(&iter), out); ret = true; fail: bson_free(data); return ret; } static void _mongocrypt_buffer_copy_as_bson_value(_mongocrypt_buffer_t *plaintext, bool (*append_func)(bson_t *bson, const void *data, int len), const void *data, int len) { bson_t wrapper = BSON_INITIALIZER; int32_t offset = INT32_LEN /* skips document size */ + TYPE_LEN /* element type */ + NULL_BYTE_LEN; /* and the key's null byte terminator */ uint8_t *wrapper_data; BSON_ASSERT_PARAM(plaintext); BSON_ASSERT_PARAM(append_func); /* It is not straightforward to transform a bson_value_t to a string of * bytes. As a workaround, we wrap the value in a bson document with an empty * key, then use the raw buffer from inside the new bson_t, skipping the * length and type header information and the key name. */ append_func(&wrapper, data, len); wrapper_data = ((uint8_t *)bson_get_data(&wrapper)); BSON_ASSERT(wrapper.len >= (uint32_t)offset + NULL_BYTE_LEN); plaintext->len = wrapper.len - (uint32_t)offset - NULL_BYTE_LEN; /* the final null byte */ plaintext->data = bson_malloc(plaintext->len); BSON_ASSERT(plaintext->data); plaintext->owned = true; memcpy(plaintext->data, wrapper_data + offset, plaintext->len); bson_destroy(&wrapper); } static bool _append_iter(bson_t *bson, const void *iter, int len) { return bson_append_iter(bson, "", 0, (const bson_iter_t *)iter); } static bool _append_utf8(bson_t *bson, const void *str, int len) { return bson_append_utf8(bson, "", 0, (const char *)str, len); } void _mongocrypt_buffer_copy_from_string_as_bson_value(_mongocrypt_buffer_t *plaintext, const char *str, int len) { BSON_ASSERT_PARAM(str); BSON_ASSERT(len >= 0); _mongocrypt_buffer_copy_as_bson_value(plaintext, _append_utf8, str, len); } void _mongocrypt_buffer_from_iter(_mongocrypt_buffer_t *plaintext, bson_iter_t *iter) { BSON_ASSERT_PARAM(iter); _mongocrypt_buffer_copy_as_bson_value(plaintext, _append_iter, iter, 0); } bool _mongocrypt_buffer_from_uuid_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { const uint8_t *data; bson_subtype_t subtype; uint32_t len; BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!BSON_ITER_HOLDS_BINARY(iter)) { return false; } bson_iter_binary(iter, &subtype, &len, &data); if (subtype != BSON_SUBTYPE_UUID) { return false; } if (len != UUID_LEN) { return false; } _mongocrypt_buffer_init(buf); buf->data = (uint8_t *)data; buf->len = len; buf->subtype = subtype; buf->owned = false; return true; } bool _mongocrypt_buffer_copy_from_uuid_iter(_mongocrypt_buffer_t *buf, bson_iter_t *iter) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(iter); if (!_mongocrypt_buffer_from_uuid_iter(buf, iter)) { return false; } _make_owned(buf); return true; } bool _mongocrypt_buffer_is_uuid(_mongocrypt_buffer_t *buf) { BSON_ASSERT_PARAM(buf); return buf->len == UUID_LEN && buf->subtype == BSON_SUBTYPE_UUID; } void _mongocrypt_buffer_copy_from_hex(_mongocrypt_buffer_t *buf, const char *hex) { uint32_t i; size_t hex_len; BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(hex); hex_len = strlen(hex); if (hex_len == 0) { _mongocrypt_buffer_init(buf); return; } BSON_ASSERT(hex_len / 2u <= UINT32_MAX); buf->len = (uint32_t)(hex_len / 2u); buf->data = bson_malloc(buf->len); BSON_ASSERT(buf->data); buf->owned = true; for (i = 0; i < buf->len; i++) { uint32_t tmp; BSON_ASSERT(i <= UINT32_MAX / 2); BSON_ASSERT(sscanf(hex + (2 * i), "%02x", &tmp)); *(buf->data + i) = (uint8_t)tmp; } } int _mongocrypt_buffer_cmp_hex(_mongocrypt_buffer_t *buf, const char *hex) { _mongocrypt_buffer_t tmp; int res; BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(hex); _mongocrypt_buffer_copy_from_hex(&tmp, hex); res = _mongocrypt_buffer_cmp(buf, &tmp); _mongocrypt_buffer_cleanup(&tmp); return res; } bool _mongocrypt_buffer_concat(_mongocrypt_buffer_t *dst, const _mongocrypt_buffer_t *srcs, uint32_t num_srcs) { uint32_t total = 0; uint32_t offset; uint32_t i; BSON_ASSERT_PARAM(dst); BSON_ASSERT_PARAM(srcs); for (i = 0; i < num_srcs; i++) { uint32_t old_total = total; total += srcs[i].len; /* If the previous operation overflowed, then total will have a smaller * value than previously. */ if (total < old_total) { return false; } } _mongocrypt_buffer_init(dst); _mongocrypt_buffer_resize(dst, total); offset = 0; for (i = 0; i < num_srcs; i++) { if (srcs[i].len) { memcpy(dst->data + offset, srcs[i].data, srcs[i].len); } offset += srcs[i].len; } return true; } struct _mongocrypt_binary_t *_mongocrypt_buffer_as_binary(_mongocrypt_buffer_t *buf) { BSON_ASSERT_PARAM(buf); buf->bin.data = buf->data; buf->bin.len = buf->len; return &buf->bin; } bool _mongocrypt_buffer_copy_from_data_and_size(_mongocrypt_buffer_t *buf, const uint8_t *data, size_t len) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(data); _mongocrypt_buffer_init(buf); if (!size_to_uint32(len, &buf->len)) { return false; } if ((buf->data = bson_malloc(len))) { memcpy(buf->data, data, len); buf->owned = true; } return true; } bool _mongocrypt_buffer_steal_from_data_and_size(_mongocrypt_buffer_t *buf, uint8_t *data, size_t len) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(data); _mongocrypt_buffer_init(buf); if (!size_to_uint32(len, &buf->len)) { return false; } buf->data = data; buf->owned = true; return true; } bool _mongocrypt_buffer_steal_from_string(_mongocrypt_buffer_t *buf, char *str) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(str); _mongocrypt_buffer_init(buf); if (!size_to_uint32(strlen(str), &buf->len)) { return false; } buf->data = (uint8_t *)str; buf->owned = true; return true; } bool _mongocrypt_buffer_from_string(_mongocrypt_buffer_t *buf, const char *str) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(str); _mongocrypt_buffer_init(buf); if (!size_to_uint32(strlen(str), &buf->len)) { return false; } buf->data = (uint8_t *)str; buf->owned = false; return true; } void _mongocrypt_buffer_from_data(_mongocrypt_buffer_t *buf, const uint8_t *data, uint32_t len) { BSON_ASSERT_PARAM(buf); BSON_ASSERT_PARAM(data); _mongocrypt_buffer_init(buf); buf->data = (uint8_t *)data; buf->len = len; buf->owned = false; } void _mongocrypt_buffer_copy_from_uint64_le(_mongocrypt_buffer_t *buf, uint64_t value) { uint64_t value_le = MONGOCRYPT_UINT64_TO_LE(value); BSON_ASSERT_PARAM(buf); _mongocrypt_buffer_init(buf); _mongocrypt_buffer_resize(buf, sizeof(value)); memcpy(buf->data, &value_le, buf->len); } bool _mongocrypt_buffer_from_subrange(_mongocrypt_buffer_t *out, const _mongocrypt_buffer_t *in, uint32_t offset, uint32_t len) { BSON_ASSERT_PARAM(out); BSON_ASSERT_PARAM(in); _mongocrypt_buffer_init(out); BSON_ASSERT(offset <= UINT32_MAX - len); if (offset + len > in->len) { return false; } out->data = in->data + offset; out->len = len; return true; } libmongocrypt-1.19.0/src/mongocrypt-cache-collinfo-private.h000066400000000000000000000015171521103432300241450ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MONGOCRYPT_CACHE_COLLINFO_PRIVATE_H #define MONGOCRYPT_CACHE_COLLINFO_PRIVATE_H #include "mongocrypt-cache-private.h" void _mongocrypt_cache_collinfo_init(_mongocrypt_cache_t *cache); #endif /* MONGOCRYPT_CACHE_COLLINFO_PRIVATE_H */ libmongocrypt-1.19.0/src/mongocrypt-cache-collinfo.c000066400000000000000000000033601521103432300224660ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mongocrypt-cache-collinfo-private.h" // #include "mongocrypt-cache-private.h" /* The collinfo cache. * * Attribute is a null terminated namespace. * Value is a collection info doc (response to listCollections). */ static bool _cmp_attr(void *a, void *b, int *out) { BSON_ASSERT_PARAM(a); BSON_ASSERT_PARAM(b); BSON_ASSERT_PARAM(out); *out = strcmp((char *)a, (char *)b); return true; } static void *_copy_attr(void *ns) { BSON_ASSERT_PARAM(ns); return bson_strdup((const char *)ns); } static void _destroy_attr(void *ns) { bson_free(ns); } static void *_copy_value(void *bson) { BSON_ASSERT_PARAM(bson); return bson_copy(bson); } static void _destroy_value(void *bson) { bson_destroy(bson); } void _mongocrypt_cache_collinfo_init(_mongocrypt_cache_t *cache) { BSON_ASSERT_PARAM(cache); cache->cmp_attr = _cmp_attr; cache->copy_attr = _copy_attr; cache->destroy_attr = _destroy_attr; cache->copy_value = _copy_value; cache->destroy_value = _destroy_value; _mongocrypt_mutex_init(&cache->mutex); cache->pair = NULL; cache->expiration = CACHE_EXPIRATION_MS_DEFAULT; } libmongocrypt-1.19.0/src/mongocrypt-cache-key-private.h000066400000000000000000000034771521103432300231370ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef MONGOCRYPT_CACHE_KEY_PRIVATE_H #define MONGOCRYPT_CACHE_KEY_PRIVATE_H #include "mongocrypt-buffer-private.h" #include "mongocrypt-cache-private.h" #include "mongocrypt-key-private.h" #include "mongocrypt-mutex-private.h" #include "mongocrypt-opts-private.h" #include "mongocrypt-status-private.h" typedef struct { _mongocrypt_key_doc_t *key_doc; _mongocrypt_buffer_t decrypted_key_material; } _mongocrypt_cache_key_value_t; typedef struct { _mongocrypt_buffer_t id; /* may be empty */ _mongocrypt_key_alt_name_t *alt_names; /* may be NULL */ } _mongocrypt_cache_key_attr_t; void _mongocrypt_cache_key_init(_mongocrypt_cache_t *cache); _mongocrypt_cache_key_attr_t *_mongocrypt_cache_key_attr_new(_mongocrypt_buffer_t *id, _mongocrypt_key_alt_name_t *alt_names); _mongocrypt_cache_key_value_t *_mongocrypt_cache_key_value_new(_mongocrypt_key_doc_t *key_doc, _mongocrypt_buffer_t *decrypted_key_material); void _mongocrypt_cache_key_value_destroy(void *value); void _mongocrypt_cache_key_attr_destroy(_mongocrypt_cache_key_attr_t *attr); #endif /* MONGOCRYPT_CACHE_KEY_PRIVATE_H */ libmongocrypt-1.19.0/src/mongocrypt-cache-key.c000066400000000000000000000105271521103432300214540ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mongocrypt-cache-key-private.h" /* The key cache. * * Attribute is a UUID in the form of a _mongocrypt_buffer_t. * Value contains a key document and decrypted key material. */ /* returns true on success. out is set to 0 if equal, non-zero otherwise. */ static bool _cmp_attr(void *a, void *b, int *out) { _mongocrypt_cache_key_attr_t *attr_a, *attr_b; BSON_ASSERT_PARAM(a); BSON_ASSERT_PARAM(b); BSON_ASSERT_PARAM(out); *out = 1; attr_a = (_mongocrypt_cache_key_attr_t *)a; attr_b = (_mongocrypt_cache_key_attr_t *)b; if (!_mongocrypt_buffer_empty(&attr_a->id) && !_mongocrypt_buffer_empty(&attr_b->id)) { if (0 == _mongocrypt_buffer_cmp(&attr_a->id, &attr_b->id)) { *out = 0; } } if (_mongocrypt_key_alt_name_intersects(attr_a->alt_names, attr_b->alt_names)) { *out = 0; } /* No error. */ return true; } static void *_copy_attr(void *attr) { _mongocrypt_cache_key_attr_t *src; BSON_ASSERT_PARAM(attr); src = (_mongocrypt_cache_key_attr_t *)attr; return _mongocrypt_cache_key_attr_new(&src->id, src->alt_names); } static void _destroy_attr(void *attr) { _mongocrypt_cache_key_attr_destroy(attr); } static void *_copy_contents(void *value) { _mongocrypt_cache_key_value_t *key_value; BSON_ASSERT_PARAM(value); key_value = (_mongocrypt_cache_key_value_t *)value; return _mongocrypt_cache_key_value_new(key_value->key_doc, &key_value->decrypted_key_material); } _mongocrypt_cache_key_value_t *_mongocrypt_cache_key_value_new(_mongocrypt_key_doc_t *key_doc, _mongocrypt_buffer_t *decrypted_key_material) { _mongocrypt_cache_key_value_t *key_value; BSON_ASSERT_PARAM(key_doc); BSON_ASSERT_PARAM(decrypted_key_material); key_value = bson_malloc0(sizeof(*key_value)); BSON_ASSERT(key_value); _mongocrypt_buffer_copy_to(decrypted_key_material, &key_value->decrypted_key_material); key_value->key_doc = _mongocrypt_key_new(); _mongocrypt_key_doc_copy_to(key_doc, key_value->key_doc); return key_value; } void _mongocrypt_cache_key_value_destroy(void *value) { _mongocrypt_cache_key_value_t *key_value; if (!value) { return; } key_value = (_mongocrypt_cache_key_value_t *)value; _mongocrypt_key_destroy(key_value->key_doc); _mongocrypt_buffer_cleanup(&key_value->decrypted_key_material); bson_free(key_value); } void _mongocrypt_cache_key_init(_mongocrypt_cache_t *cache) { BSON_ASSERT_PARAM(cache); cache->cmp_attr = _cmp_attr; cache->copy_attr = _copy_attr; cache->destroy_attr = _destroy_attr; cache->copy_value = _copy_contents; cache->destroy_value = _mongocrypt_cache_key_value_destroy; _mongocrypt_mutex_init(&cache->mutex); cache->pair = NULL; cache->expiration = CACHE_EXPIRATION_MS_DEFAULT; } /* Since key cache may be looked up by either _id or keyAltName, * "id" or "alt_names" may be NULL, but not both. Returns NULL on error. */ _mongocrypt_cache_key_attr_t *_mongocrypt_cache_key_attr_new(_mongocrypt_buffer_t *id, _mongocrypt_key_alt_name_t *alt_names) { _mongocrypt_cache_key_attr_t *attr; if (NULL == id && NULL == alt_names) { return NULL; } attr = bson_malloc0(sizeof(*attr)); BSON_ASSERT(attr); if (id) { _mongocrypt_buffer_copy_to(id, &attr->id); } attr->alt_names = _mongocrypt_key_alt_name_copy_all(alt_names); return attr; } void _mongocrypt_cache_key_attr_destroy(_mongocrypt_cache_key_attr_t *attr) { if (!attr) { return; } _mongocrypt_buffer_cleanup(&attr->id); _mongocrypt_key_alt_name_destroy_all(attr->alt_names); bson_free(attr); } libmongocrypt-1.19.0/src/mongocrypt-cache-oauth-private.h000066400000000000000000000032221521103432300234530ustar00rootroot00000000000000/* * Copyright 2020-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. */ #ifndef MONGOCRYPT_CACHE_OAUTH_PRIVATE_H #define MONGOCRYPT_CACHE_OAUTH_PRIVATE_H #include "mongocrypt-mutex-private.h" #include "mongocrypt-status-private.h" // `mc_mapof_kmsid_to_token_t` maps a KMS ID (e.g. `azure` or `azure:myname`) to an OAuth token. typedef struct _mc_mapof_kmsid_to_token_t mc_mapof_kmsid_to_token_t; mc_mapof_kmsid_to_token_t *mc_mapof_kmsid_to_token_new(void); void mc_mapof_kmsid_to_token_destroy(mc_mapof_kmsid_to_token_t *k2t); // `mc_mapof_kmsid_to_token_get_token` returns a copy of the base64 encoded oauth token, or NULL. // Thread-safe. char *mc_mapof_kmsid_to_token_get_token(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid); // `mc_mapof_kmsid_to_token_add_response` overwrites an entry if `kms_id` exists. // Thread-safe. bool mc_mapof_kmsid_to_token_add_response(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid, bson_t *response, mongocrypt_status_t *status); #endif /* MONGOCRYPT_CACHE_OAUTH_PRIVATE_H */ libmongocrypt-1.19.0/src/mongocrypt-cache-oauth.c000066400000000000000000000125211521103432300220000ustar00rootroot00000000000000/* * Copyright 2020-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-cache-oauth-private.h" #include "mc-array-private.h" #include "mongocrypt-private.h" /* How long before the reported "expires_in" time cache entries get evicted. * This is intended to prevent use of an oauth token too close to the expiration * time. */ #define MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US 5000 * 1000 typedef struct { char *kmsid; char *access_token; int64_t expiration_time_us; } mc_mapof_kmsid_to_token_entry_t; struct _mc_mapof_kmsid_to_token_t { mc_array_t entries; mongocrypt_mutex_t mutex; // Guards `entries`. }; mc_mapof_kmsid_to_token_t *mc_mapof_kmsid_to_token_new(void) { mc_mapof_kmsid_to_token_t *k2t = bson_malloc0(sizeof(mc_mapof_kmsid_to_token_t)); _mc_array_init(&k2t->entries, sizeof(mc_mapof_kmsid_to_token_entry_t)); _mongocrypt_mutex_init(&k2t->mutex); return k2t; } void mc_mapof_kmsid_to_token_destroy(mc_mapof_kmsid_to_token_t *k2t) { if (!k2t) { return; } _mongocrypt_mutex_cleanup(&k2t->mutex); for (size_t i = 0; i < k2t->entries.len; i++) { mc_mapof_kmsid_to_token_entry_t k2te = _mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i); bson_free(k2te.kmsid); bson_free(k2te.access_token); } _mc_array_destroy(&k2t->entries); bson_free(k2t); } char *mc_mapof_kmsid_to_token_get_token(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid) { BSON_ASSERT_PARAM(k2t); BSON_ASSERT_PARAM(kmsid); _mongocrypt_mutex_lock(&k2t->mutex); for (size_t i = 0; i < k2t->entries.len; i++) { mc_mapof_kmsid_to_token_entry_t k2te = _mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i); if (0 == strcmp(k2te.kmsid, kmsid)) { if (bson_get_monotonic_time() >= k2te.expiration_time_us) { // Expired. _mongocrypt_mutex_unlock(&k2t->mutex); return NULL; } char *access_token = bson_strdup(k2te.access_token); _mongocrypt_mutex_unlock(&k2t->mutex); return access_token; } } _mongocrypt_mutex_unlock(&k2t->mutex); return NULL; } bool mc_mapof_kmsid_to_token_add_response(mc_mapof_kmsid_to_token_t *k2t, const char *kmsid, bson_t *response, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(k2t); BSON_ASSERT_PARAM(kmsid); BSON_ASSERT_PARAM(response); // Parse access token before locking. const char *access_token; int64_t expiration_time_us; { bson_iter_t iter; int64_t cache_time_us; int64_t expires_in_s; int64_t expires_in_us; /* The OAuth spec strongly implies that the value of expires_in is positive, * so the overflow checks in this function don't consider negative values. */ if (!bson_iter_init_find(&iter, response, "expires_in") || !BSON_ITER_HOLDS_INT(&iter)) { CLIENT_ERR("OAuth response invalid, no 'expires_in' field."); return false; } cache_time_us = bson_get_monotonic_time(); expires_in_s = bson_iter_as_int64(&iter); BSON_ASSERT(expires_in_s <= INT64_MAX / 1000 / 1000); expires_in_us = expires_in_s * 1000 * 1000; BSON_ASSERT(expires_in_us <= INT64_MAX - cache_time_us && expires_in_us + cache_time_us > MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US); expiration_time_us = expires_in_us + cache_time_us - MONGOCRYPT_OAUTH_CACHE_EVICTION_PERIOD_US; if (!bson_iter_init_find(&iter, response, "access_token") || !BSON_ITER_HOLDS_UTF8(&iter)) { CLIENT_ERR("OAuth response invalid, no 'access_token' field."); return false; } access_token = bson_iter_utf8(&iter, NULL); } _mongocrypt_mutex_lock(&k2t->mutex); // Check if there is an existing entry. for (size_t i = 0; i < k2t->entries.len; i++) { mc_mapof_kmsid_to_token_entry_t *k2te = &_mc_array_index(&k2t->entries, mc_mapof_kmsid_to_token_entry_t, i); if (0 == strcmp(k2te->kmsid, kmsid)) { // Update entry. bson_free(k2te->access_token); k2te->access_token = bson_strdup(access_token); k2te->expiration_time_us = expiration_time_us; _mongocrypt_mutex_unlock(&k2t->mutex); return true; } } // Create an entry. mc_mapof_kmsid_to_token_entry_t to_put = {.kmsid = bson_strdup(kmsid), .access_token = bson_strdup(access_token), .expiration_time_us = expiration_time_us}; _mc_array_append_val(&k2t->entries, to_put); _mongocrypt_mutex_unlock(&k2t->mutex); return true; } libmongocrypt-1.19.0/src/mongocrypt-cache-private.h000066400000000000000000000050771521103432300223470ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MONGOCRYPT_CACHE_PRIVATE #define MONGOCRYPT_CACHE_PRIVATE #include "mongocrypt-buffer-private.h" #include "mongocrypt-mutex-private.h" #include "mongocrypt-status-private.h" #define CACHE_EXPIRATION_MS_DEFAULT 60000 /* A generic simple cache. * To avoid overusing the names "key" or "id", the cache contains * "attribute-value" pairs. * https://en.wikipedia.org/wiki/Attribute%E2%80%93value_pair */ typedef bool (*cache_compare_fn)(void *thing_a, void *thing_b, int *out); typedef void (*cache_destroy_fn)(void *thing); typedef void *(*cache_copy_fn)(void *thing); typedef void (*cache_dump_fn)(void *thing); typedef struct __mongocrypt_cache_pair_t { void *attr; void *value; struct __mongocrypt_cache_pair_t *next; int64_t last_updated; } _mongocrypt_cache_pair_t; typedef struct { cache_dump_fn dump_attr; cache_compare_fn cmp_attr; cache_copy_fn copy_attr; cache_destroy_fn destroy_attr; cache_copy_fn copy_value; cache_destroy_fn destroy_value; _mongocrypt_cache_pair_t *pair; mongocrypt_mutex_t mutex; /* global lock of cache. */ uint64_t expiration; } _mongocrypt_cache_t; /* Attempt to get an entry. * Returns boolean indicating success. */ bool _mongocrypt_cache_get(_mongocrypt_cache_t *cache, void *attr, void **value) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_cache_add_copy(_mongocrypt_cache_t *cache, void *attr, void *value, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; /* Steals the value instead of copying. Caller relinquishes value when calling. */ bool _mongocrypt_cache_add_stolen(_mongocrypt_cache_t *cache, void *attr, void *value, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; void _mongocrypt_cache_cleanup(_mongocrypt_cache_t *cache); /* Tests may override the default expiration */ void _mongocrypt_cache_set_expiration(_mongocrypt_cache_t *cache, uint64_t milli); uint32_t _mongocrypt_cache_num_entries(_mongocrypt_cache_t *cache); #endif /* MONGOCRYPT_CACHE_PRIVATE */ libmongocrypt-1.19.0/src/mongocrypt-cache.c000066400000000000000000000157171521103432300206740ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mongocrypt-cache-private.h" #include "mongocrypt-private.h" /* Did the cache pair expire? Caller must hold lock. */ static bool _pair_expired(_mongocrypt_cache_t *cache, _mongocrypt_cache_pair_t *pair) { int64_t current; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(pair); current = bson_get_monotonic_time() / 1000; BSON_ASSERT(current >= INT64_MIN + pair->last_updated); BSON_ASSERT(cache->expiration <= INT64_MAX); return cache->expiration > 0 && (current - pair->last_updated) > (int64_t)cache->expiration; } /* Return the pair after the one being destroyed. */ static _mongocrypt_cache_pair_t * _destroy_pair(_mongocrypt_cache_t *cache, _mongocrypt_cache_pair_t *prev, _mongocrypt_cache_pair_t *pair) { _mongocrypt_cache_pair_t *tmp; BSON_ASSERT_PARAM(cache); /* prev is checked before being used, so it can be NULL */ BSON_ASSERT_PARAM(pair); tmp = pair->next; /* Unlink */ if (!prev) { cache->pair = cache->pair->next; } else { prev->next = pair->next; } /* Destroy pair */ cache->destroy_attr(pair->attr); cache->destroy_value(pair->value); bson_free(pair); return tmp; } /* Caller must hold mutex. */ static void _mongocrypt_cache_evict(_mongocrypt_cache_t *cache) { _mongocrypt_cache_pair_t *pair, *prev; BSON_ASSERT_PARAM(cache); prev = NULL; pair = cache->pair; while (pair) { if (_pair_expired(cache, pair)) { pair = _destroy_pair(cache, prev, pair); continue; } prev = pair; pair = pair->next; } } /* Caller must hold mutex. */ static bool _mongocrypt_remove_matches(_mongocrypt_cache_t *cache, void *attr) { _mongocrypt_cache_pair_t *pair, *prev; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); prev = NULL; pair = cache->pair; while (pair) { int res; if (!cache->cmp_attr(pair->attr, attr, &res)) { return false; } if (0 == res) { pair = _destroy_pair(cache, prev, pair); continue; } prev = pair; pair = pair->next; } return true; } void _mongocrypt_cache_set_expiration(_mongocrypt_cache_t *cache, uint64_t milli) { BSON_ASSERT_PARAM(cache); cache->expiration = milli; } /* caller must hold lock. */ static bool _find_pair(_mongocrypt_cache_t *cache, void *attr, _mongocrypt_cache_pair_t **out) { _mongocrypt_cache_pair_t *pair; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); BSON_ASSERT_PARAM(out); *out = NULL; pair = cache->pair; while (pair) { int res; /* TODO: this is a naive O(n) lookup. Consider optimizing with a hash map (possibly vendor one). */ if (!cache->cmp_attr(pair->attr, attr, &res)) { return false; } if (res == 0) { *out = pair; return true; } pair = pair->next; } return true; } /* Create a new pair on linked list. Caller must hold lock. */ static _mongocrypt_cache_pair_t *_pair_new(_mongocrypt_cache_t *cache, void *attr) { _mongocrypt_cache_pair_t *pair; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); pair = bson_malloc0(sizeof(_mongocrypt_cache_pair_t)); BSON_ASSERT(pair); pair->attr = cache->copy_attr(attr); /* add rest of values. */ pair->next = cache->pair; pair->last_updated = bson_get_monotonic_time() / 1000; cache->pair = pair; return pair; } /* Caller must hold lock. */ static void _cache_pair_destroy(_mongocrypt_cache_t *cache, _mongocrypt_cache_pair_t *pair) { BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(pair); cache->destroy_attr(pair->attr); cache->destroy_value(pair->value); bson_free(pair); } bool _mongocrypt_cache_get(_mongocrypt_cache_t *cache, void *attr, /* attr of cache item */ void **value /* copied to. */) { _mongocrypt_cache_pair_t *match; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); BSON_ASSERT_PARAM(value); *value = NULL; _mongocrypt_mutex_lock(&cache->mutex); /* TODO CDRIVER-3120: optimize the eviction algorithm to avoid unnecessary * O(n) traversal */ _mongocrypt_cache_evict(cache); if (!_find_pair(cache, attr, &match)) { _mongocrypt_mutex_unlock(&cache->mutex); return false; } if (match) { *value = cache->copy_value(match->value); } _mongocrypt_mutex_unlock(&cache->mutex); return true; } static bool _cache_add(_mongocrypt_cache_t *cache, void *attr, void *value, mongocrypt_status_t *status, bool steal_value) { _mongocrypt_cache_pair_t *pair; BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); BSON_ASSERT_PARAM(value); _mongocrypt_mutex_lock(&cache->mutex); _mongocrypt_cache_evict(cache); if (!_mongocrypt_remove_matches(cache, attr)) { CLIENT_ERR("error removing from cache"); _mongocrypt_mutex_unlock(&cache->mutex); return false; } pair = _pair_new(cache, attr); if (steal_value) { pair->value = value; } else { pair->value = cache->copy_value(value); } _mongocrypt_mutex_unlock(&cache->mutex); return true; } bool _mongocrypt_cache_add_copy(_mongocrypt_cache_t *cache, void *attr, void *value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); BSON_ASSERT_PARAM(value); return _cache_add(cache, attr, value, status, false); } bool _mongocrypt_cache_add_stolen(_mongocrypt_cache_t *cache, void *attr, void *value, mongocrypt_status_t *status) { BSON_ASSERT_PARAM(cache); BSON_ASSERT_PARAM(attr); BSON_ASSERT_PARAM(value); return _cache_add(cache, attr, value, status, true); } void _mongocrypt_cache_cleanup(_mongocrypt_cache_t *cache) { _mongocrypt_cache_pair_t *pair, *tmp; if (!cache) { return; } pair = cache->pair; while (pair) { tmp = pair->next; _cache_pair_destroy(cache, pair); pair = tmp; } } uint32_t _mongocrypt_cache_num_entries(_mongocrypt_cache_t *cache) { _mongocrypt_cache_pair_t *pair; uint32_t count; BSON_ASSERT_PARAM(cache); _mongocrypt_mutex_lock(&cache->mutex); count = 0; for (pair = cache->pair; pair != NULL; pair = pair->next) { count++; } _mongocrypt_mutex_unlock(&cache->mutex); return count; } libmongocrypt-1.19.0/src/mongocrypt-ciphertext-private.h000066400000000000000000000037461521103432300234640ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MONGOCRYPT_CIPHERTEXT_PRIVATE_H #define MONGOCRYPT_CIPHERTEXT_PRIVATE_H #include "mc-fle-blob-subtype-private.h" #include "mongocrypt-buffer-private.h" #include "mongocrypt.h" /** * Produced by mongocrypt-marking.u.fle1.c from _mongocrypt_marking_t * as encrypted payloads for blob_subtypes: * FLE1DeterministicEncryptedValue(1) * FLE1RandomEncryptedValue(2) * FLE2InsertUpdatePayload(4) */ typedef struct { _mongocrypt_buffer_t key_id; mc_fle_blob_subtype_t blob_subtype; uint8_t original_bson_type; _mongocrypt_buffer_t data; } _mongocrypt_ciphertext_t; void _mongocrypt_ciphertext_init(_mongocrypt_ciphertext_t *ciphertext); void _mongocrypt_ciphertext_cleanup(_mongocrypt_ciphertext_t *ciphertext); bool _mongocrypt_ciphertext_parse_unowned(_mongocrypt_buffer_t *in, _mongocrypt_ciphertext_t *ciphertext, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_serialize_ciphertext(_mongocrypt_ciphertext_t *ciphertext, _mongocrypt_buffer_t *out) MONGOCRYPT_WARN_UNUSED_RESULT; bool _mongocrypt_ciphertext_serialize_associated_data(_mongocrypt_ciphertext_t *ciphertext, _mongocrypt_buffer_t *out) MONGOCRYPT_WARN_UNUSED_RESULT; #endif /* MONGOCRYPT_CIPHERTEXT_PRIVATE_H */ libmongocrypt-1.19.0/src/mongocrypt-ciphertext.c000066400000000000000000000124571521103432300220060ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mongocrypt-ciphertext-private.h" #include "mongocrypt-private.h" void _mongocrypt_ciphertext_init(_mongocrypt_ciphertext_t *ciphertext) { BSON_ASSERT_PARAM(ciphertext); memset(ciphertext, 0, sizeof(*ciphertext)); } void _mongocrypt_ciphertext_cleanup(_mongocrypt_ciphertext_t *ciphertext) { if (!ciphertext) { return; } _mongocrypt_buffer_cleanup(&ciphertext->key_id); _mongocrypt_buffer_cleanup(&ciphertext->data); } bool _mongocrypt_ciphertext_parse_unowned(_mongocrypt_buffer_t *in, _mongocrypt_ciphertext_t *ciphertext, mongocrypt_status_t *status) { uint32_t offset; /* From BSON Binary subtype 6 specification: struct fle_blob { uint8 fle_blob_subtype = (1 or 2); uint8 key_uuid[16]; uint8 original_bson_type; uint8 ciphertext[ciphertext_length]; } */ if (!ciphertext) { CLIENT_ERR("ciphertext cannot be null"); return false; } if (!in) { CLIENT_ERR("in parameter cannot be null"); return false; } if (!status) { CLIENT_ERR("status cannot be null"); return false; } offset = 0; /* At a minimum, a ciphertext must be 19 bytes: * fle_blob_subtype (1) + * key_uuid (16) + * original_bson_type (1) + * ciphertext (> 0) */ if (in->len < 19) { CLIENT_ERR("malformed ciphertext, too small"); return false; } ciphertext->blob_subtype = in->data[0]; offset += 1; /* TODO: merge new changes. */ if (ciphertext->blob_subtype != 1 && ciphertext->blob_subtype != 2) { CLIENT_ERR("malformed ciphertext, expected blob subtype of 1 or 2"); return false; } _mongocrypt_buffer_init(&ciphertext->key_id); ciphertext->key_id.data = in->data + offset; ciphertext->key_id.len = 16; ciphertext->key_id.subtype = BSON_SUBTYPE_UUID; offset += 16; ciphertext->original_bson_type = in->data[offset]; offset += 1; _mongocrypt_buffer_init(&ciphertext->data); ciphertext->data.data = in->data + offset; ciphertext->data.len = in->len - offset; return true; } bool _mongocrypt_serialize_ciphertext(_mongocrypt_ciphertext_t *ciphertext, _mongocrypt_buffer_t *out) { uint32_t offset; /* From BSON Binary subtype 6 specification: struct fle_blob { uint8 fle_blob_subtype = (1 or 2); uint8 key_uuid[16]; uint8 original_bson_type; uint8 ciphertext[ciphertext_length]; } */ if (!ciphertext || !out) { return false; } if (ciphertext->key_id.len != 16) { return false; } if (ciphertext->key_id.len > (UINT32_MAX - ciphertext->data.len - 1) || ciphertext->key_id.len > (SIZE_MAX - ciphertext->data.len - 1)) { return false; } _mongocrypt_buffer_init(out); offset = 0; out->len = 1 + ciphertext->key_id.len + 1 + ciphertext->data.len; out->data = bson_malloc0(out->len); BSON_ASSERT(out->data); out->owned = true; /* ciphertext->blob_subtype is an enum and easily fits in uint8_t */ out->data[offset] = (uint8_t)ciphertext->blob_subtype; offset += 1; memcpy(out->data + offset, ciphertext->key_id.data, ciphertext->key_id.len); offset += ciphertext->key_id.len; out->data[offset] = ciphertext->original_bson_type; offset += 1; memcpy(out->data + offset, ciphertext->data.data, ciphertext->data.len); return true; } /* From "FLE and AEAD" doc: A = Associated Data = fle_blob_subtype + key_uuid[16] + original_bson_type */ bool _mongocrypt_ciphertext_serialize_associated_data(_mongocrypt_ciphertext_t *ciphertext, _mongocrypt_buffer_t *out) { BSON_ASSERT_PARAM(ciphertext); uint32_t bytes_written = 0; if (!out) { return false; } _mongocrypt_buffer_init(out); if (!ciphertext->original_bson_type) { return false; } if (!_mongocrypt_buffer_is_uuid(&ciphertext->key_id)) { return false; } if ((ciphertext->blob_subtype != MC_SUBTYPE_FLE1DeterministicEncryptedValue) && (ciphertext->blob_subtype != MC_SUBTYPE_FLE1RandomEncryptedValue)) { return false; } if (ciphertext->key_id.len > (UINT32_MAX - 2)) { return false; } out->len = 1 + ciphertext->key_id.len + 1; out->data = bson_malloc(out->len); BSON_ASSERT(out->data); out->owned = true; out->data[bytes_written++] = (uint8_t)ciphertext->blob_subtype; memcpy(out->data + bytes_written, ciphertext->key_id.data, ciphertext->key_id.len); bytes_written += ciphertext->key_id.len; out->data[bytes_written++] = (uint8_t)ciphertext->original_bson_type; return true; } libmongocrypt-1.19.0/src/mongocrypt-compat.h000066400000000000000000000024631521103432300211130ustar00rootroot00000000000000/* * Copyright 2018-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. */ #ifndef MONGOCRYPT_COMPAT_H #define MONGOCRYPT_COMPAT_H /* Utilities for cross-platform and C89 compatibility */ /* Copied from bson-compat.h from the C driver. */ #include #include #ifdef MONGOCRYPT_HAVE_STDBOOL_H /* TODO - check for stdbool.h if we need to support older compilers and reconile with kms-message's rules for including stdbool.h */ #elif !defined(__bool_true_false_are_defined) #ifndef __cplusplus typedef signed char bool; #define false 0 #define true 1 #endif #define __bool_true_false_are_defined 1 #endif #ifdef __GNUC__ #define MONGOCRYPT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define MONGOCRYPT_WARN_UNUSED_RESULT #endif #endif /* MONGOCRYPT_COMPAT_H */ libmongocrypt-1.19.0/src/mongocrypt-config.h.in000066400000000000000000000045521521103432300215030ustar00rootroot00000000000000/* * Copyright 2019-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MONGOCRYPT_CONFIG_H #define MONGOCRYPT_CONFIG_H /* clang-format off */ /** * @def MONGOCRYPT_VERSION * @brief The version string describing libmongocrypt. * Has the form x.y.z-
++git.
 */
#define MONGOCRYPT_VERSION "@MONGOCRYPT_BUILD_VERSION@"

/*
 * MONGOCRYPT_ENABLE_CRYPTO_CNG is set from configure to determine if we are
 * compiled with Native Crypto support on Windows
 */
#define MONGOCRYPT_ENABLE_CRYPTO_CNG @MONGOCRYPT_ENABLE_CRYPTO_CNG@

#if MONGOCRYPT_ENABLE_CRYPTO_CNG != 1
#  undef MONGOCRYPT_ENABLE_CRYPTO_CNG
#endif


/*
 * MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO is set from configure to determine if we are
 * compiled with Native Crypto support on Darwin
 */
#define MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO @MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO@

#if MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO != 1
#  undef MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO
#endif


/*
 * MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO is set from configure to determine if we are
 * compiled with OpenSSL/LibreSSL (which both use libcrypto) support.
 */
#define MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO @MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO@
#define MONGOCRYPT_ENABLE_CRYPTO_OPENSSL @MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO@

#if MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO != 1
#  undef MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO
#  undef MONGOCRYPT_ENABLE_CRYPTO_OPENSSL
#endif


/*
 * MONGOCRYPT_ENABLE_CRYPTO is set from configure to determine if we are
 * compiled with any crypto support.
 */
#define MONGOCRYPT_ENABLE_CRYPTO @MONGOCRYPT_ENABLE_CRYPTO@

#if MONGOCRYPT_ENABLE_CRYPTO != 1
#  undef MONGOCRYPT_ENABLE_CRYPTO
#endif


/*
 * MONGOCRYPT_ENABLE_TRACE is automatically disabled as 
 * trace logging is no longer supported.
 */
#define MONGOCRYPT_ENABLE_TRACE 0

/* clang-format on */

#endif /* MONGOCRYPT_CONFIG_H */
libmongocrypt-1.19.0/src/mongocrypt-crypto-private.h000066400000000000000000000220561521103432300226200ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_CRYPTO_PRIVATE_H
#define MONGOCRYPT_CRYPTO_PRIVATE_H

#include "mongocrypt-buffer-private.h"
#include "mongocrypt.h"

#define MONGOCRYPT_KEY_LEN 96
#define MONGOCRYPT_IV_KEY_LEN 32
#define MONGOCRYPT_MAC_KEY_LEN 32
#define MONGOCRYPT_ENC_KEY_LEN 32
#define MONGOCRYPT_IV_LEN 16
#define MONGOCRYPT_HMAC_SHA512_LEN 64
#define MONGOCRYPT_HMAC_LEN 32
#define MONGOCRYPT_BLOCK_SIZE 16
#define MONGOCRYPT_HMAC_SHA256_LEN 32
#define MONGOCRYPT_TOKEN_KEY_LEN 32

typedef struct {
    int hooks_enabled;
    mongocrypt_crypto_fn aes_256_cbc_encrypt;
    mongocrypt_crypto_fn aes_256_cbc_decrypt;
    mongocrypt_crypto_fn aes_256_ctr_encrypt;
    mongocrypt_crypto_fn aes_256_ctr_decrypt;
    mongocrypt_crypto_fn aes_256_ecb_encrypt;
    mongocrypt_random_fn random;
    mongocrypt_hmac_fn hmac_sha_512;
    mongocrypt_hmac_fn hmac_sha_256;
    mongocrypt_hash_fn sha_256;
    void *ctx;
} _mongocrypt_crypto_t;

typedef uint32_t (*_mongocrypt_ciphertextlen_fn)(uint32_t plaintext_len, mongocrypt_status_t *status);
typedef uint32_t (*_mongocrypt_plaintextlen_fn)(uint32_t ciphertext_len, mongocrypt_status_t *status);
typedef bool (*_mongocrypt_do_encryption_fn)(_mongocrypt_crypto_t *crypto,
                                             const _mongocrypt_buffer_t *iv,
                                             const _mongocrypt_buffer_t *associated_data,
                                             const _mongocrypt_buffer_t *key,
                                             const _mongocrypt_buffer_t *plaintext,
                                             _mongocrypt_buffer_t *ciphertext,
                                             uint32_t *bytes_written,
                                             mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;
typedef bool (*_mongocrypt_do_decryption_fn)(_mongocrypt_crypto_t *crypto,
                                             const _mongocrypt_buffer_t *associated_data,
                                             const _mongocrypt_buffer_t *key,
                                             const _mongocrypt_buffer_t *ciphertext,
                                             _mongocrypt_buffer_t *plaintext,
                                             uint32_t *bytes_written,
                                             mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/**
 * Defines the application layer protocol to use when
 * encrypting client data values.
 */
typedef struct {
    _mongocrypt_ciphertextlen_fn get_ciphertext_len;
    _mongocrypt_plaintextlen_fn get_plaintext_len;
    _mongocrypt_do_encryption_fn do_encrypt;
    _mongocrypt_do_decryption_fn do_decrypt;
} _mongocrypt_value_encryption_algorithm_t;

// FLE1 algorithm: AES-256-CBC HMAC/SHA-512-256 (SHA-512 truncated to 256 bits)
// Algorithm is documented in [FLE and
// AEAD](https://docs.google.com/document/d/1D8xTXWo1B1dunO0bDZhPdolKTMbbD5fUIgsERubWRmY)
const _mongocrypt_value_encryption_algorithm_t *_mcFLE1Algorithm(void);

// FLE2 general algorithm: AES-256-CTR HMAC/SHA-256
// Algorithm is documented in [AEAD with
// CTR](https://docs.google.com/document/d/1eCU7R8Kjr-mdyz6eKvhNIDVmhyYQcAaLtTfHeK7a_vE/).
const _mongocrypt_value_encryption_algorithm_t *_mcFLE2AEADAlgorithm(void);

// FLE2 used with FLE2IndexedEncryptedValue: AES-256-CTR no HMAC
const _mongocrypt_value_encryption_algorithm_t *_mcFLE2Algorithm(void);

// FLE2AEAD general algorithm: AES-256-CBC HMAC/SHA-256
const _mongocrypt_value_encryption_algorithm_t *_mcFLE2v2AEADAlgorithm(void);

bool _mongocrypt_random(_mongocrypt_crypto_t *crypto,
                        _mongocrypt_buffer_t *out,
                        uint32_t count,
                        mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Generates a random number in the range [0, exclusive_upper_bound) in out. */
bool _mongocrypt_random_uint64(_mongocrypt_crypto_t *crypto,
                               uint64_t exclusive_upper_bound,
                               uint64_t *out,
                               mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Generates a random number in the range [0, exclusive_upper_bound) in out. */
bool _mongocrypt_random_int64(_mongocrypt_crypto_t *crypto,
                              int64_t exclusive_upper_bound,
                              int64_t *out,
                              mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Returns 0 if equal, non-zero otherwise */
int _mongocrypt_memequal(const void *const b1, const void *const b2, size_t len);

/*
 * _mongocrypt_wrap_key encrypts a DEK with a KEK.

 * kek is an input Key Encryption Key.
 * dek is an input Data Encryption Key.
 * encrypted_dek the result of encrypting dek with kek.
 * encrypted_dek is always initialized.
 * Returns true if no error occurred.
 * Returns false and sets @status if an error occurred.
 */
bool _mongocrypt_wrap_key(_mongocrypt_crypto_t *crypto,
                          _mongocrypt_buffer_t *kek,
                          _mongocrypt_buffer_t *dek,
                          _mongocrypt_buffer_t *encrypted_dek,
                          mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/*
 * _mongocrypt_unwrap_key decrypts an encrypted DEK with a KEK.
 *
 * kek is an input Key Encryption Key.
 * encrypted_dek is an input encrypted Data Encryption Key.
 * dek is the result of decrypting encrypted_dek with kek.
 * dek is always initialized.
 * Returns true if no error occurred.
 * Returns false and sets @status if an error occurred.
 */
bool _mongocrypt_unwrap_key(_mongocrypt_crypto_t *crypto,
                            _mongocrypt_buffer_t *kek,
                            _mongocrypt_buffer_t *encrypted_dek,
                            _mongocrypt_buffer_t *dek,
                            mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_calculate_deterministic_iv(_mongocrypt_crypto_t *crypto,
                                            const _mongocrypt_buffer_t *key,
                                            const _mongocrypt_buffer_t *plaintext,
                                            const _mongocrypt_buffer_t *associated_data,
                                            _mongocrypt_buffer_t *out,
                                            mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

/*
 * _mongocrypt_hmac_sha_256 computes the HMAC SHA-256.
 *
 * Uses the hmac_sha_256 hook set on @crypto if set, and otherwise
 * calls the native implementation.
 *
 * @out must have length 32 bytes.
 *
 * Returns true if no error occurred.
 * Returns false sets @status if an error occurred.
 */
bool _mongocrypt_hmac_sha_256(_mongocrypt_crypto_t *crypto,
                              const _mongocrypt_buffer_t *key,
                              const _mongocrypt_buffer_t *in,
                              _mongocrypt_buffer_t *out,
                              mongocrypt_status_t *status);

/* Crypto implementations must implement these functions. */

/* This variable must be defined in implementation
   files, and must be set to true when _crypto_init
   is successful. */
extern bool _native_crypto_initialized;

void _native_crypto_init(void);

typedef struct {
    const _mongocrypt_buffer_t *key;
    const _mongocrypt_buffer_t *iv;
    const _mongocrypt_buffer_t *in;
    _mongocrypt_buffer_t *out;
    uint32_t *bytes_written;
    mongocrypt_status_t *status;
} aes_256_args_t;

bool _native_crypto_aes_256_cbc_encrypt(aes_256_args_t args) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_aes_256_cbc_decrypt(aes_256_args_t args) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_hmac_sha_512(const _mongocrypt_buffer_t *key,
                                 const _mongocrypt_buffer_t *in,
                                 _mongocrypt_buffer_t *out,
                                 mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_random(_mongocrypt_buffer_t *out,
                           uint32_t count,
                           mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_aes_256_ctr_encrypt(aes_256_args_t args) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_aes_256_ctr_decrypt(aes_256_args_t args) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _native_crypto_hmac_sha_256(const _mongocrypt_buffer_t *key,
                                 const _mongocrypt_buffer_t *in,
                                 _mongocrypt_buffer_t *out,
                                 mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

#endif /* MONGOCRYPT_CRYPTO_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-crypto.c000066400000000000000000001605541521103432300211510ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Comments in this implementation refer to:
 * [MCGREW] https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
 */

#include 

#include "mongocrypt-binary-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-status-private.h"

#include 

/* This function uses ECB callback to simulate CTR encrypt and decrypt
 *
 * Note: the same function performs both encrypt and decrypt using same ECB
 * encryption function
 */

static bool _crypto_aes_256_ctr_encrypt_decrypt_via_ecb(void *ctx,
                                                        mongocrypt_crypto_fn aes_256_ecb_encrypt,
                                                        aes_256_args_t args,
                                                        mongocrypt_status_t *status) {
    BSON_ASSERT(args.iv && args.iv->len);
    BSON_ASSERT(args.in);
    BSON_ASSERT(args.out);

    if (args.out->len < args.in->len) {
        CLIENT_ERR("output buffer too small");
        return false;
    }

    _mongocrypt_buffer_t ctr, tmp;
    mongocrypt_binary_t key_bin, out_bin, in_bin, ctr_bin, tmp_bin;
    bool ret;

    _mongocrypt_buffer_to_binary(args.key, &key_bin);
    _mongocrypt_buffer_init(&ctr);
    _mongocrypt_buffer_copy_to(args.iv, &ctr);
    _mongocrypt_buffer_to_binary(&ctr, &ctr_bin);
    _mongocrypt_buffer_to_binary(args.out, &out_bin);
    _mongocrypt_buffer_to_binary(args.in, &in_bin);
    _mongocrypt_buffer_init_size(&tmp, args.iv->len);
    _mongocrypt_buffer_to_binary(&tmp, &tmp_bin);

    for (uint32_t ptr = 0; ptr < args.in->len;) {
        /* Encrypt value in CTR buffer */
        uint32_t bytes_written = 0;
        if (!aes_256_ecb_encrypt(ctx, &key_bin, NULL, &ctr_bin, &tmp_bin, &bytes_written, status)) {
            ret = false;
            goto cleanup;
        }

        if (bytes_written != tmp_bin.len) {
            CLIENT_ERR("encryption hook returned unexpected length");
            ret = false;
            goto cleanup;
        }

        /* XOR resulting stream with original data */
        for (uint32_t i = 0; i < bytes_written && ptr < args.in->len; i++, ptr++) {
            uint8_t *in_bin_u8 = in_bin.data;
            uint8_t *out_bin_u8 = out_bin.data;
            uint8_t *tmp_bin_u8 = tmp_bin.data;
            out_bin_u8[ptr] = in_bin_u8[ptr] ^ tmp_bin_u8[i];
        }

        /* Increment value in CTR buffer */
        uint32_t carry = 1;
        /* assert rather than return since this should never happen */
        BSON_ASSERT(ctr_bin.len == 0u || ctr_bin.len - 1u <= INT_MAX);
        for (int i = (int)ctr_bin.len - 1; i >= 0 && carry != 0; --i) {
            uint8_t *ctr_bin_u8 = ctr_bin.data;
            uint32_t bpp = carry + ctr_bin_u8[i];
            carry = bpp >> 8;
            ctr_bin_u8[i] = bpp & 0xFF;
        }
    }

    if (args.bytes_written) {
        *args.bytes_written = args.in->len;
    }

    ret = true;

cleanup:
    _mongocrypt_buffer_cleanup(&ctr);
    _mongocrypt_buffer_cleanup(&tmp);
    return ret;
}

/* Crypto primitives. These either call the native built in crypto primitives or
 * user supplied hooks. */
static bool _crypto_aes_256_cbc_encrypt(_mongocrypt_crypto_t *crypto, aes_256_args_t args) {
    mongocrypt_status_t *status = args.status;

    BSON_ASSERT_PARAM(crypto);

    BSON_ASSERT(args.key);
    if (args.key->len != MONGOCRYPT_ENC_KEY_LEN) {
        CLIENT_ERR("invalid encryption key length");
        return false;
    }

    BSON_ASSERT(args.iv);
    if (args.iv->len != MONGOCRYPT_IV_LEN) {
        CLIENT_ERR("invalid iv length");
        return false;
    }

    if (crypto->hooks_enabled) {
        mongocrypt_binary_t enc_key_bin, iv_bin, out_bin, in_bin;
        bool ret;

        _mongocrypt_buffer_to_binary(args.key, &enc_key_bin);
        _mongocrypt_buffer_to_binary(args.iv, &iv_bin);
        _mongocrypt_buffer_to_binary(args.out, &out_bin);
        _mongocrypt_buffer_to_binary(args.in, &in_bin);

        ret = crypto->aes_256_cbc_encrypt(crypto->ctx,
                                          &enc_key_bin,
                                          &iv_bin,
                                          &in_bin,
                                          &out_bin,
                                          args.bytes_written,
                                          status);
        return ret;
    }
    return _native_crypto_aes_256_cbc_encrypt(args);
}

static bool _crypto_aes_256_ctr_encrypt(_mongocrypt_crypto_t *crypto, aes_256_args_t args) {
    mongocrypt_status_t *status = args.status;

    BSON_ASSERT_PARAM(crypto);

    BSON_ASSERT(args.key);
    if (args.key->len != MONGOCRYPT_ENC_KEY_LEN) {
        CLIENT_ERR("invalid encryption key length");
        return false;
    }

    BSON_ASSERT(args.iv);
    if (args.iv->len != MONGOCRYPT_IV_LEN) {
        CLIENT_ERR("invalid iv length");
        return false;
    }

    if (crypto->aes_256_ctr_encrypt) {
        mongocrypt_binary_t enc_key_bin, iv_bin, out_bin, in_bin;
        bool ret;

        _mongocrypt_buffer_to_binary(args.key, &enc_key_bin);
        _mongocrypt_buffer_to_binary(args.iv, &iv_bin);
        _mongocrypt_buffer_to_binary(args.out, &out_bin);
        _mongocrypt_buffer_to_binary(args.in, &in_bin);

        ret = crypto->aes_256_ctr_encrypt(crypto->ctx,
                                          &enc_key_bin,
                                          &iv_bin,
                                          &in_bin,
                                          &out_bin,
                                          args.bytes_written,
                                          status);
        return ret;
    }

    if (crypto->aes_256_ecb_encrypt) {
        return _crypto_aes_256_ctr_encrypt_decrypt_via_ecb(crypto->ctx, crypto->aes_256_ecb_encrypt, args, status);
    }

    return _native_crypto_aes_256_ctr_encrypt(args);
}

static bool _crypto_aes_256_cbc_decrypt(_mongocrypt_crypto_t *crypto, aes_256_args_t args) {
    mongocrypt_status_t *status = args.status;

    BSON_ASSERT_PARAM(crypto);

    BSON_ASSERT(args.key);
    if (args.key->len != MONGOCRYPT_ENC_KEY_LEN) {
        CLIENT_ERR("invalid encryption key length");
        return false;
    }

    if (crypto->hooks_enabled) {
        mongocrypt_binary_t enc_key_bin, iv_bin, out_bin, in_bin;
        bool ret;

        _mongocrypt_buffer_to_binary(args.key, &enc_key_bin);
        _mongocrypt_buffer_to_binary(args.iv, &iv_bin);
        _mongocrypt_buffer_to_binary(args.out, &out_bin);
        _mongocrypt_buffer_to_binary(args.in, &in_bin);

        ret = crypto->aes_256_cbc_decrypt(crypto->ctx,
                                          &enc_key_bin,
                                          &iv_bin,
                                          &in_bin,
                                          &out_bin,
                                          args.bytes_written,
                                          status);
        return ret;
    }
    return _native_crypto_aes_256_cbc_decrypt(args);
}

static bool _crypto_aes_256_ctr_decrypt(_mongocrypt_crypto_t *crypto, aes_256_args_t args) {
    mongocrypt_status_t *status = args.status;

    BSON_ASSERT_PARAM(crypto);

    BSON_ASSERT(args.key);
    if (args.key->len != MONGOCRYPT_ENC_KEY_LEN) {
        CLIENT_ERR("invalid encryption key length");
        return false;
    }

    if (crypto->aes_256_ctr_decrypt) {
        mongocrypt_binary_t enc_key_bin, iv_bin, out_bin, in_bin;
        bool ret;

        _mongocrypt_buffer_to_binary(args.key, &enc_key_bin);
        _mongocrypt_buffer_to_binary(args.iv, &iv_bin);
        _mongocrypt_buffer_to_binary(args.out, &out_bin);
        _mongocrypt_buffer_to_binary(args.in, &in_bin);

        ret = crypto->aes_256_ctr_decrypt(crypto->ctx,
                                          &enc_key_bin,
                                          &iv_bin,
                                          &in_bin,
                                          &out_bin,
                                          args.bytes_written,
                                          status);
        return ret;
    }

    if (crypto->aes_256_ecb_encrypt) {
        return _crypto_aes_256_ctr_encrypt_decrypt_via_ecb(crypto->ctx, crypto->aes_256_ecb_encrypt, args, status);
    }

    return _native_crypto_aes_256_ctr_decrypt(args);
}

static bool _crypto_hmac_sha_512(_mongocrypt_crypto_t *crypto,
                                 const _mongocrypt_buffer_t *hmac_key,
                                 const _mongocrypt_buffer_t *in,
                                 _mongocrypt_buffer_t *out,
                                 mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(hmac_key);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    if (hmac_key->len != MONGOCRYPT_MAC_KEY_LEN) {
        CLIENT_ERR("invalid hmac key length");
        return false;
    }

    if (out->len != MONGOCRYPT_HMAC_SHA512_LEN) {
        CLIENT_ERR("out does not contain %d bytes", MONGOCRYPT_HMAC_SHA512_LEN);
        return false;
    }

    if (crypto->hooks_enabled) {
        mongocrypt_binary_t hmac_key_bin, out_bin, in_bin;
        bool ret;

        _mongocrypt_buffer_to_binary(hmac_key, &hmac_key_bin);
        _mongocrypt_buffer_to_binary(out, &out_bin);
        _mongocrypt_buffer_to_binary(in, &in_bin);

        ret = crypto->hmac_sha_512(crypto->ctx, &hmac_key_bin, &in_bin, &out_bin, status);
        return ret;
    }
    return _native_crypto_hmac_sha_512(hmac_key, in, out, status);
}

bool _mongocrypt_hmac_sha_256(_mongocrypt_crypto_t *crypto,
                              const _mongocrypt_buffer_t *key,
                              const _mongocrypt_buffer_t *in,
                              _mongocrypt_buffer_t *out,
                              mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    if (key->len != MONGOCRYPT_MAC_KEY_LEN) {
        CLIENT_ERR("invalid hmac_sha_256 key length. Got %" PRIu32 ", expected: %d", key->len, MONGOCRYPT_MAC_KEY_LEN);
        return false;
    }

    if (crypto->hooks_enabled) {
        mongocrypt_binary_t key_bin, out_bin, in_bin;
        _mongocrypt_buffer_to_binary(key, &key_bin);
        _mongocrypt_buffer_to_binary(out, &out_bin);
        _mongocrypt_buffer_to_binary(in, &in_bin);

        return crypto->hmac_sha_256(crypto->ctx, &key_bin, &in_bin, &out_bin, status);
    }
    return _native_crypto_hmac_sha_256(key, in, out, status);
}

static bool
_crypto_random(_mongocrypt_crypto_t *crypto, _mongocrypt_buffer_t *out, uint32_t count, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(out);

    if (out->len != count) {
        CLIENT_ERR("out does not contain %u bytes", count);
        return false;
    }

    if (crypto->hooks_enabled) {
        mongocrypt_binary_t out_bin;

        _mongocrypt_buffer_to_binary(out, &out_bin);
        return crypto->random(crypto->ctx, &out_bin, count, status);
    }
    return _native_crypto_random(out, count, status);
}

/*
 * Secure memcmp copied from the C driver.
 */
int _mongocrypt_memequal(const void *const b1, const void *const b2, size_t len) {
    const unsigned char *p1 = b1, *p2 = b2;
    int ret = 0;

    BSON_ASSERT_PARAM(b1);
    BSON_ASSERT_PARAM(b2);

    for (; len > 0; len--) {
        ret |= *p1++ ^ *p2++;
    }

    return ret;
}

typedef enum {
    MODE_CBC,
    MODE_CTR,
} _mongocrypt_encryption_mode_t;

typedef enum {
    HMAC_NONE,
    HMAC_SHA_512_256, // sha512 truncated to 256 bits
    HMAC_SHA_256,
} _mongocrypt_hmac_type_t;

typedef enum {
    KEY_FORMAT_FLE1,       // 32 octets MAC key, 32 DATA key, 32 IV key (ignored)
    KEY_FORMAT_FLE2,       // 32 octets DATA key
    KEY_FORMAT_FLE2AEAD,   // 32 octets DATA key, 32 MAC key, 32 IV key (ignored)
    KEY_FORMAT_FLE2v2AEAD, // 32 octets DATA key, 32 MAC key, 32 IV key (ignored)
} _mongocrypt_key_format_t;

typedef enum {
    MAC_FORMAT_FLE1,       // HMAC(AAD || IV || S || LEN(AAD) as uint64be)
    MAC_FORMAT_FLE2,       // NONE
    MAC_FORMAT_FLE2AEAD,   // HMAC(AAD || IV || S)
    MAC_FORMAT_FLE2v2AEAD, // HMAC(AAD || IV || S)
} _mongocrypt_mac_format_t;

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_calculate_ciphertext_len
 *
 * Calculate the space needed for a ciphertext payload of a given size
 * and using fixed iv/hmac lengths.
 *
 * MODE_CBC: Assumes the ciphertext will be padded according to PKCS#7
 * which rounds up to the next block size, adding up to a complete block
 * for block aligned input payloads.
 *
 * MODE_CTR: Assumes no additional padding since CTR is a streaming cipher.
 *
 * Assumes all algorithms use identical IV length and blocksizes.
 *
 * ----------------------------------------------------------------------------
 */
static uint32_t _mongocrypt_calculate_ciphertext_len(uint32_t inlen,
                                                     _mongocrypt_encryption_mode_t mode,
                                                     _mongocrypt_hmac_type_t hmac,
                                                     mongocrypt_status_t *status) {
    const uint32_t hmaclen = (hmac == HMAC_NONE) ? 0 : MONGOCRYPT_HMAC_LEN;
    const uint32_t maxinlen = UINT32_MAX - (MONGOCRYPT_IV_LEN + MONGOCRYPT_BLOCK_SIZE + hmaclen);
    uint32_t fill;
    if (inlen > maxinlen) {
        CLIENT_ERR("plaintext too long");
        return 0;
    }

    if (mode == MODE_CBC) {
        fill = MONGOCRYPT_BLOCK_SIZE - (inlen % MONGOCRYPT_BLOCK_SIZE);
    } else {
        BSON_ASSERT(mode == MODE_CTR);
        fill = 0;
    }

    return MONGOCRYPT_IV_LEN + inlen + fill + hmaclen;
}

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_calculate_plaintext_len
 *
 * Calculate the space needed for a plaintext payload of a given size
 * and using fixed iv/hmac lengths.
 *
 * MODE_CBC: In practice, plaintext will be between 1 and {blocksize} bytes
 * shorter
 * than the input ciphertext, but it's easier and safer to assume the
 * full ciphertext length and waste a few bytes.
 *
 * MODE_CTR: Assumes no additional padding since CTR is a streaming cipher.
 *
 * Assumes all algorithms use identical IV length and blocksizes.
 *
 * ----------------------------------------------------------------------------
 */
static uint32_t _mongocrypt_calculate_plaintext_len(uint32_t inlen,
                                                    _mongocrypt_encryption_mode_t mode,
                                                    _mongocrypt_hmac_type_t hmac,
                                                    mongocrypt_status_t *status) {
    const uint32_t hmaclen = (hmac == HMAC_NONE) ? 0 : MONGOCRYPT_HMAC_LEN;
    const uint32_t mincipher = (mode == MODE_CTR) ? 0 : MONGOCRYPT_BLOCK_SIZE;
    if (inlen < (MONGOCRYPT_IV_LEN + mincipher + hmaclen)) {
        CLIENT_ERR("input ciphertext too small. Must be at least %" PRIu32 " bytes",
                   MONGOCRYPT_IV_LEN + mincipher + hmaclen);
        return 0;
    }
    return inlen - (MONGOCRYPT_IV_LEN + hmaclen);
}

/* ----------------------------------------------------------------------------
 *
 * _aes256_cbc_encrypt --
 *
 *    Encrypts using AES256 CBC using a secret key and a known IV.
 *
 * Parameters:
 *    @iv a 16 byte IV.
 *    @enc_key a 32 byte key.
 *    @plaintext the plaintext to encrypt.
 *    @ciphertext the resulting ciphertext.
 *    @bytes_written a location for the resulting number of bytes written into
 *    ciphertext->data.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 * Preconditions:
 *    1. ciphertext->data has been pre-allocated with enough space for the
 *    resulting ciphertext.
 *
 * Postconditions:
 *    1. bytes_written is set to the length of the written ciphertext. This
 *    is the same as
 *    _mongocrypt_calculate_ciphertext_len (plaintext->len, mode, hmac, status).
 *
 * ----------------------------------------------------------------------------
 */
static bool _encrypt_step(_mongocrypt_crypto_t *crypto,
                          _mongocrypt_encryption_mode_t mode,
                          const _mongocrypt_buffer_t *iv,
                          const _mongocrypt_buffer_t *enc_key,
                          const _mongocrypt_buffer_t *plaintext,
                          _mongocrypt_buffer_t *ciphertext,
                          uint32_t *bytes_written,
                          mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(iv);
    BSON_ASSERT_PARAM(enc_key);
    BSON_ASSERT_PARAM(plaintext);
    BSON_ASSERT_PARAM(ciphertext);

    BSON_ASSERT_PARAM(bytes_written);
    *bytes_written = 0;

    if (MONGOCRYPT_IV_LEN != iv->len) {
        CLIENT_ERR("IV should have length %d, but has length %" PRIu32, MONGOCRYPT_IV_LEN, iv->len);
        return false;
    }

    if (MONGOCRYPT_ENC_KEY_LEN != enc_key->len) {
        CLIENT_ERR("Encryption key should have length %d, but has length %" PRIu32,
                   MONGOCRYPT_ENC_KEY_LEN,
                   enc_key->len);
        return false;
    }

    if (mode == MODE_CTR) {
        // Streaming cipher, no padding required.
        return _crypto_aes_256_ctr_encrypt(crypto,
                                           (aes_256_args_t){.key = enc_key,
                                                            .iv = iv,
                                                            .in = plaintext,
                                                            .out = ciphertext,
                                                            .bytes_written = bytes_written,
                                                            .status = status});
    }

    BSON_ASSERT(mode == MODE_CBC);

    /* calculate how many extra bytes there are after a block boundary */
    const uint32_t unaligned = plaintext->len % MONGOCRYPT_BLOCK_SIZE;
    uint32_t padding_byte = MONGOCRYPT_BLOCK_SIZE - unaligned;
    _mongocrypt_buffer_t intermediates[2], to_encrypt;
    uint8_t final_block_storage[MONGOCRYPT_BLOCK_SIZE];
    bool ret;

    BSON_ASSERT(MONGOCRYPT_BLOCK_SIZE >= unaligned);

    /* Some crypto providers disallow variable length inputs, and require
     * the input to be a multiple of the block size. So add everything up
     * to but excluding the last block if not block aligned, then add
     * the last block with padding. */
    _mongocrypt_buffer_init(&intermediates[0]);
    _mongocrypt_buffer_init(&intermediates[1]);
    intermediates[0].data = (uint8_t *)plaintext->data;
    /* don't check plaintext->len, as the above modulo operation guarantees
     * that unaligned will be smaller */
    intermediates[0].len = plaintext->len - unaligned;
    intermediates[1].data = final_block_storage;
    intermediates[1].len = sizeof(final_block_storage);

    /* [MCGREW]: "Prior to CBC encryption, the plaintext P is padded by appending
     * a padding string PS to that data, to ensure that len(P || PS) is a
     * multiple of 128". This is also known as PKCS #7 padding. */
    if (unaligned) {
        /* Copy the unaligned bytes. */
        memcpy(intermediates[1].data, plaintext->data + (plaintext->len - unaligned), unaligned);
    }
    /* Fill out block remained or whole block with padding_byte */
    memset(intermediates[1].data + unaligned, (int)padding_byte, padding_byte);

    _mongocrypt_buffer_init(&to_encrypt);
    if (!_mongocrypt_buffer_concat(&to_encrypt, intermediates, 2)) {
        CLIENT_ERR("failed to allocate buffer");
        _mongocrypt_buffer_cleanup(&to_encrypt);
        return false;
    }

    ret = _crypto_aes_256_cbc_encrypt(crypto,
                                      (aes_256_args_t){.key = enc_key,
                                                       .iv = iv,
                                                       .in = &to_encrypt,
                                                       .out = ciphertext,
                                                       .bytes_written = bytes_written,
                                                       .status = status});
    _mongocrypt_buffer_cleanup(&to_encrypt);
    if (!ret) {
        return false;
    }

    if (*bytes_written % MONGOCRYPT_BLOCK_SIZE != 0) {
        CLIENT_ERR("encryption failure, wrote %" PRIu32 " bytes, not a multiple of %d",
                   *bytes_written,
                   MONGOCRYPT_BLOCK_SIZE);
        return false;
    }

    return true;
}

/* ----------------------------------------------------------------------------
 *
 * _hmac_step --
 *
 *    Compute the selected HMAC with a secret key.
 *
 * Parameters:
 *    @Km a 32 byte key.
 *    @AAD associated data to add into the HMAC. This may be
 *    an empty buffer.
 *    @iv_and_ciphertext the IV and S components to add into the HMAC.
 *    @out a location for the resulting HMAC tag.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 * Preconditions:
 *    1. out->data has been pre-allocated with at least 64 bytes.
 *
 * Postconditions:
 *    1. out->data will have a 64 byte tag appended.
 *
 * ----------------------------------------------------------------------------
 */
static bool _hmac_step(_mongocrypt_crypto_t *crypto,
                       _mongocrypt_mac_format_t mac_format,
                       _mongocrypt_hmac_type_t hmac,
                       const _mongocrypt_buffer_t *Km,
                       const _mongocrypt_buffer_t *AAD,
                       const _mongocrypt_buffer_t *iv_and_ciphertext,
                       _mongocrypt_buffer_t *out,
                       mongocrypt_status_t *status) {
    _mongocrypt_buffer_t to_hmac = {0};
    bool ret = false;

    BSON_ASSERT(hmac != HMAC_NONE);
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(Km);
    // AAD may be NULL
    BSON_ASSERT_PARAM(iv_and_ciphertext);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_buffer_init(&to_hmac);

    if (MONGOCRYPT_MAC_KEY_LEN != Km->len) {
        CLIENT_ERR("HMAC key wrong length: %" PRIu32, Km->len);
        goto done;
    }

    if (out->len != MONGOCRYPT_HMAC_LEN) {
        CLIENT_ERR("out wrong length: %" PRIu32, out->len);
        goto done;
    }

    /* Construct the input to the HMAC */
    uint32_t num_intermediates = 0;
    _mongocrypt_buffer_t intermediates[3];
    if (AAD && !_mongocrypt_buffer_from_subrange(&intermediates[num_intermediates++], AAD, 0, AAD->len)) {
        CLIENT_ERR("Failed creating MAC subrange on AD");
        goto done;
    }
    if (!_mongocrypt_buffer_from_subrange(&intermediates[num_intermediates++],
                                          iv_and_ciphertext,
                                          0,
                                          iv_and_ciphertext->len)) {
        CLIENT_ERR("Failed creating MAC subrange on IV and S");
        goto done;
    }

    // {AL} must be stored in the function's lexical scope so that
    // {intermediates}'s reference to it survives until the
    // _mongocrypt_buffer_concat operation later.
    uint64_t AL;
    if (mac_format == MAC_FORMAT_FLE1) {
        /* T := HMAC(AAD || IV || S || AL)
         * AL is equal to the number of bits in AAD expressed
         * as a 64bit unsigned big-endian integer.
         * Multiplying a uint32_t by 8 won't bring it anywhere close to
         * UINT64_MAX.
         */
        AL = AAD ? BSON_UINT64_TO_BE(8 * (uint64_t)AAD->len) : 0;
        _mongocrypt_buffer_init(&intermediates[num_intermediates]);
        intermediates[num_intermediates].data = (uint8_t *)&AL;
        intermediates[num_intermediates++].len = sizeof(uint64_t);

    } else {
        /* T := HMAC(AAD || IV || S) */
        BSON_ASSERT((mac_format == MAC_FORMAT_FLE2AEAD) || (mac_format == MAC_FORMAT_FLE2v2AEAD));
    }

    if (!_mongocrypt_buffer_concat(&to_hmac, intermediates, num_intermediates)) {
        CLIENT_ERR("failed to allocate buffer");
        goto done;
    }

    if (hmac == HMAC_SHA_512_256) {
        uint8_t storage[64];
        _mongocrypt_buffer_t tag = {.data = storage, .len = sizeof(storage)};

        if (!_crypto_hmac_sha_512(crypto, Km, &to_hmac, &tag, status)) {
            goto done;
        }

        // Truncate sha512 to first 256 bits.
        memcpy(out->data, tag.data, MONGOCRYPT_HMAC_LEN);

    } else {
        BSON_ASSERT(hmac == HMAC_SHA_256);
        if (!_mongocrypt_hmac_sha_256(crypto, Km, &to_hmac, out, status)) {
            goto done;
        }
    }

    ret = true;
done:
    _mongocrypt_buffer_cleanup(&to_hmac);
    return ret;
}

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_do_encryption --
 *
 *    Defer encryption to whichever crypto library libmongocrypt is using.
 *
 * Parameters:
 *    @iv a 16 byte IV.
 *    @associated_data associated data for the HMAC. May be NULL.
 *    @key is the encryption key. The size depends on @key_format.
 *    @plaintext the plaintext to encrypt.
 *    @ciphertext a location for the resulting ciphertext and HMAC tag.
 *    @bytes_written a location for the resulting bytes written.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 * Preconditions:
 *    1. ciphertext->data has been pre-allocated with enough space for the
 *    resulting ciphertext. Use _mongocrypt_calculate_ciphertext_len.
 *
 * Postconditions:
 *    1. bytes_written is set to the length of the written ciphertext. This
 *    is the same as
 *    _mongocrypt_calculate_ciphertext_len (plaintext->len, mode, hmac, status).
 *
 * ----------------------------------------------------------------------------
 */
static bool _mongocrypt_do_encryption(_mongocrypt_crypto_t *crypto,
                                      _mongocrypt_key_format_t key_format,
                                      _mongocrypt_mac_format_t mac_format,
                                      _mongocrypt_encryption_mode_t mode,
                                      _mongocrypt_hmac_type_t hmac,
                                      const _mongocrypt_buffer_t *iv,
                                      const _mongocrypt_buffer_t *associated_data,
                                      const _mongocrypt_buffer_t *key,
                                      const _mongocrypt_buffer_t *plaintext,
                                      _mongocrypt_buffer_t *ciphertext,
                                      uint32_t *bytes_written,
                                      mongocrypt_status_t *status) {
    _mongocrypt_buffer_t Ke = {0}; // Ke == Key for Encryption
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(iv);
    /* associated_data is checked at the point it is used, so it can be NULL */
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(plaintext);
    BSON_ASSERT_PARAM(ciphertext);

    if (plaintext->len <= 0) {
        CLIENT_ERR("input plaintext too small. Must be more than zero bytes.");
        return false;
    }

    const uint32_t expect_ciphertext_len = _mongocrypt_calculate_ciphertext_len(plaintext->len, mode, hmac, status);
    if (mongocrypt_status_type(status) != MONGOCRYPT_STATUS_OK) {
        return false;
    }
    if (expect_ciphertext_len != ciphertext->len) {
        CLIENT_ERR("output ciphertext should have been allocated with %" PRIu32 " bytes", expect_ciphertext_len);
        return false;
    }

    if (MONGOCRYPT_IV_LEN != iv->len) {
        CLIENT_ERR("IV should have length %d, but has length %" PRIu32, MONGOCRYPT_IV_LEN, iv->len);
        return false;
    }

    const uint32_t expected_key_len = (key_format == KEY_FORMAT_FLE2) ? MONGOCRYPT_ENC_KEY_LEN : MONGOCRYPT_KEY_LEN;
    if (key->len != expected_key_len) {
        CLIENT_ERR("key should have length %" PRIu32 ", but has length %" PRIu32, expected_key_len, key->len);
        return false;
    }

    // Copy IV into the output, and clear remainder.
    memmove(ciphertext->data, iv->data, MONGOCRYPT_IV_LEN);
    memset(ciphertext->data + MONGOCRYPT_IV_LEN, 0, ciphertext->len - MONGOCRYPT_IV_LEN);

    // S is the encryption payload without IV or HMAC
    _mongocrypt_buffer_t S;
    if (!_mongocrypt_buffer_from_subrange(&S, ciphertext, MONGOCRYPT_IV_LEN, ciphertext->len - MONGOCRYPT_IV_LEN)) {
        CLIENT_ERR("unable to create S subrange from C");
        return false;
    }
    if (hmac != HMAC_NONE) {
        S.len -= MONGOCRYPT_HMAC_LEN;
    }

    // Ke is the key used for payload encryption
    const uint32_t Ke_offset = (key_format == KEY_FORMAT_FLE1) ? MONGOCRYPT_MAC_KEY_LEN : 0;
    if (!_mongocrypt_buffer_from_subrange(&Ke, key, Ke_offset, MONGOCRYPT_ENC_KEY_LEN)) {
        CLIENT_ERR("unable to create Ke subrange from key");
        return false;
    }

    uint32_t S_bytes_written = 0;
    if (!_encrypt_step(crypto, mode, iv, &Ke, plaintext, &S, &S_bytes_written, status)) {
        return false;
    }
    BSON_ASSERT_PARAM(bytes_written);
    BSON_ASSERT((UINT32_MAX - S_bytes_written) > MONGOCRYPT_IV_LEN);
    *bytes_written = MONGOCRYPT_IV_LEN + S_bytes_written;

    if (hmac != HMAC_NONE) {
        // Km == Key for MAC
        const uint32_t Km_offset = (key_format == KEY_FORMAT_FLE1) ? 0 : MONGOCRYPT_ENC_KEY_LEN;

        // Km is the HMAC Key.
        _mongocrypt_buffer_t Km;
        if (!_mongocrypt_buffer_from_subrange(&Km, key, Km_offset, MONGOCRYPT_MAC_KEY_LEN)) {
            CLIENT_ERR("unable to create Km subrange from key");
            return false;
        }

        /* Primary payload to MAC. */
        _mongocrypt_buffer_t iv_and_ciphertext;
        if (!_mongocrypt_buffer_from_subrange(&iv_and_ciphertext, ciphertext, 0, *bytes_written)) {
            CLIENT_ERR("unable to create IV || S subrange from C");
            return false;
        }

        // T == HMAC Tag
        _mongocrypt_buffer_t T;
        if (!_mongocrypt_buffer_from_subrange(&T, ciphertext, *bytes_written, MONGOCRYPT_HMAC_LEN)) {
            CLIENT_ERR("unable to create T subrange from C");
            return false;
        }

        if (!_hmac_step(crypto, mac_format, hmac, &Km, associated_data, &iv_and_ciphertext, &T, status)) {
            return false;
        }

        *bytes_written += MONGOCRYPT_HMAC_LEN;
    }

    return true;
}

/* ----------------------------------------------------------------------------
 *
 * _decrypt_step --
 *
 *    Decrypts using AES256 using a secret key and a known IV.
 *
 * Parameters:
 *    @enc_key a 32 byte key.
 *    @ciphertext the ciphertext to decrypt.
 *    @plaintext the resulting plaintext.
 *    @bytes_written a location for the resulting number of bytes written into
 *    plaintext->data.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 * Preconditions:
 *    1. plaintext->data has been pre-allocated with enough space for the
 *    resulting plaintext.
 *
 * Postconditions:
 *    1. bytes_written is set to the length of the written plaintext, excluding
 *    padding. This may be less than
 *    _mongocrypt_calculate_plaintext_len (ciphertext->len, home, hmac, status).
 *
 * ----------------------------------------------------------------------------
 */
static bool _decrypt_step(_mongocrypt_crypto_t *crypto,
                          _mongocrypt_encryption_mode_t mode,
                          const _mongocrypt_buffer_t *iv,
                          const _mongocrypt_buffer_t *enc_key,
                          const _mongocrypt_buffer_t *ciphertext,
                          _mongocrypt_buffer_t *plaintext,
                          uint32_t *bytes_written,
                          mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(iv);
    BSON_ASSERT_PARAM(enc_key);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT_PARAM(plaintext);

    BSON_ASSERT_PARAM(bytes_written);
    *bytes_written = 0;

    if (MONGOCRYPT_IV_LEN != iv->len) {
        CLIENT_ERR("IV should have length %d, but has length %" PRIu32, MONGOCRYPT_IV_LEN, iv->len);
        return false;
    }
    if (MONGOCRYPT_ENC_KEY_LEN != enc_key->len) {
        CLIENT_ERR("encryption key should have length %d, but has length %" PRIu32,
                   MONGOCRYPT_ENC_KEY_LEN,
                   enc_key->len);
        return false;
    }

    if (mode == MODE_CBC) {
        if (ciphertext->len % MONGOCRYPT_BLOCK_SIZE > 0) {
            CLIENT_ERR("error, ciphertext length is not a multiple of block size");
            return false;
        }

        if (!_crypto_aes_256_cbc_decrypt(crypto,
                                         (aes_256_args_t){.iv = iv,
                                                          .key = enc_key,
                                                          .in = ciphertext,
                                                          .out = plaintext,
                                                          .bytes_written = bytes_written,
                                                          .status = status})) {
            return false;
        }

        BSON_ASSERT(*bytes_written > 0);
        uint8_t padding_byte = plaintext->data[*bytes_written - 1];
        if (padding_byte > 16) {
            CLIENT_ERR("error, ciphertext malformed padding");
            return false;
        }
        *bytes_written -= padding_byte;

    } else {
        BSON_ASSERT(mode == MODE_CTR);
        if (!_crypto_aes_256_ctr_decrypt(crypto,
                                         (aes_256_args_t){.iv = iv,
                                                          .key = enc_key,
                                                          .in = ciphertext,
                                                          .out = plaintext,
                                                          .bytes_written = bytes_written,
                                                          .status = status})) {
            return false;
        }
        BSON_ASSERT(*bytes_written == plaintext->len);
    }

    return true;
}

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_do_decryption --
 *
 *    Defer decryption to whichever crypto library libmongocrypt is using.
 *
 * Parameters:
 *    @associated_data associated data for the HMAC. May be NULL.
 *    @key a 96 byte key.
 *    @ciphertext the ciphertext to decrypt. This contains the IV prepended.
 *    @plaintext a location for the resulting plaintext.
 *    @bytes_written a location for the resulting bytes written.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 *  Preconditions:
 *    1. plaintext->data has been pre-allocated with enough space for the
 *    resulting plaintext and padding. See _mongocrypt_calculate_plaintext_len.
 *
 *  Postconditions:
 *    1. bytes_written is set to the length of the written plaintext, excluding
 *    padding. This may be less than
 *    _mongocrypt_calculate_plaintext_len (ciphertext->len, mode, hmac, status).
 *
 * ----------------------------------------------------------------------------
 */
static bool _mongocrypt_do_decryption(_mongocrypt_crypto_t *crypto,
                                      _mongocrypt_key_format_t key_format,
                                      _mongocrypt_mac_format_t mac_format,
                                      _mongocrypt_encryption_mode_t mode,
                                      _mongocrypt_hmac_type_t hmac,
                                      const _mongocrypt_buffer_t *associated_data,
                                      const _mongocrypt_buffer_t *key,
                                      const _mongocrypt_buffer_t *ciphertext,
                                      _mongocrypt_buffer_t *plaintext,
                                      uint32_t *bytes_written,
                                      mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    /* associated_data is checked at the point it is used, so it can be NULL */
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT_PARAM(plaintext);
    BSON_ASSERT_PARAM(bytes_written);

    const uint32_t expect_plaintext_len = _mongocrypt_calculate_plaintext_len(ciphertext->len, mode, hmac, status);
    if (mongocrypt_status_type(status) != MONGOCRYPT_STATUS_OK) {
        return false;
    }
    if (plaintext->len != expect_plaintext_len) {
        CLIENT_ERR("output plaintext should have been allocated with %" PRIu32 " bytes, "
                   "but has: %" PRIu32,
                   expect_plaintext_len,
                   plaintext->len);
        return false;
    }
    if (expect_plaintext_len == 0) {
        // While a ciphertext string describing a zero length plaintext is
        // technically valid,
        // it's not actually particularly useful in the context of FLE where such
        // values aren't encoded.
        CLIENT_ERR("input ciphertext too small. Must be more than %" PRIu32 " bytes",
                   _mongocrypt_calculate_ciphertext_len(0, mode, hmac, NULL));
        return false;
    }

    const uint32_t expected_key_len = (key_format == KEY_FORMAT_FLE2) ? MONGOCRYPT_ENC_KEY_LEN : MONGOCRYPT_KEY_LEN;
    if (expected_key_len != key->len) {
        CLIENT_ERR("key should have length %" PRIu32 ", but has length %" PRIu32, expected_key_len, key->len);
        return false;
    }

    const uint32_t min_cipherlen = _mongocrypt_calculate_ciphertext_len(0, mode, hmac, NULL);
    if (ciphertext->len < min_cipherlen) {
        CLIENT_ERR("corrupt ciphertext - must be >= %" PRIu32 " bytes", min_cipherlen);
        return false;
    }

    _mongocrypt_buffer_t Ke;
    const uint32_t Ke_offset = (key_format == KEY_FORMAT_FLE1) ? MONGOCRYPT_MAC_KEY_LEN : 0;
    if (!_mongocrypt_buffer_from_subrange(&Ke, key, Ke_offset, MONGOCRYPT_ENC_KEY_LEN)) {
        CLIENT_ERR("unable to create Ke subrange from key");
        return false;
    }

    _mongocrypt_buffer_t IV;
    if (!_mongocrypt_buffer_from_subrange(&IV, ciphertext, 0, MONGOCRYPT_IV_LEN)) {
        CLIENT_ERR("unable to create IV subrange from ciphertext");
        return false;
    }

    if (hmac == HMAC_NONE) {
        BSON_ASSERT(key_format == KEY_FORMAT_FLE2);

    } else {
        BSON_ASSERT(key_format != KEY_FORMAT_FLE2);

        uint8_t hmac_tag_storage[MONGOCRYPT_HMAC_LEN];
        const uint32_t mac_key_offset = (key_format == KEY_FORMAT_FLE1) ? 0 : MONGOCRYPT_ENC_KEY_LEN;
        _mongocrypt_buffer_t Km;
        if (!_mongocrypt_buffer_from_subrange(&Km, key, mac_key_offset, MONGOCRYPT_MAC_KEY_LEN)) {
            CLIENT_ERR("unable to create Km subrange from key");
            return false;
        }

        _mongocrypt_buffer_t iv_and_ciphertext;
        if (!_mongocrypt_buffer_from_subrange(&iv_and_ciphertext,
                                              ciphertext,
                                              0,
                                              ciphertext->len - MONGOCRYPT_HMAC_LEN)) {
            CLIENT_ERR("unable to create IV || S subrange from C");
            return false;
        }

        _mongocrypt_buffer_t hmac_tag = {.data = hmac_tag_storage, .len = MONGOCRYPT_HMAC_LEN};

        if (!_hmac_step(crypto, mac_format, hmac, &Km, associated_data, &iv_and_ciphertext, &hmac_tag, status)) {
            return false;
        }

        /* Constant time compare. */
        _mongocrypt_buffer_t T;
        if (!_mongocrypt_buffer_from_subrange(&T,
                                              ciphertext,
                                              ciphertext->len - MONGOCRYPT_HMAC_LEN,
                                              MONGOCRYPT_HMAC_LEN)) {
            CLIENT_ERR("unable to create T subrange from C");
            return false;
        }
        if (0 != _mongocrypt_memequal(hmac_tag.data, T.data, MONGOCRYPT_HMAC_LEN)) {
            CLIENT_ERR("HMAC validation failure");
            return false;
        }
    }

    /* Decrypt data excluding IV + HMAC. */
    const uint32_t hmac_len = (hmac == HMAC_NONE) ? 0 : MONGOCRYPT_HMAC_LEN;
    _mongocrypt_buffer_t S;
    if (!_mongocrypt_buffer_from_subrange(&S,
                                          ciphertext,
                                          MONGOCRYPT_IV_LEN,
                                          ciphertext->len - MONGOCRYPT_IV_LEN - hmac_len)) {
        CLIENT_ERR("unable to create S subrange from C");
        return false;
    }

    return _decrypt_step(crypto, mode, &IV, &Ke, &S, plaintext, bytes_written, status);
}

#define DECLARE_ALGORITHM(name, mode, hmac)                                                                            \
    static uint32_t _mc_##name##_ciphertext_len(uint32_t plaintext_len, mongocrypt_status_t *status) {                 \
        return _mongocrypt_calculate_ciphertext_len(plaintext_len, MODE_##mode, HMAC_##hmac, status);                  \
    }                                                                                                                  \
    static uint32_t _mc_##name##_plaintext_len(uint32_t ciphertext_len, mongocrypt_status_t *status) {                 \
        return _mongocrypt_calculate_plaintext_len(ciphertext_len, MODE_##mode, HMAC_##hmac, status);                  \
    }                                                                                                                  \
    static bool _mc_##name##_do_encryption(_mongocrypt_crypto_t *crypto,                                               \
                                           const _mongocrypt_buffer_t *iv,                                             \
                                           const _mongocrypt_buffer_t *aad,                                            \
                                           const _mongocrypt_buffer_t *key,                                            \
                                           const _mongocrypt_buffer_t *plaintext,                                      \
                                           _mongocrypt_buffer_t *ciphertext,                                           \
                                           uint32_t *written,                                                          \
                                           mongocrypt_status_t *status) {                                              \
        return _mongocrypt_do_encryption(crypto,                                                                       \
                                         KEY_FORMAT_##name,                                                            \
                                         MAC_FORMAT_##name,                                                            \
                                         MODE_##mode,                                                                  \
                                         HMAC_##hmac,                                                                  \
                                         iv,                                                                           \
                                         aad,                                                                          \
                                         key,                                                                          \
                                         plaintext,                                                                    \
                                         ciphertext,                                                                   \
                                         written,                                                                      \
                                         status);                                                                      \
    }                                                                                                                  \
    static bool _mc_##name##_do_decryption(_mongocrypt_crypto_t *crypto,                                               \
                                           const _mongocrypt_buffer_t *aad,                                            \
                                           const _mongocrypt_buffer_t *key,                                            \
                                           const _mongocrypt_buffer_t *ciphertext,                                     \
                                           _mongocrypt_buffer_t *plaintext,                                            \
                                           uint32_t *written,                                                          \
                                           mongocrypt_status_t *status) {                                              \
        return _mongocrypt_do_decryption(crypto,                                                                       \
                                         KEY_FORMAT_##name,                                                            \
                                         MAC_FORMAT_##name,                                                            \
                                         MODE_##mode,                                                                  \
                                         HMAC_##hmac,                                                                  \
                                         aad,                                                                          \
                                         key,                                                                          \
                                         ciphertext,                                                                   \
                                         plaintext,                                                                    \
                                         written,                                                                      \
                                         status);                                                                      \
    }                                                                                                                  \
    static const _mongocrypt_value_encryption_algorithm_t _mc##name##Algorithm_definition = {                          \
        _mc_##name##_ciphertext_len,                                                                                   \
        _mc_##name##_plaintext_len,                                                                                    \
        _mc_##name##_do_encryption,                                                                                    \
        _mc_##name##_do_decryption,                                                                                    \
    };                                                                                                                 \
    const _mongocrypt_value_encryption_algorithm_t *_mc##name##Algorithm(void) {                                       \
        return &_mc##name##Algorithm_definition;                                                                       \
    }

// FLE1 algorithm: AES-256-CBC HMAC/SHA-512-256 (SHA-512 truncated to 256 bits)
DECLARE_ALGORITHM(FLE1, CBC, SHA_512_256)

// FLE2 AEAD used value algorithm: AES-256-CTR HMAC/SHA-256
DECLARE_ALGORITHM(FLE2AEAD, CTR, SHA_256)

// FLE2 used with ESC/ECOC tokens: AES-256-CTR no HMAC
DECLARE_ALGORITHM(FLE2, CTR, NONE)

// FLE2v2 AEAD general algorithm: AES-256-CBC HMAC/SHA-256
DECLARE_ALGORITHM(FLE2v2AEAD, CBC, SHA_256)

#undef DECLARE_ALGORITHM

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_random --
 *
 *    Generates a string of random bytes.
 *
 * Parameters:
 *    @out an output buffer that has been pre-allocated.
 *    @status set on error.
 *    @count the size of the random string in bytes.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 *  Preconditions:
 *    1. out has been pre-allocated with at least 'count' bytes of space.
 *
 * ----------------------------------------------------------------------------
 */
bool _mongocrypt_random(_mongocrypt_crypto_t *crypto,
                        _mongocrypt_buffer_t *out,
                        uint32_t count,
                        mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(out);

    if (count != out->len) {
        CLIENT_ERR("out should have length %" PRIu32 ", but has length %" PRIu32, count, out->len);
        return false;
    }

    return _crypto_random(crypto, out, count, status);
}

/* ----------------------------------------------------------------------------
 *
 * _mongocrypt_calculate_deterministic_iv --
 *
 *    Compute the IV for deterministic encryption from the plaintext and IV
 *    key by using HMAC function.
 *
 * Parameters:
 *    @key the 96 byte key. The last 32 represent the IV key.
 *    @plaintext the plaintext to be encrypted.
 *    @associated_data associated data to include in the HMAC.
 *    @out an output buffer that has been pre-allocated.
 *    @status set on error.
 *
 * Returns:
 *    True on success. On error, sets @status and returns false.
 *
 *  Preconditions:
 *    1. out has been pre-allocated with at least MONGOCRYPT_IV_LEN bytes.
 *
 * ----------------------------------------------------------------------------
 */
bool _mongocrypt_calculate_deterministic_iv(_mongocrypt_crypto_t *crypto,
                                            const _mongocrypt_buffer_t *key,
                                            const _mongocrypt_buffer_t *plaintext,
                                            const _mongocrypt_buffer_t *associated_data,
                                            _mongocrypt_buffer_t *out,
                                            mongocrypt_status_t *status) {
    _mongocrypt_buffer_t intermediates[3];
    _mongocrypt_buffer_t to_hmac;
    _mongocrypt_buffer_t iv_key;
    uint64_t associated_data_len_be;
    uint8_t tag_storage[64];
    _mongocrypt_buffer_t tag;
    bool ret = false;

    _mongocrypt_buffer_init(&to_hmac);

    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(plaintext);
    BSON_ASSERT_PARAM(associated_data);
    BSON_ASSERT_PARAM(out);

    if (MONGOCRYPT_KEY_LEN != key->len) {
        CLIENT_ERR("key should have length %d, but has length %" PRIu32 "\n", MONGOCRYPT_KEY_LEN, key->len);
        goto done;
    }
    if (MONGOCRYPT_IV_LEN != out->len) {
        CLIENT_ERR("out should have length %d, but has length %" PRIu32 "\n", MONGOCRYPT_IV_LEN, out->len);
        goto done;
    }

    _mongocrypt_buffer_init(&iv_key);
    iv_key.data = key->data + MONGOCRYPT_ENC_KEY_LEN + MONGOCRYPT_MAC_KEY_LEN;
    iv_key.len = MONGOCRYPT_IV_KEY_LEN;

    _mongocrypt_buffer_init(&intermediates[0]);
    _mongocrypt_buffer_init(&intermediates[1]);
    _mongocrypt_buffer_init(&intermediates[2]);
    /* Add associated data. */
    intermediates[0].data = associated_data->data;
    intermediates[0].len = associated_data->len;
    /* Add associated data length in bits. */
    /* multiplying a uint32_t by 8 won't bring it anywhere close to UINT64_MAX */
    associated_data_len_be = 8 * (uint64_t)associated_data->len;
    associated_data_len_be = BSON_UINT64_TO_BE(associated_data_len_be);
    intermediates[1].data = (uint8_t *)&associated_data_len_be;
    intermediates[1].len = sizeof(uint64_t);
    /* Add plaintext. */
    intermediates[2].data = (uint8_t *)plaintext->data;
    intermediates[2].len = plaintext->len;

    tag.data = tag_storage;
    tag.len = sizeof(tag_storage);

    if (!_mongocrypt_buffer_concat(&to_hmac, intermediates, 3)) {
        CLIENT_ERR("failed to allocate buffer");
        goto done;
    }

    if (!_crypto_hmac_sha_512(crypto, &iv_key, &to_hmac, &tag, status)) {
        goto done;
    }

    /* Truncate to IV length */
    memcpy(out->data, tag.data, MONGOCRYPT_IV_LEN);

    ret = true;
done:
    _mongocrypt_buffer_cleanup(&to_hmac);
    return ret;
}

bool _mongocrypt_wrap_key(_mongocrypt_crypto_t *crypto,
                          _mongocrypt_buffer_t *kek,
                          _mongocrypt_buffer_t *dek,
                          _mongocrypt_buffer_t *encrypted_dek,
                          mongocrypt_status_t *status) {
    const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
    uint32_t bytes_written;
    _mongocrypt_buffer_t iv = {0};
    bool ret = false;

    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(kek);
    BSON_ASSERT_PARAM(dek);
    BSON_ASSERT_PARAM(encrypted_dek);

    _mongocrypt_buffer_init(encrypted_dek);

    if (dek->len != MONGOCRYPT_KEY_LEN) {
        CLIENT_ERR("data encryption key is incorrect length, expected: %d, got: %" PRIu32,
                   MONGOCRYPT_KEY_LEN,
                   dek->len);
        goto done;
    }

    // _mongocrypt_wrap_key() uses FLE1 algorithm parameters.
    _mongocrypt_buffer_resize(encrypted_dek, fle1alg->get_ciphertext_len(dek->len, status));
    _mongocrypt_buffer_resize(&iv, MONGOCRYPT_IV_LEN);

    if (!_mongocrypt_random(crypto, &iv, MONGOCRYPT_IV_LEN, status)) {
        goto done;
    }

    if (!fle1alg
             ->do_encrypt(crypto, &iv, NULL /* associated data. */, kek, dek, encrypted_dek, &bytes_written, status)) {
        goto done;
    }

    ret = true;
done:
    _mongocrypt_buffer_cleanup(&iv);
    return ret;
}

bool _mongocrypt_unwrap_key(_mongocrypt_crypto_t *crypto,
                            _mongocrypt_buffer_t *kek,
                            _mongocrypt_buffer_t *encrypted_dek,
                            _mongocrypt_buffer_t *dek,
                            mongocrypt_status_t *status) {
    const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
    uint32_t bytes_written;

    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(kek);
    BSON_ASSERT_PARAM(dek);
    BSON_ASSERT_PARAM(encrypted_dek);

    // _mongocrypt_wrap_key() uses FLE1 algorithm parameters.
    _mongocrypt_buffer_init(dek);
    _mongocrypt_buffer_resize(dek, fle1alg->get_plaintext_len(encrypted_dek->len, status));

    if (!fle1alg->do_decrypt(crypto, NULL /* associated data. */, kek, encrypted_dek, dek, &bytes_written, status)) {
        return false;
    }
    dek->len = bytes_written;

    if (dek->len != MONGOCRYPT_KEY_LEN) {
        CLIENT_ERR("decrypted key is incorrect length, expected: %d, got: %" PRIu32, MONGOCRYPT_KEY_LEN, dek->len);
        return false;
    }
    return true;
}

/* This implementation avoids modulo bias. It is based on arc4random_uniform:
https://github.com/openbsd/src/blob/2207c4325726fdc5c4bcd0011af0fdf7d3dab137/lib/libc/crypt/arc4random_uniform.c#L33
*/
bool _mongocrypt_random_uint64(_mongocrypt_crypto_t *crypto,
                               uint64_t exclusive_upper_bound,
                               uint64_t *out,
                               mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(out);

    *out = 0;

    if (exclusive_upper_bound < 2) {
        *out = 0;
        return true;
    }

    /* 2**64 % x == (2**64 - x) % x */
    uint64_t min = (0 - exclusive_upper_bound) % exclusive_upper_bound;

    _mongocrypt_buffer_t rand_u64_buf;
    _mongocrypt_buffer_init(&rand_u64_buf);
    _mongocrypt_buffer_resize(&rand_u64_buf, (uint32_t)sizeof(uint64_t));

    uint64_t rand_u64;
    for (;;) {
        if (!_mongocrypt_random(crypto, &rand_u64_buf, rand_u64_buf.len, status)) {
            _mongocrypt_buffer_cleanup(&rand_u64_buf);
            return false;
        }

        memcpy(&rand_u64, rand_u64_buf.data, rand_u64_buf.len);
        // Use little-endian to enable deterministic tests on big-endian machines.
        rand_u64 = BSON_UINT64_FROM_LE(rand_u64);

        if (rand_u64 >= min) {
            break;
        }
    }

    *out = rand_u64 % exclusive_upper_bound;

    _mongocrypt_buffer_cleanup(&rand_u64_buf);
    return true;
}

bool _mongocrypt_random_int64(_mongocrypt_crypto_t *crypto,
                              int64_t exclusive_upper_bound,
                              int64_t *out,
                              mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(out);

    if (exclusive_upper_bound <= 0) {
        CLIENT_ERR("Expected exclusive_upper_bound > 0");
        return false;
    }

    uint64_t u64_exclusive_upper_bound = (uint64_t)exclusive_upper_bound;
    uint64_t u64_out;

    if (!_mongocrypt_random_uint64(crypto, u64_exclusive_upper_bound, &u64_out, status)) {
        return false;
    }

    /* Zero the leading bit to ensure rand_i64 is non-negative. */
    u64_out &= (~(1ull << 63));
    *out = (int64_t)u64_out;
    return true;
}
libmongocrypt-1.19.0/src/mongocrypt-ctx-datakey.c000066400000000000000000000562261521103432300220470ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"

static void _cleanup(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_datakey_t *dkctx;

    BSON_ASSERT_PARAM(ctx);

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
    _mongocrypt_buffer_cleanup(&dkctx->key_doc);
    _mongocrypt_kms_ctx_cleanup(&dkctx->kms);
    _mongocrypt_buffer_cleanup(&dkctx->encrypted_key_material);
    _mongocrypt_buffer_cleanup(&dkctx->plaintext_key_material);
    _mongocrypt_buffer_cleanup(&dkctx->kmip_secretdata);
    bson_free((void *)dkctx->kmip_unique_identifier);
}

static mongocrypt_kms_ctx_t *_next_kms_ctx(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_datakey_t *dkctx;

    BSON_ASSERT_PARAM(ctx);

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
    if (!dkctx->kms.should_retry && dkctx->kms_returned) {
        return NULL;
    }
    dkctx->kms.should_retry = false; // Reset retry state.
    dkctx->kms_returned = true;
    return &dkctx->kms;
}

static bool _kms_kmip_start(mongocrypt_ctx_t *ctx, const mc_kms_creds_t *kc) {
    bool ret = false;
    _mongocrypt_ctx_datakey_t *dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
    char *user_supplied_keyid = NULL;
    const _mongocrypt_endpoint_t *endpoint = NULL;
    mongocrypt_status_t *status = ctx->status;
    _mongocrypt_buffer_t secretdata = {0};

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(kc);
    BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_KMIP);

    if (ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_KMIP) {
        CLIENT_ERR("KMS provider is not KMIP");
        goto fail;
    }
    const bool delegated = ctx->opts.kek.provider.kmip.delegated;

    user_supplied_keyid = ctx->opts.kek.provider.kmip.key_id;

    if (ctx->opts.kek.provider.kmip.endpoint) {
        endpoint = ctx->opts.kek.provider.kmip.endpoint;
    } else if (kc->value.kmip.endpoint) {
        endpoint = kc->value.kmip.endpoint;
    } else {
        CLIENT_ERR("endpoint not set for KMIP request");
        goto fail;
    }

    if (user_supplied_keyid && !dkctx->kmip_unique_identifier) {
        /* User set a 'keyId'. */
        dkctx->kmip_unique_identifier = bson_strdup(user_supplied_keyid);
        dkctx->kmip_activated = true;
    }

    if (delegated) {
        /*
         * The KMIP delegated createDataKey flow is the following:
         * 1. Send a KMIP Create request (symmetric key) (returns keyId)
         * 2. Send a KMIP Activate request with that keyId
         * 3. Send a KMIP Encrypt request to encrypt the DEK
         *
         * Steps 1 and 2 are skipped if the user provided a keyId
         */

        if (!dkctx->kmip_unique_identifier) {
            /* User did not set a 'keyId'. */
            /* Step 1. Send a KMIP Create request for a new AES-256 symmetric key. */
            if (!_mongocrypt_kms_ctx_init_kmip_create(&dkctx->kms, endpoint, ctx->opts.kek.kmsid, &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }

        if (!dkctx->kmip_activated) {
            /* Step 2. Send a KMIP Activate request. */
            if (!_mongocrypt_kms_ctx_init_kmip_activate(&dkctx->kms,
                                                        endpoint,
                                                        dkctx->kmip_unique_identifier,
                                                        ctx->opts.kek.kmsid,
                                                        &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }

        if (!dkctx->encrypted_key_material.data) {
            /* Step 3. Have the KMS encrypt a new DEK. */
            if (!_mongocrypt_kms_ctx_init_kmip_encrypt(&dkctx->kms,
                                                       endpoint,
                                                       dkctx->kmip_unique_identifier,
                                                       ctx->opts.kek.kmsid,
                                                       &dkctx->plaintext_key_material,
                                                       &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }
    } else {
        /* The KMIP createDataKey flow is the following:
         *
         * 1. Send a KMIP Register request with a new 96 byte key as a SecretData
         *    managed object. This returns a Unique Identifier.
         * 2. Send a KMIP Activate request with the Unique Identifier.
         *    This returns the same Unique Identifier.
         * 3. Send a KMIP Get request with the Unique Identifier.
         *    This returns the 96 byte SecretData.
         * 4. Use the 96 byte SecretData to encrypt a new DEK.
         *
         * If the user set a 'keyId' to use, the flow begins at step 3.
         */
        if (!dkctx->kmip_unique_identifier) {
            /* User did not set a 'keyId'. */
            /* Step 1. Send a KMIP Register request with a new 96 byte SecretData. */
            _mongocrypt_buffer_init(&secretdata);
            _mongocrypt_buffer_resize(&secretdata, MONGOCRYPT_KEY_LEN);
            if (!_mongocrypt_random(ctx->crypt->crypto, &secretdata, MONGOCRYPT_KEY_LEN, ctx->status)) {
                goto fail;
            }

            if (!_mongocrypt_kms_ctx_init_kmip_register(&dkctx->kms,
                                                        endpoint,
                                                        secretdata.data,
                                                        secretdata.len,
                                                        ctx->opts.kek.kmsid,
                                                        &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }

        if (!dkctx->kmip_activated) {
            /* Step 2. Send a KMIP Activate request. */
            if (!_mongocrypt_kms_ctx_init_kmip_activate(&dkctx->kms,
                                                        endpoint,
                                                        dkctx->kmip_unique_identifier,
                                                        ctx->opts.kek.kmsid,
                                                        &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }

        if (!dkctx->kmip_secretdata.data) {
            /* Step 3. Send a KMIP Get request with the Unique Identifier. */
            if (!_mongocrypt_kms_ctx_init_kmip_get(&dkctx->kms,
                                                   endpoint,
                                                   dkctx->kmip_unique_identifier,
                                                   ctx->opts.kek.kmsid,
                                                   &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                goto fail;
            }
            ctx->state = MONGOCRYPT_CTX_NEED_KMS;
            goto success;
        }

        /* Step 4. Use the 96 byte SecretData to encrypt a new DEK. */
        if (!_mongocrypt_wrap_key(ctx->crypt->crypto,
                                  &dkctx->kmip_secretdata,
                                  &dkctx->plaintext_key_material,
                                  &dkctx->encrypted_key_material,
                                  ctx->status)) {
            goto fail;
        }
    }

    if (!ctx->opts.kek.provider.kmip.key_id) {
        /* If there was no user supplied key_id, set it from the
         * UniqueIdentifer of the newly registered SecretData. */
        ctx->opts.kek.provider.kmip.key_id = bson_strdup(dkctx->kmip_unique_identifier);
    }
    ctx->state = MONGOCRYPT_CTX_READY;

success:
    ret = true;
fail:
    if (!ret) {
        _mongocrypt_ctx_fail(ctx);
    }
    _mongocrypt_buffer_cleanup(&secretdata);
    return ret;
}

/* For local, immediately encrypt.
 * For AWS, create the KMS request to encrypt.
 * For Azure/GCP, auth first if needed, otherwise encrypt.
 */
static bool _kms_start(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    bool ret = false;
    _mongocrypt_ctx_datakey_t *dkctx;
    char *access_token = NULL;
    _mongocrypt_opts_kms_providers_t *const kms_providers = _mongocrypt_ctx_kms_providers(ctx);

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;

    mc_kms_creds_t kc;
    if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, ctx->opts.kek.kmsid, &kc)) {
        mongocrypt_status_t *status = ctx->status;
        CLIENT_ERR("KMS provider `%s` is not configured", ctx->opts.kek.kmsid);
        _mongocrypt_ctx_fail(ctx);
        goto done;
    }

    /* Clear out any pre-existing initialized KMS context, and zero it (so it is
     * safe to call cleanup again). */
    _mongocrypt_kms_ctx_cleanup(&dkctx->kms);
    memset(&dkctx->kms, 0, sizeof(dkctx->kms));
    dkctx->kms_returned = false;
    if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_LOCAL);
        if (!_mongocrypt_wrap_key(ctx->crypt->crypto,
                                  &kc.value.local.key,
                                  &dkctx->plaintext_key_material,
                                  &dkctx->encrypted_key_material,
                                  ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto done;
        }
        ctx->state = MONGOCRYPT_CTX_READY;
    } else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        /* For AWS provider, AWS credentials are supplied in
         * mongocrypt_setopt_kms_provider_aws. Data keys are encrypted with an
         * "encrypt" HTTP message to KMS. */
        if (!_mongocrypt_kms_ctx_init_aws_encrypt(&dkctx->kms,
                                                  kms_providers,
                                                  &ctx->opts,
                                                  &dkctx->plaintext_key_material,
                                                  ctx->crypt->crypto,
                                                  ctx->opts.kek.kmsid,
                                                  &ctx->crypt->log)) {
            mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
            _mongocrypt_ctx_fail(ctx);
            goto done;
        }

        ctx->state = MONGOCRYPT_CTX_NEED_KMS;
    } else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
        if (kc.value.azure.access_token) {
            access_token = bson_strdup(kc.value.azure.access_token);
        } else {
            access_token = mc_mapof_kmsid_to_token_get_token(ctx->crypt->cache_oauth, ctx->opts.kek.kmsid);
        }
        if (access_token) {
            if (!_mongocrypt_kms_ctx_init_azure_wrapkey(&dkctx->kms,
                                                        kms_providers,
                                                        &ctx->opts,
                                                        access_token,
                                                        &dkctx->plaintext_key_material,
                                                        ctx->opts.kek.kmsid,
                                                        &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                _mongocrypt_ctx_fail(ctx);
                goto done;
            }
        } else {
            if (!_mongocrypt_kms_ctx_init_azure_auth(&dkctx->kms,
                                                     &kc,
                                                     ctx->opts.kek.provider.azure.key_vault_endpoint,
                                                     ctx->opts.kek.kmsid,
                                                     &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                _mongocrypt_ctx_fail(ctx);
                goto done;
            }
        }
        ctx->state = MONGOCRYPT_CTX_NEED_KMS;
    } else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
        if (NULL != kc.value.gcp.access_token) {
            access_token = bson_strdup(kc.value.gcp.access_token);
        } else {
            access_token = mc_mapof_kmsid_to_token_get_token(ctx->crypt->cache_oauth, ctx->opts.kek.kmsid);
        }
        if (access_token) {
            if (!_mongocrypt_kms_ctx_init_gcp_encrypt(&dkctx->kms,
                                                      kms_providers,
                                                      &ctx->opts,
                                                      access_token,
                                                      &dkctx->plaintext_key_material,
                                                      ctx->opts.kek.kmsid,
                                                      &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                _mongocrypt_ctx_fail(ctx);
                goto done;
            }
        } else {
            if (!_mongocrypt_kms_ctx_init_gcp_auth(&dkctx->kms,
                                                   &ctx->crypt->opts,
                                                   &kc,
                                                   ctx->opts.kek.provider.gcp.endpoint,
                                                   ctx->opts.kek.kmsid,
                                                   &ctx->crypt->log)) {
                mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status);
                _mongocrypt_ctx_fail(ctx);
                goto done;
            }
        }
        ctx->state = MONGOCRYPT_CTX_NEED_KMS;
    } else if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
        if (!_kms_kmip_start(ctx, &kc)) {
            goto done;
        }
    } else {
        _mongocrypt_ctx_fail_w_msg(ctx, "unsupported KMS provider");
        goto done;
    }

    ret = true;
done:
    bson_free(access_token);
    return ret;
}

static bool _kms_done(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_datakey_t *dkctx;
    mongocrypt_status_t *status;

    BSON_ASSERT_PARAM(ctx);

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
    status = ctx->status;
    if (!mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (mongocrypt_kms_ctx_bytes_needed(&dkctx->kms) != 0) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "KMS response unfinished");
    }

    /* If this was an oauth request, store the response and proceed to encrypt.
     */
    if (dkctx->kms.req_type == MONGOCRYPT_KMS_AZURE_OAUTH) {
        bson_t oauth_response;

        BSON_ASSERT(_mongocrypt_buffer_to_bson(&dkctx->kms.result, &oauth_response));
        if (!mc_mapof_kmsid_to_token_add_response(ctx->crypt->cache_oauth,
                                                  ctx->opts.kek.kmsid,
                                                  &oauth_response,
                                                  status)) {
            return _mongocrypt_ctx_fail(ctx);
        }
        return _kms_start(ctx);
    } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_GCP_OAUTH) {
        bson_t oauth_response;

        BSON_ASSERT(_mongocrypt_buffer_to_bson(&dkctx->kms.result, &oauth_response));
        if (!mc_mapof_kmsid_to_token_add_response(ctx->crypt->cache_oauth,
                                                  ctx->opts.kek.kmsid,
                                                  &oauth_response,
                                                  status)) {
            return _mongocrypt_ctx_fail(ctx);
        }
        return _kms_start(ctx);
    } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_REGISTER
               || dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_CREATE) {
        dkctx->kmip_unique_identifier = bson_strdup((const char *)dkctx->kms.result.data);
        return _kms_start(ctx);
    } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_ACTIVATE) {
        dkctx->kmip_activated = true;
        return _kms_start(ctx);
    } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_GET) {
        _mongocrypt_buffer_copy_to(&dkctx->kms.result, &dkctx->kmip_secretdata);
        return _kms_start(ctx);
    } else if (dkctx->kms.req_type == MONGOCRYPT_KMS_KMIP_ENCRYPT) {
        _mongocrypt_buffer_copy_to(&dkctx->kms.result, &dkctx->encrypted_key_material);
        return _kms_start(ctx);
    }

    /* Store the result. */
    if (!_mongocrypt_kms_ctx_result(&dkctx->kms, &dkctx->encrypted_key_material)) {
        BSON_ASSERT(!mongocrypt_kms_ctx_status(&dkctx->kms, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }

    /* The encrypted key material must be at least as large as the plaintext. */
    if (dkctx->encrypted_key_material.len < MONGOCRYPT_KEY_LEN) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "key material not expected length");
    }

    ctx->state = MONGOCRYPT_CTX_READY;
    return true;
}

/* Append a UUID _id. Confer with libmongoc's `_mongoc_server_session_uuid`. */
static bool _append_id(mongocrypt_t *crypt, bson_t *bson, mongocrypt_status_t *status) {
    _mongocrypt_buffer_t uuid;

    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(bson);

    _mongocrypt_buffer_init(&uuid);
    uuid.data = bson_malloc(UUID_LEN);
    BSON_ASSERT(uuid.data);

    uuid.len = UUID_LEN;
    uuid.subtype = BSON_SUBTYPE_UUID;
    uuid.owned = true;

    if (!_mongocrypt_random(crypt->crypto, &uuid, UUID_LEN, status)) {
        _mongocrypt_buffer_cleanup(&uuid);
        return false;
    }

    uuid.data[6] = (uint8_t)(0x40 | (uuid.data[6] & 0xf));
    uuid.data[8] = (uint8_t)(0x80 | (uuid.data[8] & 0x3f));
    if (!_mongocrypt_buffer_append(&uuid, bson, "_id", 3)) {
        _mongocrypt_buffer_cleanup(&uuid);
        return false;
    }

    _mongocrypt_buffer_cleanup(&uuid);

    return true;
}

static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    _mongocrypt_ctx_datakey_t *dkctx;
    bson_t key_doc, child;
    struct timeval tp;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

#define BSON_CHECK(_stmt)                                                                                              \
    if (!(_stmt)) {                                                                                                    \
        bson_destroy(&key_doc);                                                                                        \
        return _mongocrypt_ctx_fail_w_msg(ctx, "unable to construct BSON doc");                                        \
    } else                                                                                                             \
        ((void)0)

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;

    bson_init(&key_doc);
    if (!_append_id(ctx->crypt, &key_doc, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (ctx->opts.key_alt_names) {
        _mongocrypt_key_alt_name_t *alt_name = ctx->opts.key_alt_names;
        int i;

        bson_append_array_unsafe_begin(&key_doc, "keyAltNames", -1, &child);
        for (i = 0; alt_name; i++) {
            char *key = bson_strdup_printf("%d", i);
            bson_append_value(&child, key, -1, &alt_name->value);
            bson_free(key);
            alt_name = alt_name->next;
        }
        bson_append_array_end(&key_doc, &child);
    }
    if (!_mongocrypt_buffer_append(&dkctx->encrypted_key_material, &key_doc, MONGOCRYPT_STR_AND_LEN("keyMaterial"))) {
        bson_destroy(&key_doc);
        return _mongocrypt_ctx_fail_w_msg(ctx, "could not append keyMaterial");
    }
    bson_gettimeofday(&tp);
    BSON_CHECK(bson_append_timeval(&key_doc, MONGOCRYPT_STR_AND_LEN("creationDate"), &tp));
    BSON_CHECK(bson_append_timeval(&key_doc, MONGOCRYPT_STR_AND_LEN("updateDate"), &tp));
    BSON_CHECK(bson_append_int32(&key_doc, MONGOCRYPT_STR_AND_LEN("status"), 0)); /* 0 = enabled. */
    BSON_CHECK(bson_append_document_begin(&key_doc, MONGOCRYPT_STR_AND_LEN("masterKey"), &child));
    if (!_mongocrypt_kek_append(&ctx->opts.kek, &child, ctx->status)) {
        bson_destroy(&key_doc);
        return _mongocrypt_ctx_fail(ctx);
    }
    BSON_CHECK(bson_append_document_end(&key_doc, &child));
    _mongocrypt_buffer_steal_from_bson(&dkctx->key_doc, &key_doc);
    _mongocrypt_buffer_to_binary(&dkctx->key_doc, out);
    ctx->state = MONGOCRYPT_CTX_DONE;
    return true;
}

bool mongocrypt_ctx_datakey_init(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_datakey_t *dkctx;
    _mongocrypt_ctx_opts_spec_t opts_spec;
    bool ret;

    if (!ctx) {
        return false;
    }
    ret = false;
    memset(&opts_spec, 0, sizeof(opts_spec));
    opts_spec.kek = OPT_REQUIRED;
    opts_spec.key_alt_names = OPT_OPTIONAL;
    opts_spec.key_material = OPT_OPTIONAL;

    if (!_mongocrypt_ctx_init(ctx, &opts_spec)) {
        return false;
    }

    dkctx = (_mongocrypt_ctx_datakey_t *)ctx;
    ctx->type = _MONGOCRYPT_TYPE_CREATE_DATA_KEY;
    ctx->vtable.mongo_op_keys = NULL;
    ctx->vtable.mongo_feed_keys = NULL;
    ctx->vtable.mongo_done_keys = NULL;
    ctx->vtable.next_kms_ctx = _next_kms_ctx;
    ctx->vtable.after_kms_credentials_provided = _kms_start;
    ctx->vtable.kms_done = _kms_done;
    ctx->vtable.finalize = _finalize;
    ctx->vtable.cleanup = _cleanup;

    _mongocrypt_buffer_init(&dkctx->plaintext_key_material);

    if (ctx->opts.key_material.owned) {
        /* Use keyMaterial provided by user via DataKeyOpts. */
        _mongocrypt_buffer_steal(&dkctx->plaintext_key_material, &ctx->opts.key_material);
    } else {
        /* Generate keyMaterial instead. */
        dkctx->plaintext_key_material.data = bson_malloc(MONGOCRYPT_KEY_LEN);
        BSON_ASSERT(dkctx->plaintext_key_material.data);
        dkctx->plaintext_key_material.len = MONGOCRYPT_KEY_LEN;
        dkctx->plaintext_key_material.owned = true;
        if (!_mongocrypt_random(ctx->crypt->crypto, &dkctx->plaintext_key_material, MONGOCRYPT_KEY_LEN, ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto done;
        }
    }

    if (_mongocrypt_needs_credentials_for_provider(ctx->crypt, ctx->opts.kek.kms_provider, ctx->opts.kek.kmsid_name)) {
        ctx->state = MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS;
    } else if (!_kms_start(ctx)) {
        goto done;
    }

    ret = true;
done:
    return ret;
}
libmongocrypt-1.19.0/src/mongocrypt-ctx-decrypt.c000066400000000000000000001051511521103432300220670ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mc-fle-blob-subtype-private.h"
#include "mc-fle2-insert-update-payload-private-v2.h"
#include "mc-fle2-insert-update-payload-private.h"
#include "mc-fle2-payload-iev-private-v2.h"
#include "mc-fle2-payload-iev-private.h"
#include "mc-fle2-payload-uev-private.h"
#include "mc-fle2-payload-uev-v2-private.h"
#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-traverse-util-private.h"

#define CHECK_AND_RETURN(cond)                                                                                         \
    if (!(cond)) {                                                                                                     \
        goto fail;                                                                                                     \
    } else                                                                                                             \
        ((void)0)

#define CHECK_AND_RETURN_STATUS(cond, msg)                                                                             \
    if (!(cond)) {                                                                                                     \
        CLIENT_ERR(msg);                                                                                               \
        goto fail;                                                                                                     \
    } else                                                                                                             \
        ((void)0)

#define CHECK_AND_RETURN_KB_STATUS(cond)                                                                               \
    if (!(cond)) {                                                                                                     \
        _mongocrypt_key_broker_status(kb, status);                                                                     \
        goto fail;                                                                                                     \
    } else                                                                                                             \
        ((void)0)

static bool _replace_FLE2IndexedEncryptedValue_with_plaintext(void *ctx,
                                                              _mongocrypt_buffer_t *in,
                                                              bson_value_t *out,
                                                              mongocrypt_status_t *providedStatus) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2IndexedEncryptedValue_t *iev = mc_FLE2IndexedEncryptedValue_new();
    _mongocrypt_buffer_t S_Key = {0};
    _mongocrypt_buffer_t K_Key = {0};
    mongocrypt_status_t *status = providedStatus;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    if (providedStatus == NULL) {
        // We accept a NULL status, but add_[SK]_Key require non-NULL.
        // Make a temporary status object as needed.
        status = mongocrypt_status_new();
    }

    // Parse the IEV payload to get S_KeyId.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_parse(iev, in, status));
    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));

    // Use S_Key to decrypt envelope and get to K_KeyId.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_add_S_Key(kb->crypt->crypto, iev, &S_Key, status));
    const _mongocrypt_buffer_t *K_KeyId = mc_FLE2IndexedEncryptedValue_get_K_KeyId(iev, status);
    CHECK_AND_RETURN(K_KeyId);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, K_KeyId, &K_Key));

    // Decrypt the actual data value using K_Key.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_add_K_Key(kb->crypt->crypto, iev, &K_Key, status));
    const _mongocrypt_buffer_t *clientValue = mc_FLE2IndexedEncryptedValue_get_ClientValue(iev, status);
    CHECK_AND_RETURN(clientValue);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t original_bson_type = mc_FLE2IndexedEncryptedValue_get_original_bson_type(iev, status);
    CHECK_AND_RETURN(original_bson_type != BSON_TYPE_EOD);
    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)clientValue, (uint8_t)original_bson_type, out),
        "decrypted clientValue is not valid BSON");

    ret = true;
fail:
    if (status != providedStatus) {
        mongocrypt_status_destroy(status);
    }
    _mongocrypt_buffer_cleanup(&K_Key);
    _mongocrypt_buffer_cleanup(&S_Key);
    mc_FLE2IndexedEncryptedValue_destroy(iev);
    return ret;
}

static bool _replace_FLE2IndexedEncryptedValueV2_with_plaintext(void *ctx,
                                                                _mongocrypt_buffer_t *in,
                                                                bson_value_t *out,
                                                                mongocrypt_status_t *providedStatus) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();
    _mongocrypt_buffer_t S_Key = {0};
    _mongocrypt_buffer_t K_Key = {0};
    mongocrypt_status_t *status = providedStatus;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    if (providedStatus == NULL) {
        // We accept a NULL status, but add_[SK]_Key require non-NULL.
        // Make a temporary status object as needed.
        status = mongocrypt_status_new();
    }

    // Parse the IEV payload to get S_KeyId.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_parse(iev, in, status));
    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));

    // Use S_Key to decrypt envelope and get to K_KeyId.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_add_S_Key(kb->crypt->crypto, iev, &S_Key, status));
    const _mongocrypt_buffer_t *K_KeyId = mc_FLE2IndexedEncryptedValueV2_get_K_KeyId(iev, status);
    CHECK_AND_RETURN(K_KeyId);
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, K_KeyId, &K_Key));

    // Decrypt the actual data value using K_Key.
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_add_K_Key(kb->crypt->crypto, iev, &K_Key, status));
    const _mongocrypt_buffer_t *clientValue = mc_FLE2IndexedEncryptedValueV2_get_ClientValue(iev, status);
    CHECK_AND_RETURN(clientValue);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t bson_type = mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(iev, status);
    CHECK_AND_RETURN(bson_type != BSON_TYPE_EOD);
    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)clientValue, (uint8_t)bson_type, out),
        "decrypted clientValue is not valid BSON");

    ret = true;
fail:
    if (status != providedStatus) {
        mongocrypt_status_destroy(status);
    }
    _mongocrypt_buffer_cleanup(&K_Key);
    _mongocrypt_buffer_cleanup(&S_Key);
    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    return ret;
}

static bool _replace_FLE2UnindexedEncryptedValue_with_plaintext(void *ctx,
                                                                _mongocrypt_buffer_t *in,
                                                                bson_value_t *out,
                                                                mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2UnindexedEncryptedValue_t *uev = mc_FLE2UnindexedEncryptedValue_new();
    _mongocrypt_buffer_t key = {0};

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    // Parse the UEV payload to get the encryption key.
    CHECK_AND_RETURN(mc_FLE2UnindexedEncryptedValue_parse(uev, in, status));

    const _mongocrypt_buffer_t *key_uuid = mc_FLE2UnindexedEncryptedValue_get_key_uuid(uev, status);
    CHECK_AND_RETURN(key_uuid);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, key_uuid, &key));

    // Decrypt the actual data value using encryption key.
    const _mongocrypt_buffer_t *plaintext =
        mc_FLE2UnindexedEncryptedValue_decrypt(kb->crypt->crypto, uev, &key, status);
    CHECK_AND_RETURN(plaintext);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t original_bson_type = mc_FLE2UnindexedEncryptedValue_get_original_bson_type(uev, status);
    CHECK_AND_RETURN(original_bson_type != BSON_TYPE_EOD);

    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)plaintext, (uint8_t)original_bson_type, out),
        "decrypted plaintext is not valid BSON");

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&key);
    mc_FLE2UnindexedEncryptedValue_destroy(uev);
    return ret;
}

static bool _replace_FLE2UnindexedEncryptedValueV2_with_plaintext(void *ctx,
                                                                  _mongocrypt_buffer_t *in,
                                                                  bson_value_t *out,
                                                                  mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2UnindexedEncryptedValueV2_t *uev = mc_FLE2UnindexedEncryptedValueV2_new();
    _mongocrypt_buffer_t key = {0};

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    // Parse the UEV payload to get the encryption key.
    CHECK_AND_RETURN(mc_FLE2UnindexedEncryptedValueV2_parse(uev, in, status));

    const _mongocrypt_buffer_t *key_uuid = mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(uev, status);
    CHECK_AND_RETURN(key_uuid);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, key_uuid, &key));

    // Decrypt the actual data value using encryption key.
    const _mongocrypt_buffer_t *plaintext =
        mc_FLE2UnindexedEncryptedValueV2_decrypt(kb->crypt->crypto, uev, &key, status);
    CHECK_AND_RETURN(plaintext);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t original_bson_type = mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type(uev, status);
    CHECK_AND_RETURN(original_bson_type != BSON_TYPE_EOD);

    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)plaintext, (uint8_t)original_bson_type, out),
        "decrypted plaintext is not valid BSON");

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&key);
    mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
    return ret;
}

static bool _replace_FLE2InsertUpdatePayload_with_plaintext(void *ctx,
                                                            _mongocrypt_buffer_t *in,
                                                            bson_value_t *out,
                                                            mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2InsertUpdatePayload_t iup;
    _mongocrypt_buffer_t key = {0};

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    mc_FLE2InsertUpdatePayload_init(&iup);

    // Parse the IUP payload to get the encryption key.
    CHECK_AND_RETURN(mc_FLE2InsertUpdatePayload_parse(&iup, in, status));
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, &iup.userKeyId, &key));

    // Decrypt the actual data value using encryption key.
    const _mongocrypt_buffer_t *plaintext = mc_FLE2InsertUpdatePayload_decrypt(kb->crypt->crypto, &iup, &key, status);
    CHECK_AND_RETURN(plaintext);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t original_bson_type = iup.valueType;
    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)plaintext, (uint8_t)original_bson_type, out),
        "decrypted plaintext is not valid BSON");

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&key);
    mc_FLE2InsertUpdatePayload_cleanup(&iup);
    return ret;
}

static bool _replace_FLE2InsertUpdatePayloadV2_with_plaintext(void *ctx,
                                                              _mongocrypt_buffer_t *in,
                                                              bson_value_t *out,
                                                              mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    mc_FLE2InsertUpdatePayloadV2_t iup;
    _mongocrypt_buffer_t key = {0};

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    mc_FLE2InsertUpdatePayloadV2_init(&iup);

    // Parse the IUP payload to get the encryption key.
    CHECK_AND_RETURN(mc_FLE2InsertUpdatePayloadV2_parse(&iup, in, status));
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, &iup.userKeyId, &key));

    // Decrypt the actual data value using encryption key.
    const _mongocrypt_buffer_t *plaintext = mc_FLE2InsertUpdatePayloadV2_decrypt(kb->crypt->crypto, &iup, &key, status);
    CHECK_AND_RETURN(plaintext);

    // Marshal BSON value and type into a usable bson_value_t.
    bson_type_t original_bson_type = iup.valueType;
    CHECK_AND_RETURN_STATUS(
        _mongocrypt_buffer_to_bson_value((_mongocrypt_buffer_t *)plaintext, (uint8_t)original_bson_type, out),
        "decrypted plaintext is not valid BSON");

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&key);
    mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
    return ret;
}

static bool _replace_FLE1Payload_with_plaintext(void *ctx,
                                                _mongocrypt_buffer_t *in,
                                                bson_value_t *out,
                                                mongocrypt_status_t *status) {
    _mongocrypt_key_broker_t *kb;
    _mongocrypt_ciphertext_t ciphertext;
    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t key_material;
    _mongocrypt_buffer_t associated_data;
    uint32_t bytes_written;
    bool ret = false;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT(in->data);

    _mongocrypt_buffer_init(&plaintext);
    _mongocrypt_buffer_init(&associated_data);
    _mongocrypt_buffer_init(&key_material);
    kb = (_mongocrypt_key_broker_t *)ctx;

    CHECK_AND_RETURN(_mongocrypt_ciphertext_parse_unowned(in, &ciphertext, status));

    /* look up the key */
    CHECK_AND_RETURN_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, &ciphertext.key_id, &key_material),
                            "key not found");

    const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
    plaintext.len = fle1alg->get_plaintext_len(ciphertext.data.len, status);
    CHECK_AND_RETURN(plaintext.len != 0);
    plaintext.data = bson_malloc0(plaintext.len);
    BSON_ASSERT(plaintext.data);

    plaintext.owned = true;

    CHECK_AND_RETURN_STATUS(_mongocrypt_ciphertext_serialize_associated_data(&ciphertext, &associated_data),
                            "could not serialize associated data");

    CHECK_AND_RETURN(fle1alg->do_decrypt(kb->crypt->crypto,
                                         &associated_data,
                                         &key_material,
                                         &ciphertext.data,
                                         &plaintext,
                                         &bytes_written,
                                         status));

    plaintext.len = bytes_written;

    CHECK_AND_RETURN_STATUS(_mongocrypt_buffer_to_bson_value(&plaintext, ciphertext.original_bson_type, out),
                            "malformed encrypted bson");

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&associated_data);
    _mongocrypt_buffer_cleanup(&key_material);
    return ret;
}

static bool _replace_ciphertext_with_plaintext(void *ctx,
                                               _mongocrypt_buffer_t *in,
                                               bson_value_t *out,
                                               mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT(in->data);

    switch (in->data[0]) {
    // FLE2v2 / Text Search
    case MC_SUBTYPE_FLE2IndexedTextEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2:
        return _replace_FLE2IndexedEncryptedValueV2_with_plaintext(ctx, in, out, status);
    case MC_SUBTYPE_FLE2InsertUpdatePayloadV2:
        return _replace_FLE2InsertUpdatePayloadV2_with_plaintext(ctx, in, out, status);
    case MC_SUBTYPE_FLE2UnindexedEncryptedValueV2:
        return _replace_FLE2UnindexedEncryptedValueV2_with_plaintext(ctx, in, out, status);

    // FLE2v1
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValue:
        return _replace_FLE2IndexedEncryptedValue_with_plaintext(ctx, in, out, status);
    case MC_SUBTYPE_FLE2InsertUpdatePayload:
        return _replace_FLE2InsertUpdatePayload_with_plaintext(ctx, in, out, status);
    case MC_SUBTYPE_FLE2UnindexedEncryptedValue:
        return _replace_FLE2UnindexedEncryptedValue_with_plaintext(ctx, in, out, status);

    // FLE1
    default: return _replace_FLE1Payload_with_plaintext(ctx, in, out, status);
    }
}

static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    bson_t as_bson, final_bson;
    bson_iter_t iter = {0};
    _mongocrypt_ctx_decrypt_t *dctx;
    bool res;

    if (!ctx) {
        return false;
    }

    if (!out) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "null out parameter");
    }

    dctx = (_mongocrypt_ctx_decrypt_t *)ctx;

    if (ctx->nothing_to_do) {
        _mongocrypt_buffer_to_binary(&dctx->original_doc, out);
        ctx->state = MONGOCRYPT_CTX_DONE;
        return true;
    }

    if (!_mongocrypt_buffer_to_bson(&dctx->original_doc, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
    }

    bson_iter_init(&iter, &as_bson);
    bson_init(&final_bson);
    res = _mongocrypt_transform_binary_in_bson(_replace_ciphertext_with_plaintext,
                                               &ctx->kb,
                                               TRAVERSE_MATCH_CIPHERTEXT,
                                               &iter,
                                               &final_bson,
                                               ctx->status);
    if (!res) {
        bson_destroy(&final_bson);
        return _mongocrypt_ctx_fail(ctx);
    }

    _mongocrypt_buffer_steal_from_bson(&dctx->decrypted_doc, &final_bson);
    out->data = dctx->decrypted_doc.data;
    out->len = dctx->decrypted_doc.len;
    ctx->state = MONGOCRYPT_CTX_DONE;
    return true;
}

static bool _collect_S_KeyID_from_FLE2IndexedEncryptedValue(void *ctx,
                                                            const _mongocrypt_buffer_t *in,
                                                            mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(in);

    mc_FLE2IndexedEncryptedValue_t *iev = mc_FLE2IndexedEncryptedValue_new();
    CHECK_AND_RETURN(iev);
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_parse(iev, in, status));
    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, S_KeyId));

    ret = true;
fail:
    mc_FLE2IndexedEncryptedValue_destroy(iev);
    return ret;
}

static bool _collect_S_KeyID_from_FLE2IndexedEncryptedValueV2(void *ctx,
                                                              const _mongocrypt_buffer_t *in,
                                                              mongocrypt_status_t *status) {
    bool ret = false;
    _mongocrypt_key_broker_t *kb = ctx;
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(in);

    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();
    CHECK_AND_RETURN(iev);
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_parse(iev, in, status));
    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, S_KeyId));

    ret = true;
fail:
    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    return ret;
}

static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValueV2(void *ctx,
                                                              const _mongocrypt_buffer_t *in,
                                                              mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT(in->data);
    bool ret = false;

    BSON_ASSERT((in->data[0] == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2)
                || (in->data[0] == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2)
                || (in->data[0] == MC_SUBTYPE_FLE2IndexedTextEncryptedValue));

    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();
    _mongocrypt_buffer_t S_Key = {0};
    CHECK_AND_RETURN(iev);
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_parse(iev, in, status));

    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);

    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));

    /* Decrypt InnerEncrypted to get K_KeyId. */
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValueV2_add_S_Key(kb->crypt->crypto, iev, &S_Key, status));

    /* Add request for K_KeyId. */
    const _mongocrypt_buffer_t *K_KeyId = mc_FLE2IndexedEncryptedValueV2_get_K_KeyId(iev, status);
    CHECK_AND_RETURN(K_KeyId);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, K_KeyId));

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&S_Key);
    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    return ret;
}

static bool _collect_K_KeyID_from_FLE2IndexedEncryptedValue(void *ctx,
                                                            const _mongocrypt_buffer_t *in,
                                                            mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT(in->data);
    bool ret = false;
    _mongocrypt_buffer_t S_Key = {0};

    BSON_ASSERT((in->data[0] == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue)
                || (in->data[0] == MC_SUBTYPE_FLE2IndexedRangeEncryptedValue));

    mc_FLE2IndexedEncryptedValue_t *iev = mc_FLE2IndexedEncryptedValue_new();
    CHECK_AND_RETURN(iev);
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_parse(iev, in, status));

    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
    CHECK_AND_RETURN(S_KeyId);

    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_decrypted_key_by_id(kb, S_KeyId, &S_Key));

    /* Decrypt InnerEncrypted to get K_KeyId. */
    CHECK_AND_RETURN(mc_FLE2IndexedEncryptedValue_add_S_Key(kb->crypt->crypto, iev, &S_Key, status));

    /* Add request for K_KeyId. */
    const _mongocrypt_buffer_t *K_KeyId = mc_FLE2IndexedEncryptedValue_get_K_KeyId(iev, status);
    CHECK_AND_RETURN(K_KeyId);

    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, K_KeyId));

    ret = true;
fail:
    _mongocrypt_buffer_cleanup(&S_Key);
    mc_FLE2IndexedEncryptedValue_destroy(iev);
    return ret;
}

static bool _collect_K_KeyIDs(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT(in->data);

    switch (in->data[0]) {
    // FLE2v2 / Text Search
    case MC_SUBTYPE_FLE2IndexedTextEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2:
        return _collect_K_KeyID_from_FLE2IndexedEncryptedValueV2(ctx, in, status);

    // FLE2v1
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValue:
        return _collect_K_KeyID_from_FLE2IndexedEncryptedValue(ctx, in, status);

    default:
        // Ignore other types.
        return true;
    }
}

/* _check_for_K_KeyId must be called after requests for all S_KeyId are
 * satisfied. */
static bool _check_for_K_KeyId(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    if (ctx->kb.state != KB_DONE) {
        return true;
    }

    if (!_mongocrypt_key_broker_restart(&ctx->kb)) {
        _mongocrypt_key_broker_status(&ctx->kb, ctx->status);
        return _mongocrypt_ctx_fail(ctx);
    }

    bson_t as_bson;
    bson_iter_t iter = {0};
    _mongocrypt_ctx_decrypt_t *dctx = (_mongocrypt_ctx_decrypt_t *)ctx;
    if (!_mongocrypt_buffer_to_bson(&dctx->original_doc, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "error converting original_doc to bson");
    }
    bson_iter_init(&iter, &as_bson);

    if (!_mongocrypt_traverse_binary_in_bson(_collect_K_KeyIDs,
                                             &ctx->kb,
                                             TRAVERSE_MATCH_CIPHERTEXT,
                                             &iter,
                                             ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (!_mongocrypt_key_broker_requests_done(&ctx->kb)) {
        _mongocrypt_key_broker_status(&ctx->kb, ctx->status);
        return _mongocrypt_ctx_fail(ctx);
    }
    return true;
}

static bool _collect_key_uuid_from_FLE2UnindexedEncryptedValue(void *ctx,
                                                               const _mongocrypt_buffer_t *in,
                                                               mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    bool ret = false;

    mc_FLE2UnindexedEncryptedValue_t *uev = mc_FLE2UnindexedEncryptedValue_new();
    CHECK_AND_RETURN(uev);
    CHECK_AND_RETURN(mc_FLE2UnindexedEncryptedValue_parse(uev, in, status));

    const _mongocrypt_buffer_t *key_uuid = mc_FLE2UnindexedEncryptedValue_get_key_uuid(uev, status);
    CHECK_AND_RETURN(key_uuid);
    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, key_uuid));

    ret = true;
fail:
    mc_FLE2UnindexedEncryptedValue_destroy(uev);
    return ret;
}

static bool _collect_key_uuid_from_FLE2UnindexedEncryptedValueV2(void *ctx,
                                                                 const _mongocrypt_buffer_t *in,
                                                                 mongocrypt_status_t *status) {
    bool ret = false;
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    mc_FLE2UnindexedEncryptedValueV2_t *uev = mc_FLE2UnindexedEncryptedValueV2_new();
    CHECK_AND_RETURN(uev);
    CHECK_AND_RETURN(mc_FLE2UnindexedEncryptedValueV2_parse(uev, in, status));

    const _mongocrypt_buffer_t *key_uuid = mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(uev, status);
    CHECK_AND_RETURN(key_uuid);
    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, key_uuid));

    ret = true;
fail:
    mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
    return ret;
}

static bool
_collect_key_uuid_from_FLE2InsertUpdatePayload(void *ctx, const _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    bool ret = false;

    mc_FLE2InsertUpdatePayload_t iup;
    mc_FLE2InsertUpdatePayload_init(&iup);

    CHECK_AND_RETURN(mc_FLE2InsertUpdatePayload_parse(&iup, in, status));
    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, &iup.userKeyId));

    ret = true;
fail:
    mc_FLE2InsertUpdatePayload_cleanup(&iup);
    return ret;
}

static bool _collect_key_uuid_from_FLE2InsertUpdatePayloadV2(void *ctx,
                                                             const _mongocrypt_buffer_t *in,
                                                             mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    bool ret = false;

    mc_FLE2InsertUpdatePayloadV2_t iup;
    mc_FLE2InsertUpdatePayloadV2_init(&iup);

    CHECK_AND_RETURN(mc_FLE2InsertUpdatePayloadV2_parse(&iup, in, status));
    _mongocrypt_key_broker_t *kb = ctx;
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, &iup.userKeyId));

    ret = true;
fail:
    mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
    return ret;
}

static bool _collect_key_uuid_from_FLE1(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    _mongocrypt_ciphertext_t ciphertext;
    _mongocrypt_key_broker_t *kb = (_mongocrypt_key_broker_t *)ctx;

    CHECK_AND_RETURN(_mongocrypt_ciphertext_parse_unowned(in, &ciphertext, status));
    CHECK_AND_RETURN_KB_STATUS(_mongocrypt_key_broker_request_id(kb, &ciphertext.key_id));

    return true;
fail:
    return false;
}

static bool _collect_key_from_ciphertext(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT(in->data);

    switch (in->data[0]) {
    // FLE2v2 / Text Search
    case MC_SUBTYPE_FLE2IndexedTextEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2:
        return _collect_S_KeyID_from_FLE2IndexedEncryptedValueV2(ctx, in, status);
    case MC_SUBTYPE_FLE2UnindexedEncryptedValueV2:
        return _collect_key_uuid_from_FLE2UnindexedEncryptedValueV2(ctx, in, status);
    case MC_SUBTYPE_FLE2InsertUpdatePayloadV2: return _collect_key_uuid_from_FLE2InsertUpdatePayloadV2(ctx, in, status);

    // FLE2v1
    case MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue:
    case MC_SUBTYPE_FLE2IndexedRangeEncryptedValue:
        return _collect_S_KeyID_from_FLE2IndexedEncryptedValue(ctx, in, status);
    case MC_SUBTYPE_FLE2UnindexedEncryptedValue:
        return _collect_key_uuid_from_FLE2UnindexedEncryptedValue(ctx, in, status);
    case MC_SUBTYPE_FLE2InsertUpdatePayload: return _collect_key_uuid_from_FLE2InsertUpdatePayload(ctx, in, status);

    // FLE1
    default: return _collect_key_uuid_from_FLE1(ctx, in, status);
    }
}

static void _cleanup(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_decrypt_t *dctx;

    if (!ctx) {
        return;
    }
    dctx = (_mongocrypt_ctx_decrypt_t *)ctx;
    _mongocrypt_buffer_cleanup(&dctx->original_doc);
    _mongocrypt_buffer_cleanup(&dctx->decrypted_doc);
}

bool mongocrypt_ctx_explicit_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg) {
    bson_iter_t iter;
    bson_t as_bson;

    if (!ctx) {
        return false;
    }

    if (!msg || !msg->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid msg");
    }

    /* Expect msg to be the BSON a document of the form:
       { "v" : (BSON BINARY value of subtype 6) }
    */
    if (!_mongocrypt_binary_to_bson(msg, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
    }

    if (!bson_iter_init_find(&iter, &as_bson, "v")) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid msg, must contain 'v'");
    }

    if (!BSON_ITER_HOLDS_BINARY(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid msg, 'v' must contain a binary");
    }

    {
        bson_subtype_t subtype;
        const uint8_t *binary;
        uint32_t binary_len;
        mongocrypt_status_t *status = ctx->status;

        bson_iter_binary(&iter, &subtype, &binary_len, &binary);
        if (subtype != BSON_SUBTYPE_ENCRYPTED) {
            CLIENT_ERR("decryption expected BSON binary subtype %d, got %d", (int)BSON_SUBTYPE_ENCRYPTED, (int)subtype);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    if (!mongocrypt_ctx_decrypt_init(ctx, msg)) {
        return false;
    }
    return true;
}

static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    (void)_mongocrypt_key_broker_docs_done(&ctx->kb);
    if (!_check_for_K_KeyId(ctx)) {
        return false;
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

static bool _kms_done(mongocrypt_ctx_t *ctx) {
    _mongocrypt_opts_kms_providers_t *kms_providers;

    BSON_ASSERT_PARAM(ctx);

    kms_providers = _mongocrypt_ctx_kms_providers(ctx);

    if (!_mongocrypt_key_broker_kms_done(&ctx->kb, kms_providers)) {
        BSON_ASSERT(!_mongocrypt_key_broker_status(&ctx->kb, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }
    if (!_check_for_K_KeyId(ctx)) {
        return false;
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

bool mongocrypt_ctx_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *doc) {
    _mongocrypt_ctx_decrypt_t *dctx;
    bson_t as_bson;
    bson_iter_t iter = {0};
    _mongocrypt_ctx_opts_spec_t opts_spec;

    memset(&opts_spec, 0, sizeof(opts_spec));
    if (!ctx) {
        return false;
    }

    if (!_mongocrypt_ctx_init(ctx, &opts_spec)) {
        return false;
    }

    if (!doc || !doc->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid doc");
    }

    dctx = (_mongocrypt_ctx_decrypt_t *)ctx;
    ctx->type = _MONGOCRYPT_TYPE_DECRYPT;
    ctx->vtable.finalize = _finalize;
    ctx->vtable.cleanup = _cleanup;
    ctx->vtable.mongo_done_keys = _mongo_done_keys;
    ctx->vtable.kms_done = _kms_done;

    _mongocrypt_buffer_copy_from_binary(&dctx->original_doc, doc);
    /* get keys. */
    if (!_mongocrypt_buffer_to_bson(&dctx->original_doc, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
    }

    bson_iter_init(&iter, &as_bson);
    if (!_mongocrypt_traverse_binary_in_bson(_collect_key_from_ciphertext,
                                             &ctx->kb,
                                             TRAVERSE_MATCH_CIPHERTEXT,
                                             &iter,
                                             ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    (void)_mongocrypt_key_broker_requests_done(&ctx->kb);

    if (!_check_for_K_KeyId(ctx)) {
        return false;
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}
libmongocrypt-1.19.0/src/mongocrypt-ctx-encrypt.c000066400000000000000000003356751521103432300221210ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mc-efc-private.h"
#include "mc-fle-blob-subtype-private.h"
#include "mc-fle2-rfds-private.h"
#include "mc-schema-broker-private.h"
#include "mc-tokens-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-marking-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-traverse-util-private.h"
#include "mongocrypt-util-private.h" // mc_iter_document_as_bson
#include "mongocrypt.h"
#include 

/* Construct the list collections command to send. */
static bool _mongo_op_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    bson_t filter = BSON_INITIALIZER;
    if (!mc_schema_broker_append_listCollections_filter(ectx->sb, &filter, ctx->status)) {
        _mongocrypt_ctx_fail(ctx);
        return false;
    }
    _mongocrypt_buffer_steal_from_bson(&ectx->list_collections_filter, &filter);
    out->data = ectx->list_collections_filter.data;
    out->len = ectx->list_collections_filter.len;
    return true;
}

/* get_command_name returns the name of a command. The command name is the first
 * field. For example, the command name of: {"find": "foo", "filter": {"bar":
 * 1}} is "find". */
static const char *get_command_name(_mongocrypt_buffer_t *cmd, mongocrypt_status_t *status) {
    bson_t cmd_bson;
    bson_iter_t iter;
    const char *cmd_name;

    BSON_ASSERT_PARAM(cmd);

    if (!_mongocrypt_buffer_to_bson(cmd, &cmd_bson)) {
        CLIENT_ERR("unable to convert command buffer to BSON");
        return NULL;
    }

    if (!bson_iter_init(&iter, &cmd_bson)) {
        CLIENT_ERR("unable to iterate over command BSON");
        return NULL;
    }

    /* The command name is the first key. */
    if (!bson_iter_next(&iter)) {
        CLIENT_ERR("unexpected empty BSON for command");
        return NULL;
    }

    cmd_name = bson_iter_key(&iter);
    if (!cmd_name) {
        CLIENT_ERR("unable to get command name from BSON");
        return NULL;
    }
    return cmd_name;
}

/* context_uses_fle2 returns true if the context uses FLE 2 behavior.
 * If a collection has an encryptedFields document, it uses FLE 2.
 */
static bool context_uses_fle2(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    return mc_schema_broker_has_any_qe_schemas(ectx->sb);
}

/* _fle2_collect_keys_for_compaction requests keys required to produce
 * compactionTokens or cleanupTokens.
 * compactionTokens and cleanupTokens are only applicable to FLE 2. */
static bool _fle2_collect_keys_for_compaction(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    /* compactionTokens are only appended for FLE 2. */
    if (!context_uses_fle2(ctx)) {
        return true;
    }

    const char *cmd_name = ectx->cmd_name;

    if (0 != strcmp(cmd_name, "compactStructuredEncryptionData")
        && 0 != strcmp(cmd_name, "cleanupStructuredEncryptionData")) {
        return true;
    }

    /* (compact/cleanup)StructuredEncryptionData must not be sent to mongocryptd. */
    ectx->bypass_query_analysis = true;

    const mc_EncryptedFieldConfig_t *efc =
        mc_schema_broker_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status);
    if (!efc) {
        return _mongocrypt_ctx_fail(ctx);
    }

    for (const mc_EncryptedField_t *field = efc->fields; field != NULL; field = field->next) {
        if (field->keyAltName) {
            bson_value_t keyAltName;
            _bson_value_from_string(field->keyAltName, &keyAltName);
            if (!_mongocrypt_key_broker_request_name(&ctx->kb, &keyAltName)) {
                _mongocrypt_key_broker_status(&ctx->kb, ctx->status);
                _mongocrypt_ctx_fail(ctx);
                bson_value_destroy(&keyAltName);
                return false;
            }
            bson_value_destroy(&keyAltName);
        } else if (!_mongocrypt_key_broker_request_id(&ctx->kb, &field->keyId)) {
            _mongocrypt_key_broker_status(&ctx->kb, ctx->status);
            _mongocrypt_ctx_fail(ctx);
            return false;
        }
    }

    return true;
}

// Return value: 1 = keys needed, 0 = no keys needed, -1 = error
static int _fle2_collect_keys_for_encrypted_fields(mongocrypt_ctx_t *ctx) {
    int need_keys = 0;
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    BSON_ASSERT_PARAM(ctx);

    const mc_EncryptedFieldConfig_t *efc =
        mc_schema_broker_maybe_get_encryptedFields(ectx->sb, ectx->target_coll, ctx->status);
    if (!efc) {
        return 0;
    }

    for (const mc_EncryptedField_t *field = efc->fields; field != NULL; field = field->next) {
        if (!field->keyAltName) {
            continue;
        }
        need_keys = 1;
        bson_value_t keyAltName;
        _bson_value_from_string(field->keyAltName, &keyAltName);
        if (!_mongocrypt_key_broker_request_name(&ctx->kb, &keyAltName)) {
            _mongocrypt_key_broker_status(&ctx->kb, ctx->status);
            _mongocrypt_ctx_fail(ctx);
            bson_value_destroy(&keyAltName);
            return -1;
        }
        bson_value_destroy(&keyAltName);
    }

    return need_keys;
}

static bool _mongo_feed_collinfo(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
    bson_t as_bson;

    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    if (!bson_init_static(&as_bson, in->data, in->len)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "BSON malformed");
    }

    if (!mc_schema_broker_satisfy_from_collinfo(ectx->sb, &as_bson, &ctx->crypt->cache_collinfo, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    return true;
}

static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx);

static bool _mongo_done_collinfo(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    // If there are collections still needing schemas, assume no schema exists.
    if (!mc_schema_broker_satisfy_remaining_with_empty_schemas(ectx->sb, &ctx->crypt->cache_collinfo, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (!_fle2_collect_keys_for_compaction(ctx)) {
        return false;
    }

    if (ectx->bypass_query_analysis) {
        /* Keys may have been requested for compactionTokens.
         * Finish key requests. */
        _mongocrypt_key_broker_requests_done(&ctx->kb);
        return _mongocrypt_ctx_state_from_key_broker(ctx);
    }
    ectx->parent.state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    return _try_run_csfle_marking(ctx);
}

static const char *_mongo_db_collinfo(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    if (!ectx->target_db) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Expected target database for `listCollections`, but none exists.");
        return NULL;
    }
    return ectx->target_db;
}

/**
 * @brief Create the server-side command that contains information for
 * generating encryption markings via query analysis.
 *
 * @param ctx The encryption context.
 * @param out The destination of the generated BSON document
 * @return true On success
 * @return false Otherwise. Sets a failing status message in this case.
 */
static bool _create_markings_cmd_bson(mongocrypt_ctx_t *ctx, bson_t *out) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    bson_t bson_view = BSON_INITIALIZER;
    if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &bson_view)) {
        _mongocrypt_ctx_fail_w_msg(ctx, "invalid BSON cmd");
        return false;
    }
    // If input command included $db, do not include it in the command to
    // mongocryptd. Drivers are expected to append $db in the RunCommand helper
    // used to send the command.
    bson_copy_to_excluding_noinit(&bson_view, out, "$db", NULL);
    if (!mc_schema_broker_add_schemas_to_cmd(ectx->sb,
                                             &ctx->kb,
                                             out,
                                             ctx->crypt->csfle.okay ? MC_CMD_SCHEMAS_FOR_CRYPT_SHARED
                                                                    : MC_CMD_SCHEMAS_FOR_MONGOCRYPTD,
                                             ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (0 == strcmp(ectx->cmd_name, "create")) {
        // Translate keyAltName to keyId in encryptedFields
        bson_iter_t iter;
        if (bson_iter_init_find(&iter, out, "encryptedFields") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
            uint32_t ef_len = 0;
            const uint8_t *ef_data = NULL;
            bson_iter_document(&iter, &ef_len, &ef_data);
            bson_t ef_bson;
            bson_init_static(&ef_bson, ef_data, ef_len);

            bson_iter_t ef_iter;
            if (bson_iter_init_find(&ef_iter, &ef_bson, "fields") && BSON_ITER_HOLDS_ARRAY(&ef_iter)) {
                uint32_t fields_len = 0;
                const uint8_t *fields_data = NULL;
                bson_iter_array(&ef_iter, &fields_len, &fields_data);
                bson_t fields_bson;
                bson_init_static(&fields_bson, fields_data, fields_len);

                // Build a new encryptedFields document with keyAltName replaced by keyId
                bson_t new_ef;
                bson_init(&new_ef);
                // Copy all fields from encryptedFields except "fields"
                bson_copy_to_excluding_noinit(&ef_bson, &new_ef, "fields", NULL);

                // Process the fields array using the shared helper function
                bson_t new_fields;
                BSON_APPEND_ARRAY_UNSAFE_BEGIN(&new_ef, "fields", &new_fields);

                const int translated_keyAltName =
                    mc_translate_fields_keyAltName_to_keyId(&fields_bson, &ctx->kb, &new_fields, ctx->status);
                if (translated_keyAltName == -1) {
                    bson_destroy(&new_fields);
                    bson_destroy(&new_ef);
                    return _mongocrypt_ctx_fail(ctx);
                }

                bson_append_array_end(&new_ef, &new_fields);

                // Replace encryptedFields in out if we translated a keyAltName
                // Done conditionally to avoid reordering encryptedFields and encryptionInformation in existing tests
                if (translated_keyAltName == 1) {
                    bson_t temp_out;
                    bson_init(&temp_out);
                    bson_copy_to_excluding_noinit(out, &temp_out, "encryptedFields", NULL);
                    bson_append_document(&temp_out, "encryptedFields", -1, &new_ef);

                    // Replace out with temp_out
                    bson_destroy(out);
                    bson_steal(out, &temp_out);
                }

                bson_destroy(&new_ef);
            }
        }
    }

    return true;
}

static bool _mongo_op_markings(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    if (ectx->ismaster.needed) {
        if (_mongocrypt_buffer_empty(&ectx->ismaster.cmd)) {
            bson_t ismaster_cmd = BSON_INITIALIZER;
            // Store the generated command:
            BSON_APPEND_INT32(&ismaster_cmd, "isMaster", 1);
            _mongocrypt_buffer_steal_from_bson(&ectx->ismaster.cmd, &ismaster_cmd);
        }

        out->data = ectx->ismaster.cmd.data;
        out->len = ectx->ismaster.cmd.len;
        return true;
    }

    if (_mongocrypt_buffer_empty(&ectx->mongocryptd_cmd)) {
        // We need to generate the command document
        bson_t cmd_bson = BSON_INITIALIZER;
        if (!_create_markings_cmd_bson(ctx, &cmd_bson)) {
            // Failed
            bson_destroy(&cmd_bson);
            return false;
        }
        // Store the generated command:
        _mongocrypt_buffer_steal_from_bson(&ectx->mongocryptd_cmd, &cmd_bson);
    }

    // If we reach here, we have a valid mongocrypt_cmd
    out->data = ectx->mongocryptd_cmd.data;
    out->len = ectx->mongocryptd_cmd.len;
    return true;
}

static bool _collect_key_from_marking(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    _mongocrypt_marking_t marking;
    _mongocrypt_key_broker_t *kb;
    bool res;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    kb = (_mongocrypt_key_broker_t *)ctx;

    if (!_mongocrypt_marking_parse_unowned(in, &marking, status)) {
        _mongocrypt_marking_cleanup(&marking);
        return false;
    }

    if (marking.type == MONGOCRYPT_MARKING_FLE1_BY_ID) {
        res = _mongocrypt_key_broker_request_id(kb, &marking.u.fle1.key_id);
    } else if (marking.type == MONGOCRYPT_MARKING_FLE1_BY_ALTNAME) {
        res = _mongocrypt_key_broker_request_name(kb, &marking.u.fle1.key_alt_name);
    } else {
        BSON_ASSERT(marking.type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
        res = _mongocrypt_key_broker_request_id(kb, &marking.u.fle2.index_key_id)
           && _mongocrypt_key_broker_request_id(kb, &marking.u.fle2.user_key_id);
    }

    if (!res) {
        _mongocrypt_key_broker_status(kb, status);
        _mongocrypt_marking_cleanup(&marking);
        return false;
    }

    _mongocrypt_marking_cleanup(&marking);

    return true;
}

static bool _mongo_feed_markings(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
    /* Find keys. */
    bson_t as_bson;
    bson_iter_t iter = {0};
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    if (!_mongocrypt_binary_to_bson(in, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed BSON");
    }

    if (ectx->ismaster.needed) {
        /* This is a response to the 'isMaster' command. */
        if (!bson_iter_init_find(&iter, &as_bson, "maxWireVersion")) {
            return _mongocrypt_ctx_fail_w_msg(ctx,
                                              "expected to find 'maxWireVersion' in isMaster response, but did "
                                              "not.");
        }
        if (!BSON_ITER_HOLDS_INT32(&iter)) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "expected 'maxWireVersion' to be int32.");
        }
        ectx->ismaster.maxwireversion = bson_iter_int32(&iter);
        return true;
    }

    if (bson_iter_init_find(&iter, &as_bson, "schemaRequiresEncryption") && !bson_iter_as_bool(&iter)) {
        /* TODO: update cache: this schema does not require encryption. */
        // Schema does not require encryption. Skip copying the `result`.
        return true;
    }

    if (bson_iter_init_find(&iter, &as_bson, "hasEncryptedPlaceholders") && !bson_iter_as_bool(&iter)) {
        return true;
    }

    if (!bson_iter_init_find(&iter, &as_bson, "result")) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed marking, no 'result'");
    }

    if (!_mongocrypt_buffer_copy_from_document_iter(&ectx->marked_cmd, &iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed marking, 'result' must be a document");
    }

    if (!bson_iter_recurse(&iter, &iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed marking, could not recurse into 'result'");
    }
    if (!_mongocrypt_traverse_binary_in_bson(_collect_key_from_marking,
                                             (void *)&ctx->kb,
                                             TRAVERSE_MATCH_MARKING,
                                             &iter,
                                             ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    return true;
}

static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx);

static bool _mongo_done_markings(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    if (ectx->ismaster.needed) {
        return mongocrypt_ctx_encrypt_ismaster_done(ctx);
    }
    (void)_mongocrypt_key_broker_requests_done(&ctx->kb);
    // We can get here without going through NEED_MONGO_KEYS if the key is cached
    if (ectx->need_keys_for_encryptedFields) {
        ectx->need_keys_for_encryptedFields = false;
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

// needs_markings returns true if the operation needs markings from mongocryptd or crypt_shared.
static bool needs_markings(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    BSON_ASSERT(ectx->cmd_name);
    if ((0 == strcmp(ectx->cmd_name, "compactStructuredEncryptionData")
         || 0 == strcmp(ectx->cmd_name, "cleanupStructuredEncryptionData"))) {
        return false;
    }

    if (ctx->crypt->opts.bypass_query_analysis) {
        return false;
    }

    return true;
}

static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    (void)_mongocrypt_key_broker_docs_done(&ctx->kb);

    if (ctx->kb.state == KB_DONE) {
        // Requested keys are complete and do not require KMS.

        const bool translated_keyAltNames = ectx->need_keys_for_encryptedFields;
        ectx->need_keys_for_encryptedFields = false;

        if (translated_keyAltNames && needs_markings(ctx)) {
            // keyAltName translation is done.
            // Restart key broker to request additional keys for encrypting values:
            _mongocrypt_key_broker_restart(&ctx->kb);
            ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
            return _try_run_csfle_marking(ctx);
        }
    }

    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

static bool _kms_done(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    _mongocrypt_opts_kms_providers_t *kms_providers;

    BSON_ASSERT_PARAM(ctx);

    kms_providers = _mongocrypt_ctx_kms_providers(ctx);

    if (!_mongocrypt_key_broker_kms_done(&ctx->kb, kms_providers)) {
        BSON_ASSERT(!_mongocrypt_key_broker_status(&ctx->kb, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }

    if (ctx->kb.state == KB_DONE) {
        // Requested keys are complete and do not require KMS.

        const bool translated_keyAltNames = ectx->need_keys_for_encryptedFields;
        ectx->need_keys_for_encryptedFields = false;

        if (translated_keyAltNames && needs_markings(ctx)) {
            // keyAltName translation is done.
            // Restart key broker to request additional keys for encrypting values:
            _mongocrypt_key_broker_restart(&ctx->kb);
            ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
            return _try_run_csfle_marking(ctx);
        }
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

/**
 * @brief Append $db to a command being passed to csfle.
 */
static bool _add_dollar_db(const char *cmd_name, bson_t *cmd, const char *cmd_db, mongocrypt_status_t *status) {
    bson_t out = BSON_INITIALIZER;
    bson_t explain = BSON_INITIALIZER;
    bson_iter_t iter;
    bool ok = false;

    BSON_ASSERT_PARAM(cmd_name);
    BSON_ASSERT_PARAM(cmd);
    BSON_ASSERT_PARAM(cmd_db);

    if (!bson_iter_init_find(&iter, cmd, "$db")) {
        if (!BSON_APPEND_UTF8(cmd, "$db", cmd_db)) {
            CLIENT_ERR("failed to append '$db'");
            goto fail;
        }
    }

    if (0 != strcmp(cmd_name, "explain")) {
        goto success;
    }

    // The "explain" command for csfle is a special case.
    // csfle expects "$db" to be nested in the "explain" document and match the
    // top-level "$db". Example:
    // {
    //    "explain": {
    //       "find": "to-csfle"
    //       "$db": "db"
    //    }
    //    "$db": "db"
    // }
    BSON_ASSERT(bson_iter_init_find(&iter, cmd, "explain"));
    if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
        CLIENT_ERR("expected 'explain' to be document");
        goto fail;
    }

    {
        bson_t tmp;
        if (!mc_iter_document_as_bson(&iter, &tmp, status)) {
            goto fail;
        }
        bson_copy_to(&tmp, &explain);
    }

    if (!BSON_APPEND_UTF8(&explain, "$db", cmd_db)) {
        CLIENT_ERR("failed to append '$db'");
        goto fail;
    }

    if (!BSON_APPEND_DOCUMENT(&out, "explain", &explain)) {
        CLIENT_ERR("unable to append 'explain' document");
        goto fail;
    }

    bson_copy_to_excluding_noinit(cmd, &out, "explain", NULL);
    bson_destroy(cmd);
    if (!bson_steal(cmd, &out)) {
        CLIENT_ERR("failed to steal BSON without encryptionInformation");
        goto fail;
    }

success:
    ok = true;
fail:
    bson_destroy(&explain);
    if (!ok) {
        bson_destroy(&out);
    }
    return ok;
}

/**
 * @brief Attempt to generate csfle markings using a csfle dynamic library.
 *
 * @param ctx A context which has state NEED_MONGO_MARKINGS
 * @return true On success
 * @return false On error.
 *
 * This should be called only when we are ready for markings in the command
 * document. This function will only do anything if the csfle dynamic library
 * is loaded, otherwise it returns success immediately and leaves the state
 * as NEED_MONGO_MARKINGS.
 *
 * If csfle is loaded, this function will request the csfle library generate a
 * marked command document based on the caller's schema. If successful, the
 * state will be changed via @ref _mongo_done_markings().
 *
 * The purpose of this function is to short-circuit the phase of encryption
 * wherein we would normally return to the driver and give them the opportunity
 * to generate the markings by passing a special command to a mongocryptd daemon
 * process. Instead, we'll do it ourselves here, if possible.
 */
static bool _try_run_csfle_marking(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS
                && "_try_run_csfle_marking() should only be called when mongocrypt is "
                   "ready for markings");

    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT(ctx->crypt);

    // We have a valid schema and just need to mark the fields for encryption
    if (!ctx->crypt->csfle.okay) {
        // We don't have a csfle library to use to obtain the markings. It's up to
        // caller to resolve them.
        return true;
    }

    _mongo_crypt_v1_vtable csfle = ctx->crypt->csfle;
    mongo_crypt_v1_lib *csfle_lib = ctx->crypt->csfle_lib;
    BSON_ASSERT(csfle_lib);
    bool okay = false;

    // Obtain the command for markings
    bson_t cmd = BSON_INITIALIZER;
    if (!_create_markings_cmd_bson(ctx, &cmd)) {
        goto fail_create_cmd;
    }

    const char *cmd_name = ectx->cmd_name;

    if (!_add_dollar_db(cmd_name, &cmd, ectx->cmd_db, ctx->status)) {
        _mongocrypt_ctx_fail(ctx);
        goto fail_create_cmd;
    }

    const char *version_str = ctx->crypt->csfle.get_version_str();
    // Examples:
    // mongo_crypt_v1-dev-8.0.0
    // mongo_crypt_v1-dev-8.1.0-alpha3-245-ge1c2344
    // Strip leading prefix.
    if (strstr(version_str, "mongo_crypt_v1-dev-") == version_str) {
        version_str += strlen("mongo_crypt_v1-dev-");
    }

#define CHECK_CSFLE_ERROR(Func, FailLabel)                                                                             \
    if (1) {                                                                                                           \
        if (csfle.status_get_error(status)) {                                                                          \
            _mongocrypt_set_error(ctx->status,                                                                         \
                                  MONGOCRYPT_STATUS_ERROR_CRYPT_SHARED,                                                \
                                  MONGOCRYPT_GENERIC_ERROR_CODE,                                                       \
                                  "[crypt_shared %s] " #Func " failed: %s [Error %d, code %d]",                        \
                                  version_str,                                                                         \
                                  csfle.status_get_explanation(status),                                                \
                                  csfle.status_get_error(status),                                                      \
                                  csfle.status_get_code(status));                                                      \
            _mongocrypt_ctx_fail(ctx);                                                                                 \
            goto FailLabel;                                                                                            \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

    mongo_crypt_v1_status *status = csfle.status_create();
    BSON_ASSERT(status);

    mongo_crypt_v1_query_analyzer *qa = csfle.query_analyzer_create(csfle_lib, status);
    CHECK_CSFLE_ERROR("query_analyzer_create", fail_qa_create);

    uint32_t marked_bson_len = 0;
    uint8_t *marked_bson = csfle.analyze_query(qa,
                                               bson_get_data(&cmd),
                                               ectx->target_ns,
                                               (uint32_t)strlen(ectx->target_ns),
                                               &marked_bson_len,
                                               status);
    CHECK_CSFLE_ERROR("analyze_query", fail_analyze_query);

    // Copy out the marked document.
    mongocrypt_binary_t *marked = mongocrypt_binary_new_from_data(marked_bson, marked_bson_len);
    if (!_mongo_feed_markings(ctx, marked)) {
        // Wrap error with additional information.
        _mongocrypt_set_error(ctx->status,
                              MONGOCRYPT_STATUS_ERROR_CLIENT,
                              MONGOCRYPT_GENERIC_ERROR_CODE,
                              "Consuming the generated csfle markings failed: %s",
                              mongocrypt_status_message(ctx->status, NULL /* len */));
        goto fail_feed_markings;
    }

    okay = _mongo_done_markings(ctx);
    if (!okay) {
        // Wrap error with additional information.
        _mongocrypt_set_error(ctx->status,
                              MONGOCRYPT_STATUS_ERROR_CLIENT,
                              MONGOCRYPT_GENERIC_ERROR_CODE,
                              "Finalizing the generated csfle markings failed: %s",
                              mongocrypt_status_message(ctx->status, NULL /* len */));
    }

fail_feed_markings:
    mongocrypt_binary_destroy(marked);
    csfle.bson_free(marked_bson);
fail_analyze_query:
    csfle.query_analyzer_destroy(qa);
fail_qa_create:
    csfle.status_destroy(status);
fail_create_cmd:
    bson_destroy(&cmd);
    return okay;
}

static bool _mongocrypt_fle2_insert_update_find(mc_fle_blob_subtype_t subtype) {
    return (subtype == MC_SUBTYPE_FLE2InsertUpdatePayload) || (subtype == MC_SUBTYPE_FLE2InsertUpdatePayloadV2)
        || (subtype == MC_SUBTYPE_FLE2FindEqualityPayload) || (subtype == MC_SUBTYPE_FLE2FindEqualityPayloadV2)
        || (subtype == MC_SUBTYPE_FLE2FindRangePayload) || (subtype == MC_SUBTYPE_FLE2FindRangePayloadV2)
        || (subtype == MC_SUBTYPE_FLE2FindTextPayload);
}

static bool
_marking_to_bson_value(void *ctx, _mongocrypt_marking_t *marking, bson_value_t *out, mongocrypt_status_t *status) {
    _mongocrypt_ciphertext_t ciphertext;
    _mongocrypt_buffer_t serialized_ciphertext = {0};
    bool ret = false;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_ciphertext_init(&ciphertext);

    if (!_mongocrypt_marking_to_ciphertext(ctx, marking, &ciphertext, status)) {
        goto fail;
    }

    if (_mongocrypt_fle2_insert_update_find(ciphertext.blob_subtype)) {
        /* ciphertext_data is already a BSON object, just need to prepend
         * blob_subtype */
        if (ciphertext.data.len > UINT32_MAX - 1u) {
            CLIENT_ERR("ciphertext too long");
            goto fail;
        }
        _mongocrypt_buffer_init_size(&serialized_ciphertext, ciphertext.data.len + 1);
        /* ciphertext->blob_subtype is an enum and easily fits in uint8_t */
        serialized_ciphertext.data[0] = (uint8_t)ciphertext.blob_subtype;
        memcpy(serialized_ciphertext.data + 1, ciphertext.data.data, ciphertext.data.len);

    } else if (!_mongocrypt_serialize_ciphertext(&ciphertext, &serialized_ciphertext)) {
        CLIENT_ERR("malformed ciphertext");
        goto fail;
    }

    /* ownership of serialized_ciphertext is transferred to caller. */
    out->value_type = BSON_TYPE_BINARY;
    out->value.v_binary.data = serialized_ciphertext.data;
    out->value.v_binary.data_len = serialized_ciphertext.len;
    out->value.v_binary.subtype = (bson_subtype_t)BSON_SUBTYPE_ENCRYPTED;

    ret = true;

fail:
    _mongocrypt_ciphertext_cleanup(&ciphertext);
    return ret;
}

static bool
_replace_marking_with_ciphertext(void *ctx, _mongocrypt_buffer_t *in, bson_value_t *out, mongocrypt_status_t *status) {
    _mongocrypt_marking_t marking = {0};
    bool ret;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    if (!_mongocrypt_marking_parse_unowned(in, &marking, status)) {
        _mongocrypt_marking_cleanup(&marking);
        return false;
    }

    ret = _marking_to_bson_value(ctx, &marking, out, status);
    _mongocrypt_marking_cleanup(&marking);
    return ret;
}

static bool
_check_for_payload_requiring_encryptionInformation(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    bool *out = (bool *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    if (in->len < 1) {
        CLIENT_ERR("unexpected empty FLE payload");
        return false;
    }

    mc_fle_blob_subtype_t subtype = (mc_fle_blob_subtype_t)in->data[0];
    if (_mongocrypt_fle2_insert_update_find(subtype)) {
        *out = true;
        return true;
    }

    return true;
}

typedef struct {
    bool must_omit;
    bool ok;
} moe_result;

// must_omit_encryptionInformation returns true if the command
// must omit the "encryptionInformation" field when sent to mongod / mongos.
static moe_result must_omit_encryptionInformation(const char *command_name,
                                                  const bson_t *command,
                                                  const mc_EncryptedFieldConfig_t *efc,
                                                  mongocrypt_status_t *status) {
    // eligible_commands may omit encryptionInformation if the command does not
    // contain payloads requiring encryption.
    const char *eligible_commands[] = {"find", "aggregate", "distinct", "count", "insert"};
    size_t i;
    bool found = false;

    // prohibited_commands prohibit encryptionInformation on mongod / mongos.
    const char *prohibited_commands[] = {"cleanupStructuredEncryptionData", "create", "collMod", "createIndexes"};

    BSON_ASSERT_PARAM(command_name);
    BSON_ASSERT_PARAM(command);

    if (0 == strcmp("compactStructuredEncryptionData", command_name)) {
        if (!efc) {
            CLIENT_ERR("expected to have encryptedFields for compactStructuredEncryptionData command but have none");
            return (moe_result){.ok = false};
        }
        // `compactStructuredEncryptionData` is a special case:
        // - Server 7.0 prohibits `encryptionInformation`.
        // - Server 8.0 requires `encryptionInformation` if "range" fields are referenced. Otherwise ignores.
        // - Server 8.2 requires `encryptionInformation` if any range or text-search fields are referenced. Otherwise
        // ignores.
        // Only send `encryptionInformation` if range or text-search fields are present to support all server
        // versions.
        bool has_fields_requiring_ei = false;
        for (const mc_EncryptedField_t *ef = efc->fields; ef != NULL; ef = ef->next) {
            if (ef->supported_queries
                & (SUPPORTS_RANGE_QUERIES | SUPPORTS_SUBSTRING_PREVIEW_QUERIES | SUPPORTS_SUFFIX_QUERIES
                   | SUPPORTS_PREFIX_QUERIES)) {
                has_fields_requiring_ei = true;
                break;
            }
        }
        return (moe_result){.ok = true, .must_omit = !has_fields_requiring_ei};
    }

    for (i = 0; i < sizeof(prohibited_commands) / sizeof(prohibited_commands[0]); i++) {
        if (0 == strcmp(prohibited_commands[i], command_name)) {
            return (moe_result){.ok = true, .must_omit = true};
        }
    }

    for (i = 0; i < sizeof(eligible_commands) / sizeof(eligible_commands[0]); i++) {
        if (0 == strcmp(eligible_commands[i], command_name)) {
            found = true;
            break;
        }
    }
    if (!found) {
        return (moe_result){.ok = true};
    }

    bool has_payload_requiring_encryptionInformation = false;
    bson_iter_t iter = {0};
    if (!bson_iter_init(&iter, command)) {
        CLIENT_ERR("unable to iterate command");
        return (moe_result){.ok = false};
    }
    if (!_mongocrypt_traverse_binary_in_bson(_check_for_payload_requiring_encryptionInformation,
                                             &has_payload_requiring_encryptionInformation,
                                             TRAVERSE_MATCH_SUBTYPE6,
                                             &iter,
                                             status)) {
        return (moe_result){.ok = false};
    }

    if (!has_payload_requiring_encryptionInformation) {
        return (moe_result){.ok = true, .must_omit = true};
    }
    return (moe_result){.ok = true, .must_omit = false};
}

/* _fle2_append_compactionTokens appends compactionTokens if command_name is
 * "compactStructuredEncryptionData" or cleanupTokens if command_name is
 * "cleanupStructuredEncryptionData"
 */
static bool _fle2_append_compactionTokens(mongocrypt_t *crypt,
                                          _mongocrypt_key_broker_t *kb,
                                          const mc_EncryptedFieldConfig_t *efc,
                                          const char *command_name,
                                          bson_t *out,
                                          mongocrypt_status_t *status) {
    bson_t result_compactionTokens = BSON_INITIALIZER;
    bool ret = false;

    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(command_name);
    BSON_ASSERT_PARAM(out);
    _mongocrypt_crypto_t *crypto = crypt->crypto;

    bool cleanup = (0 == strcmp(command_name, "cleanupStructuredEncryptionData"));

    if (0 != strcmp(command_name, "compactStructuredEncryptionData") && !cleanup) {
        return true;
    }

    if (!efc) {
        CLIENT_ERR("expected to have encryptedFields for %s command but have none", command_name);
        return false;
    }

    if (cleanup) {
        BSON_APPEND_DOCUMENT_BEGIN(out, "cleanupTokens", &result_compactionTokens);
    } else {
        BSON_APPEND_DOCUMENT_BEGIN(out, "compactionTokens", &result_compactionTokens);
    }

    const mc_EncryptedField_t *ptr;
    for (ptr = efc->fields; ptr != NULL; ptr = ptr->next) {
        /* Append tokens. */
        _mongocrypt_buffer_t key = {0};
        _mongocrypt_buffer_t tokenkey = {0};
        mc_CollectionsLevel1Token_t *cl1t = NULL;
        mc_ECOCToken_t *ecoct = NULL;
        mc_ESCToken_t *esct = NULL;
        mc_AnchorPaddingTokenRoot_t *padt = NULL;
        bool ecoc_ok = false;

        if (!_mongocrypt_key_broker_decrypted_key_by_id(kb, &ptr->keyId, &key)) {
            _mongocrypt_key_broker_status(kb, status);
            goto ecoc_fail;
        }
        /* The last 32 bytes of the user key are the token key. */
        if (key.len < MONGOCRYPT_TOKEN_KEY_LEN) {
            CLIENT_ERR("key too short");
            goto ecoc_fail;
        }
        if (!_mongocrypt_buffer_from_subrange(&tokenkey,
                                              &key,
                                              key.len - MONGOCRYPT_TOKEN_KEY_LEN,
                                              MONGOCRYPT_TOKEN_KEY_LEN)) {
            CLIENT_ERR("unable to get TokenKey from Data Encryption Key");
            goto ecoc_fail;
        }
        cl1t = mc_CollectionsLevel1Token_new(crypto, &tokenkey, status);
        if (!cl1t) {
            goto ecoc_fail;
        }

        ecoct = mc_ECOCToken_new(crypto, cl1t, status);
        if (!ecoct) {
            goto ecoc_fail;
        }

        const _mongocrypt_buffer_t *ecoct_buf = mc_ECOCToken_get(ecoct);

        if (ptr->supported_queries
            & (SUPPORTS_RANGE_QUERIES | SUPPORTS_SUBSTRING_PREVIEW_QUERIES | SUPPORTS_SUFFIX_QUERIES
               | SUPPORTS_PREFIX_QUERIES)) {
            // Append the document {ecoc: , anchorPaddingToken: }
            esct = mc_ESCToken_new(crypto, cl1t, status);
            if (!esct) {
                goto ecoc_fail;
            }
            padt = mc_AnchorPaddingTokenRoot_new(crypto, esct, status);
            if (!padt) {
                goto ecoc_fail;
            }
            const _mongocrypt_buffer_t *padt_buf = mc_AnchorPaddingTokenRoot_get(padt);
            bson_t tokenDoc;
            BSON_APPEND_DOCUMENT_BEGIN(&result_compactionTokens, ptr->path, &tokenDoc);
            BSON_APPEND_BINARY(&tokenDoc, "ecoc", BSON_SUBTYPE_BINARY, ecoct_buf->data, ecoct_buf->len);
            BSON_APPEND_BINARY(&tokenDoc, "anchorPaddingToken", BSON_SUBTYPE_BINARY, padt_buf->data, padt_buf->len);
            bson_append_document_end(&result_compactionTokens, &tokenDoc);
        } else {
            // Append just 
            BSON_APPEND_BINARY(&result_compactionTokens,
                               ptr->path,
                               BSON_SUBTYPE_BINARY,
                               ecoct_buf->data,
                               ecoct_buf->len);
        }

        ecoc_ok = true;
    ecoc_fail:
        mc_AnchorPaddingTokenRoot_destroy(padt);
        mc_ESCToken_destroy(esct);
        mc_ECOCToken_destroy(ecoct);
        mc_CollectionsLevel1Token_destroy(cl1t);
        _mongocrypt_buffer_cleanup(&key);
        if (!ecoc_ok) {
            goto fail;
        }
    }

    bson_append_document_end(out, &result_compactionTokens);

    ret = true;
fail:
    return ret;
}

/**
 * @brief Removes "encryptionInformation" from cmd.
 */
static bool
_fle2_strip_encryptionInformation(const char *cmd_name, bson_t *cmd /* in and out */, mongocrypt_status_t *status) {
    bson_t stripped = BSON_INITIALIZER;
    bool ok = false;

    BSON_ASSERT_PARAM(cmd_name);
    BSON_ASSERT_PARAM(cmd);

    if (0 != strcmp(cmd_name, "explain") && 0 != strcmp(cmd_name, "bulkWrite")) {
        bson_copy_to_excluding_noinit(cmd, &stripped, "encryptionInformation", NULL);
        goto success;
    }

    if (0 == strcmp(cmd_name, "bulkWrite")) {
        // Get the single `nsInfo` document from the input command.
        bson_t nsInfo; // Non-owning.
        {
            bson_iter_t nsInfo_iter;
            if (!bson_iter_init(&nsInfo_iter, cmd)) {
                CLIENT_ERR("failed to iterate command");
                goto fail;
            }
            if (!bson_iter_find_descendant(&nsInfo_iter, "nsInfo.0", &nsInfo_iter)) {
                CLIENT_ERR("expected one namespace in `bulkWrite`, but found zero.");
                goto fail;
            }
            if (bson_has_field(cmd, "nsInfo.1")) {
                CLIENT_ERR(
                    "expected one namespace in `bulkWrite`, but found more than one. Only one namespace is supported.");
                goto fail;
            }
            if (!mc_iter_document_as_bson(&nsInfo_iter, &nsInfo, status)) {
                goto fail;
            }
        }

        // Copy input and exclude `encryptionInformation` from `nsInfo`.
        {
            // Append everything from input except `nsInfo`.
            bson_copy_to_excluding_noinit(cmd, &stripped, "nsInfo", NULL);
            // Append `nsInfo` array.
            bson_t nsInfo_array;
            if (!BSON_APPEND_ARRAY_UNSAFE_BEGIN(&stripped, "nsInfo", &nsInfo_array)) {
                CLIENT_ERR("unable to begin appending 'nsInfo' array");
                goto fail;
            }
            bson_t nsInfo_array_0;
            if (!BSON_APPEND_DOCUMENT_BEGIN(&nsInfo_array, "0", &nsInfo_array_0)) {
                CLIENT_ERR("unable to append 'nsInfo.0' document");
                goto fail;
            }
            // Copy everything from input `nsInfo` and exclude `encryptionInformation`.
            bson_copy_to_excluding_noinit(&nsInfo, &nsInfo_array_0, "encryptionInformation", NULL);
            if (!bson_append_document_end(&nsInfo_array, &nsInfo_array_0)) {
                CLIENT_ERR("unable to end appending 'nsInfo' document in array");
            }
            if (!bson_append_array_end(&stripped, &nsInfo_array)) {
                CLIENT_ERR("unable to end appending 'nsInfo' array");
                goto fail;
            }
        }

        goto success;
    }

    // The 'explain' command is a special case.
    // 'encryptionInformation' is returned from mongocryptd and csfle nested
    // inside 'explain'. Example:
    // {
    //    "explain": {
    //       "find": "coll"
    //       "encryptionInformation": {}
    //    }
    // }
    bson_iter_t iter;
    bson_t explain;

    BSON_ASSERT(bson_iter_init_find(&iter, cmd, "explain"));
    if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
        CLIENT_ERR("expected 'explain' to be document");
        goto fail;
    }

    {
        bson_t tmp;
        if (!mc_iter_document_as_bson(&iter, &tmp, status)) {
            goto fail;
        }
        bson_init(&explain);
        bson_copy_to_excluding_noinit(&tmp, &explain, "encryptionInformation", NULL);
    }

    if (!BSON_APPEND_DOCUMENT(&stripped, "explain", &explain)) {
        bson_destroy(&explain);
        CLIENT_ERR("unable to append 'explain'");
        goto fail;
    }
    bson_destroy(&explain);
    bson_copy_to_excluding_noinit(cmd, &stripped, "explain", NULL);

success:
    bson_destroy(cmd);
    if (!bson_steal(cmd, &stripped)) {
        CLIENT_ERR("failed to steal BSON without encryptionInformation");
        goto fail;
    }
    ok = true;
fail:
    if (!ok) {
        bson_destroy(&stripped);
    }
    return ok;
}

/*
 * Checks the "encryptedFields.strEncodeVersion" field for "create" commands for validity, and sets it to the default if
 * it does not exist.
 */
static bool _fle2_fixup_encryptedFields_strEncodeVersion(const char *cmd_name,
                                                         bson_t *cmd /* in and out */,
                                                         const mc_EncryptedFieldConfig_t *efc,
                                                         mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(cmd_name);
    BSON_ASSERT_PARAM(cmd);

    if (0 == strcmp(cmd_name, "create")) {
        if (!efc) {
            CLIENT_ERR("expected to have encryptedFields for create command but have none");
            return false;
        }
        bson_iter_t ef_iter;
        if (!bson_iter_init_find(&ef_iter, cmd, "encryptedFields")) {
            // No encryptedFields, nothing to check or fix
            return true;
        }
        if (!BSON_ITER_HOLDS_DOCUMENT(&ef_iter)) {
            CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Expected encryptedFields to be type obj, got: %s",
                       mc_bson_type_to_string(bson_iter_type(&ef_iter)));
            return false;
        }
        bson_iter_t sev_iter;
        if (!bson_iter_recurse(&ef_iter, &sev_iter)) {
            CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to recurse bson_iter");
            return false;
        }
        if (!bson_iter_find(&sev_iter, "strEncodeVersion")) {
            if (efc->str_encode_version == 0) {
                // Unset StrEncodeVersion matches the EFC, nothing to fix.
                return true;
            }

            // No strEncodeVersion and the EFC has a nonzero strEncodeVersion, add it.
            // Initialize the new cmd object from the old one, excluding encryptedFields.
            bson_t fixed = BSON_INITIALIZER;
            bson_copy_to_excluding_noinit(cmd, &fixed, "encryptedFields", NULL);

            // Recurse the original encryptedFields and copy everything over.
            bson_iter_t copy_iter;
            if (!bson_iter_recurse(&ef_iter, ©_iter)) {
                CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to recurse bson_iter");
                goto fail;
            }
            bson_t fixed_ef;
            if (!BSON_APPEND_DOCUMENT_BEGIN(&fixed, "encryptedFields", &fixed_ef)) {
                CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to start appending encryptedFields");
                goto fail;
            }
            while (bson_iter_next(©_iter)) {
                if (!bson_append_iter(&fixed_ef, NULL, 0, ©_iter)) {
                    CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to copy element");
                    goto fail;
                }
            }

            // Add the EFC's strEncodeVersion to encryptedFields.
            if (!BSON_APPEND_INT32(&fixed_ef, "strEncodeVersion", efc->str_encode_version)) {
                CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to append strEncodeVersion");
                goto fail;
            }
            if (!bson_append_document_end(&fixed, &fixed_ef)) {
                CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to finish appending encryptedFields");
                goto fail;
            }

            bson_destroy(cmd);
            if (!bson_steal(cmd, &fixed)) {
                CLIENT_ERR("_fle2_fixup_encryptedFields_strEncodeVersion: Failed to steal BSON");
                goto fail;
            }
            return true;
        fail:
            bson_destroy(&fixed);
            return false;
        } else {
            // Check strEncodeVersion for match against EFC
            if (!BSON_ITER_HOLDS_INT32(&sev_iter)) {
                CLIENT_ERR("expected 'strEncodeVersion' to be type int32, got: %d", (int)bson_iter_type(&sev_iter));
                return false;
            }
            int32_t version = bson_iter_int32(&sev_iter);
            if (version != efc->str_encode_version) {
                CLIENT_ERR("'strEncodeVersion' of %d does not match efc->str_encode_version of %d",
                           version,
                           efc->str_encode_version);
                return false;
            }
        }
    }
    return true;
}

/* Process a call to mongocrypt_ctx_finalize when an encryptedFieldConfig is
 * associated with the command. */
static bool _fle2_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    bson_t converted;
    _mongocrypt_ctx_encrypt_t *ectx;
    bson_t original_cmd_bson;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT(context_uses_fle2(ctx));
    BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_READY);
    BSON_ASSERT(!ectx->explicit);

    if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &original_cmd_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson in original_cmd");
    }

    /* If marked_cmd buffer is empty, there are no markings to encrypt. */
    if (_mongocrypt_buffer_empty(&ectx->marked_cmd)) {
        /* Append 'encryptionInformation' to the original command. */
        bson_copy_to(&original_cmd_bson, &converted);
    } else {
        bson_t as_bson;
        bson_iter_t iter = {0};

        if (!_mongocrypt_buffer_to_bson(&ectx->marked_cmd, &as_bson)) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
        }

        bson_iter_init(&iter, &as_bson);
        bson_init(&converted);
        if (!_mongocrypt_transform_binary_in_bson(_replace_marking_with_ciphertext,
                                                  &ctx->kb,
                                                  TRAVERSE_MATCH_MARKING,
                                                  &iter,
                                                  &converted,
                                                  ctx->status)) {
            bson_destroy(&converted);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    const char *command_name = ectx->cmd_name;

    /* Remove the 'encryptionInformation' field. It is appended in the response
     * from mongocryptd or csfle. */
    if (!_fle2_strip_encryptionInformation(command_name, &converted, ctx->status)) {
        bson_destroy(&converted);
        return _mongocrypt_ctx_fail(ctx);
    }

    // Defer error handling for potentially missing encryptedFields to command-specific routines below.
    // For create/cleanupStructuredEncryptionData/compactStructuredEncryptionData, get encryptedFields for the
    // single target collection. For other commands, encryptedFields may not be on the target collection.
    const mc_EncryptedFieldConfig_t *target_efc =
        mc_schema_broker_get_encryptedFields(ectx->sb, ectx->target_coll, NULL);

    if (target_efc) {
        for (mc_EncryptedField_t *f = target_efc->fields; f != NULL; f = f->next) {
            if (f->keyId.data == NULL) {
                BSON_ASSERT(f->keyAltName);
                bson_value_t key_alt_name;
                _mongocrypt_buffer_t _unused = {0};
                _bson_value_from_string(f->keyAltName, &key_alt_name);
                BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_name(&ctx->kb, &key_alt_name, &_unused, &f->keyId));
                bson_value_destroy(&key_alt_name);
                _mongocrypt_buffer_cleanup(&_unused);
            }
        }
    }

    moe_result result = must_omit_encryptionInformation(command_name, &converted, target_efc, ctx->status);
    if (!result.ok) {
        bson_destroy(&converted);
        return _mongocrypt_ctx_fail(ctx);
    }

    /* If this is a create command, append the encryptedFields.strEncodeVersion field if it's necessary. If the field
     * already exists, check it against the EFC for correctness. */
    if (!_fle2_fixup_encryptedFields_strEncodeVersion(command_name, &converted, target_efc, ctx->status)) {
        bson_destroy(&converted);
        return _mongocrypt_ctx_fail(ctx);
    }

    /* Append a new 'encryptionInformation'. */
    if (!result.must_omit) {
        if (!mc_schema_broker_add_schemas_to_cmd(ectx->sb,
                                                 &ctx->kb,
                                                 &converted,
                                                 MC_CMD_SCHEMAS_FOR_SERVER,
                                                 ctx->status)) {
            bson_destroy(&converted);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    if (!_fle2_append_compactionTokens(ctx->crypt, &ctx->kb, target_efc, command_name, &converted, ctx->status)) {
        bson_destroy(&converted);
        return _mongocrypt_ctx_fail(ctx);
    }

    // If input command has $db, ensure output command has $db.
    bson_iter_t iter;
    if (bson_iter_init_find(&iter, &original_cmd_bson, "$db")) {
        if (!bson_iter_init_find(&iter, &converted, "$db")) {
            BSON_APPEND_UTF8(&converted, "$db", ectx->cmd_db);
        }
    }

    _mongocrypt_buffer_steal_from_bson(&ectx->encrypted_cmd, &converted);
    _mongocrypt_buffer_to_binary(&ectx->encrypted_cmd, out);
    ctx->state = MONGOCRYPT_CTX_DONE;

    return true;
}

static bool FLE2RangeFindDriverSpec_to_ciphertexts(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    bool ok = false;
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    bson_t with_placholders = BSON_INITIALIZER;
    bson_t with_ciphertexts = BSON_INITIALIZER;

    if (!ctx->opts.rangeopts.set) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Expected RangeOpts to be set for Range Find");
        goto fail;
    }
    if (!ctx->opts.contention_factor.set) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Expected Contention Factor to be set for Range Find");
        goto fail;
    }

    bson_t in_bson;
    if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &in_bson)) {
        _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert input to BSON");
        goto fail;
    }

    bson_t v_doc;
    // Parse 'v' document from input.
    {
        bson_iter_t v_iter;
        if (!bson_iter_init_find(&v_iter, &in_bson, "v")) {
            _mongocrypt_ctx_fail_w_msg(ctx, "invalid input BSON, must contain 'v'");
            goto fail;
        }
        if (!BSON_ITER_HOLDS_DOCUMENT(&v_iter)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "invalid input BSON, expected 'v' to be document");
            goto fail;
        }
        if (!mc_iter_document_as_bson(&v_iter, &v_doc, ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }
    }

    // Parse FLE2RangeFindDriverSpec.
    {
        mc_FLE2RangeFindDriverSpec_t rfds;

        if (!mc_FLE2RangeFindDriverSpec_parse(&rfds, &v_doc, ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }

        // Convert FLE2RangeFindDriverSpec into a document with placeholders.
        if (!mc_FLE2RangeFindDriverSpec_to_placeholders(
                &rfds,
                &ctx->opts.rangeopts.value,
                ctx->opts.contention_factor.value,
                &ctx->opts.key_id,
                _mongocrypt_buffer_empty(&ctx->opts.index_key_id) ? &ctx->opts.key_id : &ctx->opts.index_key_id,
                mc_getNextPayloadId(),
                &with_placholders,
                ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }
    }

    // Convert document with placeholders into document with ciphertexts.
    {
        bson_iter_t iter;
        if (!bson_iter_init(&iter, &with_placholders)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "unable to iterate into placeholder document");
            goto fail;
        }
        if (!_mongocrypt_transform_binary_in_bson(_replace_marking_with_ciphertext,
                                                  &ctx->kb,
                                                  TRAVERSE_MATCH_MARKING,
                                                  &iter,
                                                  &with_ciphertexts,
                                                  ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }
    }

    // Wrap result in the document: { 'v':  }.
    {
        /* v_wrapped is the BSON document { 'v':  }. */
        bson_t v_wrapped = BSON_INITIALIZER;
        if (!bson_append_document(&v_wrapped, MONGOCRYPT_STR_AND_LEN("v"), &with_ciphertexts)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "unable to append document to 'v'");
            goto fail;
        }
        _mongocrypt_buffer_steal_from_bson(&ectx->encrypted_cmd, &v_wrapped);
        _mongocrypt_buffer_to_binary(&ectx->encrypted_cmd, out);
        ctx->state = MONGOCRYPT_CTX_DONE;
    }

    ok = true;
fail:
    bson_destroy(&with_ciphertexts);
    bson_destroy(&with_placholders);
    return ok;
}

static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    bool ret = false;
    _mongocrypt_marking_t marking;
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    BSON_ASSERT(ctx->opts.index_type.set);

    if (ctx->opts.rangeopts.set && ctx->opts.query_type.set) {
        // RangeOpts with query type is a special case. The result contains two
        // ciphertext values of FLE2RangeFindSpec.
        return FLE2RangeFindDriverSpec_to_ciphertexts(ctx, out);
    }

    bson_t new_v = BSON_INITIALIZER;

    _mongocrypt_marking_init(&marking);
    marking.type = MONGOCRYPT_MARKING_FLE2_ENCRYPTION;
    if (ctx->opts.query_type.set) {
        switch (ctx->opts.query_type.value) {
        case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED:
            _mongocrypt_ctx_fail_w_msg(ctx, "Cannot use rangePreview query type with Range V2");
            goto fail;
        // fallthrough
        case MONGOCRYPT_QUERY_TYPE_SUFFIX:
        case MONGOCRYPT_QUERY_TYPE_PREFIX:
        case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW:
        case MONGOCRYPT_QUERY_TYPE_RANGE:
        case MONGOCRYPT_QUERY_TYPE_EQUALITY: marking.u.fle2.type = MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND; break;
        default: _mongocrypt_ctx_fail_w_msg(ctx, "Invalid value for EncryptOpts.queryType"); goto fail;
        }
    } else {
        marking.u.fle2.type = MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT;
    }

    switch (ctx->opts.index_type.value) {
    case MONGOCRYPT_INDEX_TYPE_EQUALITY: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_EQUALITY; break;
    case MONGOCRYPT_INDEX_TYPE_NONE: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED; break;
    case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED:
        _mongocrypt_ctx_fail_w_msg(ctx, "Cannot use rangePreview index type with Range V2");
        goto fail;
        // fallthrough
    case MONGOCRYPT_INDEX_TYPE_RANGE: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_RANGE; break;
    case MONGOCRYPT_INDEX_TYPE_STRING: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH; break;
    default:
        // This might be unreachable because of other validation. Better safe than
        // sorry.
        _mongocrypt_ctx_fail_w_msg(ctx, "Invalid value for EncryptOpts.indexType");
        goto fail;
    }

    if (ctx->opts.rangeopts.set) {
        // Process the RangeOpts and the input 'v' document into a new 'v'.
        // The new 'v' document will be a FLE2RangeInsertSpec.
        bson_t old_v;

        if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &old_v)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert input to BSON");
            goto fail;
        }

        // RangeOpts with query_type is handled above.
        BSON_ASSERT(!ctx->opts.query_type.set);
        if (!mc_RangeOpts_to_FLE2RangeInsertSpec(&ctx->opts.rangeopts.value, &old_v, &new_v, ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }

        if (!bson_iter_init_find(&marking.u.fle1.v_iter, &new_v, "v")) {
            _mongocrypt_ctx_fail_w_msg(ctx, "invalid input BSON, must contain 'v'");
            goto fail;
        }

        marking.u.fle2.sparsity = ctx->opts.rangeopts.value.sparsity;

    } else if (ctx->opts.textopts.set) {
        bson_t old_v;

        if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &old_v)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert input to BSON");
            goto fail;
        }

        if (ctx->opts.query_type.set) {
            if (!mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query(&ctx->opts.textopts.value,
                                                                   &old_v,
                                                                   ctx->opts.query_type.value,
                                                                   &new_v,
                                                                   ctx->status)) {
                _mongocrypt_ctx_fail(ctx);
                goto fail;
            }
        } else {
            if (!mc_TextOpts_to_FLE2TextSearchInsertSpec(&ctx->opts.textopts.value, &old_v, &new_v, ctx->status)) {
                _mongocrypt_ctx_fail(ctx);
                goto fail;
            }
        }

        if (!bson_iter_init_find(&marking.u.fle2.v_iter, &new_v, "v")) {
            _mongocrypt_ctx_fail_w_msg(ctx, "invalid input BSON, must contain 'v'");
            goto fail;
        }
    } else {
        bson_t as_bson;

        /* Get iterator to input 'v' BSON value. */
        if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &as_bson)) {
            _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert input to BSON");
            goto fail;
        }

        if (!bson_iter_init_find(&marking.u.fle1.v_iter, &as_bson, "v")) {
            _mongocrypt_ctx_fail_w_msg(ctx, "invalid input BSON, must contain 'v'");
            goto fail;
        }
    }

    _mongocrypt_buffer_copy_to(&ctx->opts.key_id, &marking.u.fle2.user_key_id);
    if (!_mongocrypt_buffer_empty(&ctx->opts.index_key_id)) {
        _mongocrypt_buffer_copy_to(&ctx->opts.index_key_id, &marking.u.fle2.index_key_id);
    } else {
        _mongocrypt_buffer_copy_to(&ctx->opts.key_id, &marking.u.fle2.index_key_id);
    }

    // Handle contention factor:
    switch (ctx->opts.index_type.value) {
    case MONGOCRYPT_INDEX_TYPE_NONE:
    default:
        BSON_ASSERT(!ctx->opts.contention_factor.set); // Checked earlier in explicit_encrypt_init.
        break;

    case MONGOCRYPT_INDEX_TYPE_EQUALITY:
    case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED:
    case MONGOCRYPT_INDEX_TYPE_RANGE:
    case MONGOCRYPT_INDEX_TYPE_STRING:
        // All QE indexed algorithms require contention factor.
        BSON_ASSERT(ctx->opts.contention_factor.set); // Checked earlier in explicit_encrypt_init.
        marking.u.fle2.maxContentionFactor = ctx->opts.contention_factor.value;
    }

    /* Convert marking to ciphertext. */
    {
        bson_value_t v_out;
        /* v_wrapped is the BSON document { 'v':  }. */
        bson_t v_wrapped = BSON_INITIALIZER;

        if (!_marking_to_bson_value(&ctx->kb, &marking, &v_out, ctx->status)) {
            bson_destroy(&v_wrapped);
            _mongocrypt_ctx_fail(ctx);
            goto fail;
        }

        bson_append_value(&v_wrapped, MONGOCRYPT_STR_AND_LEN("v"), &v_out);
        _mongocrypt_buffer_steal_from_bson(&ectx->encrypted_cmd, &v_wrapped);
        _mongocrypt_buffer_to_binary(&ectx->encrypted_cmd, out);
        ctx->state = MONGOCRYPT_CTX_DONE;
        bson_value_destroy(&v_out);
    }

    ret = true;
fail:
    bson_destroy(&new_v);
    _mongocrypt_marking_cleanup(&marking);
    return ret;
}

static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    bson_t as_bson, converted;
    bson_iter_t iter = {0};
    _mongocrypt_ctx_encrypt_t *ectx;
    bool res;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    if (context_uses_fle2(ctx)) {
        return _fle2_finalize(ctx, out);
    } else if (ctx->opts.index_type.set) {
        return _fle2_finalize_explicit(ctx, out);
    }

    if (!ectx->explicit) {
        if (ctx->nothing_to_do) {
            _mongocrypt_buffer_to_binary(&ectx->original_cmd, out);
            ctx->state = MONGOCRYPT_CTX_DONE;
            return true;
        }
        if (!_mongocrypt_buffer_to_bson(&ectx->marked_cmd, &as_bson)) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
        }

        bson_iter_init(&iter, &as_bson);
        bson_init(&converted);
        if (!_mongocrypt_transform_binary_in_bson(_replace_marking_with_ciphertext,
                                                  &ctx->kb,
                                                  TRAVERSE_MATCH_MARKING,
                                                  &iter,
                                                  &converted,
                                                  ctx->status)) {
            bson_destroy(&converted);
            return _mongocrypt_ctx_fail(ctx);
        }

        bson_t original_cmd_bson;
        if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &original_cmd_bson)) {
            bson_destroy(&converted);
            return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson in original_cmd");
        }

        // If input command has $db, ensure output command has $db.
        bson_iter_t iter;
        if (bson_iter_init_find(&iter, &original_cmd_bson, "$db")) {
            if (!bson_iter_init_find(&iter, &converted, "$db")) {
                BSON_APPEND_UTF8(&converted, "$db", ectx->cmd_db);
            }
        }
    } else {
        /* For explicit encryption, we have no marking, but we can fake one */
        _mongocrypt_marking_t marking;
        bson_value_t value;

        memset(&value, 0, sizeof(value));

        _mongocrypt_marking_init(&marking);

        if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &as_bson)) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "malformed bson");
        }

        if (!bson_iter_init_find(&iter, &as_bson, "v")) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "invalid msg, must contain 'v'");
        }

        memcpy(&marking.u.fle1.v_iter, &iter, sizeof(bson_iter_t));
        marking.u.fle1.algorithm = ctx->opts.algorithm;
        _mongocrypt_buffer_set_to(&ctx->opts.key_id, &marking.u.fle1.key_id);
        if (ctx->opts.key_alt_names) {
            bson_value_copy(&ctx->opts.key_alt_names->value, &marking.u.fle1.key_alt_name);
            marking.type = MONGOCRYPT_MARKING_FLE1_BY_ALTNAME;
        }

        bson_init(&converted);
        res = _marking_to_bson_value(&ctx->kb, &marking, &value, ctx->status);
        if (res) {
            bson_append_value(&converted, MONGOCRYPT_STR_AND_LEN("v"), &value);
        }

        bson_value_destroy(&value);
        _mongocrypt_marking_cleanup(&marking);

        if (!res) {
            bson_destroy(&converted);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    _mongocrypt_buffer_steal_from_bson(&ectx->encrypted_cmd, &converted);
    _mongocrypt_buffer_to_binary(&ectx->encrypted_cmd, out);
    ctx->state = MONGOCRYPT_CTX_DONE;

    return true;
}

static void _cleanup(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;

    if (!ctx) {
        return;
    }

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    mc_schema_broker_destroy(ectx->sb);
    bson_free(ectx->target_ns);
    bson_free(ectx->cmd_db);
    bson_free(ectx->target_db);
    bson_free(ectx->target_coll);
    _mongocrypt_buffer_cleanup(&ectx->list_collections_filter);
    _mongocrypt_buffer_cleanup(&ectx->original_cmd);
    _mongocrypt_buffer_cleanup(&ectx->mongocryptd_cmd);
    _mongocrypt_buffer_cleanup(&ectx->marked_cmd);
    _mongocrypt_buffer_cleanup(&ectx->encrypted_cmd);
    _mongocrypt_buffer_cleanup(&ectx->ismaster.cmd);
}

static bool _try_schema_from_schema_map(mongocrypt_ctx_t *ctx) {
    mongocrypt_t *crypt;
    _mongocrypt_ctx_encrypt_t *ectx;
    bson_t schema_map;

    BSON_ASSERT_PARAM(ctx);

    crypt = ctx->crypt;
    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    if (_mongocrypt_buffer_empty(&crypt->opts.schema_map)) {
        /* No schema map set. */
        return true;
    }

    if (!_mongocrypt_buffer_to_bson(&crypt->opts.schema_map, &schema_map)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "malformed schema map");
    }

    if (!mc_schema_broker_satisfy_from_schemaMap(ectx->sb, &schema_map, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    if (!mc_schema_broker_need_more_schemas(ectx->sb)) {
        // Have all needed schemas. Proceed to next state.
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    }
    return true;
}

/* Check if the local encrypted field config map has an entry for this
 * collection.
 * If an encrypted field config is found, the context transitions to
 * MONGOCRYPT_CTX_NEED_MONGO_MARKINGS. */
static bool _fle2_try_encrypted_field_config_from_map(mongocrypt_ctx_t *ctx) {
    mongocrypt_t *crypt;
    _mongocrypt_ctx_encrypt_t *ectx;
    bson_t encrypted_field_config_map;

    BSON_ASSERT_PARAM(ctx);

    crypt = ctx->crypt;
    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    if (_mongocrypt_buffer_empty(&crypt->opts.encrypted_field_config_map)) {
        /* No encrypted_field_config_map set. */
        return true;
    }

    if (!_mongocrypt_buffer_to_bson(&crypt->opts.encrypted_field_config_map, &encrypted_field_config_map)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "unable to convert encrypted_field_config_map to BSON");
    }

    if (!mc_schema_broker_satisfy_from_encryptedFieldsMap(ectx->sb, &encrypted_field_config_map, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    if (!mc_schema_broker_need_more_schemas(ectx->sb)) {
        // Have all needed schemas. Proceed to next state.
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    }
    return true;
}

static bool _try_schema_from_cache(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    if (!mc_schema_broker_satisfy_from_cache(ectx->sb, &ctx->crypt->cache_collinfo, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    if (!mc_schema_broker_need_more_schemas(ectx->sb)) {
        // Have all needed schemas. Proceed to next state.
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    } else {
        // Request a listCollections command to check for remote schemas.
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
        if (ectx->target_db) {
            if (!ctx->crypt->opts.use_need_mongo_collinfo_with_db_state) {
                _mongocrypt_ctx_fail_w_msg(
                    ctx,
                    "Fetching remote collection information on separate databases is not supported. Try "
                    "upgrading driver, or specify a local schemaMap or encryptedFieldsMap.");
                return false;
            }
            // Target database differs from command database. Request collection info from target database.
            ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB;
        }
    }
    return true;
}

/* _try_empty_schema_for_create uses an empty JSON schema for the create
 * command. This is to avoid an unnecessary 'listCollections' command for
 * create. */
static bool _try_empty_schema_for_create(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;

    BSON_ASSERT_PARAM(ctx);

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    /* As a special case, use an empty schema for the 'create' command. */
    const char *cmd_name = ectx->cmd_name;

    if (0 != strcmp(cmd_name, "create")) {
        return true;
    }

    // Satisfy with an empty schema. Do not cache the entry.
    if (!mc_schema_broker_satisfy_remaining_with_empty_schemas(ectx->sb, NULL /* cache */, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    BSON_ASSERT(!mc_schema_broker_need_more_schemas(ectx->sb));
    // Have all needed schemas. Proceed to next state.
    ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    return true;
}

/* _try_schema_from_create_or_collMod_cmd tries to find a JSON schema included
 * in a create or collMod command by checking for "validator.$jsonSchema".
 * Example:
 * {
 *     "create" : "coll",
 *     "validator" : {
 *         "$jsonSchema" : {
 *             "properties" : { "a" : { "bsonType" : "number" } }
 *          }
 *     }
 * }
 * If the "create" command does not include a JSON schema, an empty JSON schema
 * is returned. This is to avoid an unnecessary 'listCollections' command for
 * create.
 *
 * If the "collMod" command does not include a JSON schema, a schema is later
 * requested by entering the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO state.
 * This is because a "collMod" command may have sensitive data in the
 * "validator" field.
 */
static bool _try_schema_from_create_or_collMod_cmd(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx;
    mongocrypt_status_t *status;

    BSON_ASSERT_PARAM(ctx);

    status = ctx->status;

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    const char *cmd_name = ectx->cmd_name;

    if (0 != strcmp(cmd_name, "create") && 0 != strcmp(cmd_name, "collMod")) {
        return true;
    }

    bson_t cmd_bson;

    if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &cmd_bson)) {
        CLIENT_ERR("unable to convert command buffer to BSON");
        _mongocrypt_ctx_fail(ctx);
        return false;
    }

    if (!mc_schema_broker_satisfy_from_create_or_collMod(ectx->sb, &cmd_bson, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    if (!mc_schema_broker_need_more_schemas(ectx->sb)) {
        // Have all needed schemas. Proceed to next state.
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
    }
    return true;
}

static bool
_permitted_for_encryption(bson_iter_t *iter, mongocrypt_encryption_algorithm_t algo, mongocrypt_status_t *status) {
    bson_type_t bson_type;
    const bson_value_t *bson_value;
    bool ret = false;

    BSON_ASSERT_PARAM(iter);

    bson_value = bson_iter_value(iter);
    if (!bson_value) {
        CLIENT_ERR("Unknown BSON type");
        goto fail;
    }
    bson_type = bson_value->value_type;
    switch (bson_type) {
    case BSON_TYPE_NULL:
    case BSON_TYPE_MINKEY:
    case BSON_TYPE_MAXKEY:
    case BSON_TYPE_UNDEFINED: CLIENT_ERR("BSON type invalid for encryption"); goto fail;
    case BSON_TYPE_BINARY:
        if (bson_value->value.v_binary.subtype == BSON_SUBTYPE_ENCRYPTED) {
            CLIENT_ERR("BSON binary subtype 6 is invalid for encryption");
            goto fail;
        }
        /* ok */
        break;
    case BSON_TYPE_DOUBLE:
    case BSON_TYPE_DOCUMENT:
    case BSON_TYPE_ARRAY:
    case BSON_TYPE_CODEWSCOPE:
    case BSON_TYPE_BOOL:
    case BSON_TYPE_DECIMAL128:
        if (algo == MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC) {
            CLIENT_ERR("BSON type invalid for deterministic encryption");
            goto fail;
        }
        break;
    case BSON_TYPE_UTF8:
    case BSON_TYPE_OID:
    case BSON_TYPE_DATE_TIME:
    case BSON_TYPE_REGEX:
    case BSON_TYPE_DBPOINTER:
    case BSON_TYPE_CODE:
    case BSON_TYPE_SYMBOL:
    case BSON_TYPE_INT32:
    case BSON_TYPE_TIMESTAMP:
    case BSON_TYPE_INT64:
        /* ok */
        break;
    case BSON_TYPE_EOD:
    default: CLIENT_ERR("invalid BSON value type 00"); goto fail;
    }

    ret = true;
fail:
    return ret;
}

// explicit_encrypt_init is common code shared by
// mongocrypt_ctx_explicit_encrypt_init and
// mongocrypt_ctx_explicit_encrypt_expression_init.
static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg) {
    _mongocrypt_ctx_encrypt_t *ectx;
    bson_t as_bson;
    bson_iter_t iter;
    _mongocrypt_ctx_opts_spec_t opts_spec = {0};

    if (!ctx) {
        return false;
    }
    memset(&opts_spec, 0, sizeof(opts_spec));
    opts_spec.key_descriptor = OPT_REQUIRED;
    opts_spec.algorithm = OPT_OPTIONAL;
    opts_spec.rangeopts = OPT_OPTIONAL;

    if (!_mongocrypt_ctx_init(ctx, &opts_spec)) {
        return false;
    }

    /* Error if any mutually exclusive FLE 1 and FLE 2 options are set. */
    {
        /* key_alt_names is FLE 1 only. */
        if (ctx->opts.key_alt_names != NULL) {
            if (ctx->opts.index_type.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both key alt name and index type");
            }
            if (!_mongocrypt_buffer_empty(&ctx->opts.index_key_id)) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both key alt name and index key id");
            }
            if (ctx->opts.contention_factor.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both key alt name and contention factor");
            }
            if (ctx->opts.query_type.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both key alt name and query type");
            }
            if (ctx->opts.rangeopts.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both key alt name and range opts");
            }
        }
        /* algorithm is FLE 1 only. */
        if (ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE) {
            if (!_mongocrypt_buffer_empty(&ctx->opts.index_key_id)) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both algorithm and index key id");
            }
            if (ctx->opts.contention_factor.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both algorithm and contention factor");
            }
            if (ctx->opts.query_type.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both algorithm and query type");
            }
            if (ctx->opts.rangeopts.set) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set both algorithm and range opts");
            }
        }
    }

    if (ctx->opts.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE && !ctx->opts.index_type.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "algorithm or index type required");
    }

    if (ctx->opts.contention_factor.set && ctx->opts.index_type.set
        && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set contention factor with no index type");
    }

    if (ctx->opts.query_type.set && ctx->opts.index_type.set
        && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set query type with no index type");
    }

    if (ctx->opts.query_type.set) {
        const mongocrypt_query_type_t qt = ctx->opts.query_type.value;
        if (qt == MONGOCRYPT_QUERY_TYPE_PREFIX) {
            if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "prefix query type requires string index type");
            }
        }
        if (qt == MONGOCRYPT_QUERY_TYPE_SUFFIX) {
            if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "suffix query type requires string index type");
            }
        }
        if (qt == MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW) {
            if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) {
                return _mongocrypt_ctx_fail_w_msg(ctx, "substringPreview query type requires string index type");
            }
        }
    }

    if (ctx->opts.rangeopts.set && ctx->opts.index_type.set) {
        if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_NONE) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with no index type");
        }

        if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_EQUALITY) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with equality index type");
        }

        if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with 'string' algorithm");
        }
    }

    if (ctx->opts.textopts.set && ctx->opts.index_type.set) {
        if (ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_STRING) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set string opts without string index type");
        }
    }

    if (ctx->opts.contention_factor.set && !mc_validate_contention(ctx->opts.contention_factor.value, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_EQUALITY
        && !ctx->opts.contention_factor.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for indexed algorithm");
    }

    // Check required options for range algorithm are set:
    if (ctx->opts.index_type.set
        && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGE
            || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED)) {
        if (!ctx->opts.contention_factor.set) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for range indexed algorithm");
        }

        if (!ctx->opts.rangeopts.set) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "range opts are required for range indexed algorithm");
        }
    }

    // Check required options for text algorithm are set:
    if (ctx->opts.index_type.set && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) {
        if (!ctx->opts.contention_factor.set) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for string algorithm");
        }

        if (!ctx->opts.textopts.set) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "string opts are required for string algorithm");
        }
    }

    if (ctx->opts.rangeopts.set && !mc_validate_sparsity(ctx->opts.rangeopts.value.sparsity, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    // If query type is set, it must match the index type.
    if (ctx->opts.query_type.set && ctx->opts.index_type.set) {
        mongocrypt_status_t *const status = ctx->status;
        bool matches = false;

        switch (ctx->opts.query_type.value) {
        case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED:
            // Don't allow deprecated query type if we are using new index type.
            matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED);
            break;
        case MONGOCRYPT_QUERY_TYPE_RANGE:
            // New query type is compatible with both new and old index types.
            matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED
                       || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_RANGE);
            break;
        case MONGOCRYPT_QUERY_TYPE_EQUALITY:
            matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_EQUALITY);
            break;
        // fallthrough
        case MONGOCRYPT_QUERY_TYPE_PREFIX:
        case MONGOCRYPT_QUERY_TYPE_SUFFIX:
        case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW:
            matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING);
            break;
        default:
            CLIENT_ERR("unsupported value for query_type: %d", (int)ctx->opts.query_type.value);
            return _mongocrypt_ctx_fail(ctx);
        }

        if (!matches) {
            CLIENT_ERR("query_type (%s) must match index_type (%s)",
                       _mongocrypt_query_type_to_string(ctx->opts.query_type.value),
                       _mongocrypt_index_type_to_string(ctx->opts.index_type.value));
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    ctx->type = _MONGOCRYPT_TYPE_ENCRYPT;
    ectx->explicit = true;
    ectx->sb = mc_schema_broker_new();
    ctx->vtable.finalize = _finalize;
    ctx->vtable.cleanup = _cleanup;

    if (!msg || !msg->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "msg required for explicit encryption");
    }

    if (ctx->opts.key_alt_names) {
        if (!_mongocrypt_key_broker_request_name(&ctx->kb, &ctx->opts.key_alt_names->value)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    } else {
        if (!_mongocrypt_key_broker_request_id(&ctx->kb, &ctx->opts.key_id)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    if (!_mongocrypt_buffer_empty(&ctx->opts.index_key_id)) {
        if (!_mongocrypt_key_broker_request_id(&ctx->kb, &ctx->opts.index_key_id)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    _mongocrypt_buffer_init(&ectx->original_cmd);

    _mongocrypt_buffer_copy_from_binary(&ectx->original_cmd, msg);
    if (!_mongocrypt_buffer_to_bson(&ectx->original_cmd, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "msg must be bson");
    }

    if (!bson_iter_init_find(&iter, &as_bson, "v")) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid msg, must contain 'v'");
    }

    if (!_permitted_for_encryption(&iter, ctx->opts.algorithm, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    (void)_mongocrypt_key_broker_requests_done(&ctx->kb);
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

bool mongocrypt_ctx_explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg) {
    if (!explicit_encrypt_init(ctx, msg)) {
        return false;
    }
    if (ctx->opts.query_type.set
        && (ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGE
            || ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "Encrypt may not be used for range queries. Use EncryptExpression.");
    }
    return true;
}

bool mongocrypt_ctx_explicit_encrypt_expression_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg) {
    if (!explicit_encrypt_init(ctx, msg)) {
        return false;
    }
    if (!ctx->opts.query_type.set
        || !(ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGE
             || ctx->opts.query_type.value == MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "EncryptExpression may only be used for range queries.");
    }
    return true;
}

static bool _check_cmd_for_auto_encrypt_bulkWrite(mongocrypt_binary_t *cmd,
                                                  char **target_db,
                                                  char **target_coll,
                                                  mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(cmd);
    BSON_ASSERT_PARAM(target_db);
    BSON_ASSERT_PARAM(target_coll);

    bson_t as_bson;
    bson_iter_t cmd_iter = {0};

    if (!_mongocrypt_binary_to_bson(cmd, &as_bson) || !bson_iter_init(&cmd_iter, &as_bson)) {
        CLIENT_ERR("invalid command BSON");
        return false;
    }

    bson_iter_t ns_iter = cmd_iter;
    if (!bson_iter_find_descendant(&ns_iter, "nsInfo.0.ns", &ns_iter)) {
        CLIENT_ERR("failed to find namespace in `bulkWrite` command");
        return false;
    }

    if (!BSON_ITER_HOLDS_UTF8(&ns_iter)) {
        CLIENT_ERR("expected namespace to be UTF8, got: %s", mc_bson_type_to_string(bson_iter_type(&ns_iter)));
        return false;
    }

    const char *target_ns = bson_iter_utf8(&ns_iter, NULL /* length */);
    // Parse `target_ns` into "."
    const char *dot = strstr(target_ns, ".");
    if (!dot) {
        CLIENT_ERR("expected namespace to contain dot, got: %s", target_ns);
        return false;
    }
    *target_coll = bson_strdup(dot + 1);
    // Get the database from the `ns` field (which may differ from `cmd_db`).
    ptrdiff_t db_len = dot - target_ns;
    if ((uint64_t)db_len > SIZE_MAX) {
        CLIENT_ERR("unexpected database length exceeds %zu", SIZE_MAX);
        return false;
    }
    *target_db = bson_strndup(target_ns, (size_t)db_len);

    // Ensure only one `nsInfo` element is present.
    // Query analysis (mongocryptd/crypt_shared) currently only supports one namespace.
    if (bson_has_field(&as_bson, "nsInfo.1")) {
        CLIENT_ERR("expected one namespace in `bulkWrite`, but found more than one. Only one namespace is supported.");
        return false;
    }

    return true;
}

static bool
_check_cmd_for_auto_encrypt(mongocrypt_binary_t *cmd, bool *bypass, char **target_coll, mongocrypt_status_t *status) {
    bson_t as_bson;
    bson_iter_t iter = {0}, target_coll_iter;
    const char *cmd_name;
    bool eligible = false;

    BSON_ASSERT_PARAM(cmd);
    BSON_ASSERT_PARAM(bypass);
    BSON_ASSERT_PARAM(target_coll);

    *bypass = false;

    if (!_mongocrypt_binary_to_bson(cmd, &as_bson) || !bson_iter_init(&iter, &as_bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }

    /* The command name is the first key. */
    if (!bson_iter_next(&iter)) {
        CLIENT_ERR("invalid empty BSON");
        return false;
    }

    cmd_name = bson_iter_key(&iter);
    BSON_ASSERT(cmd_name);

    /* get the collection name (or NULL if database/client command). */
    if (0 == strcmp(cmd_name, "explain")) {
        if (!BSON_ITER_HOLDS_DOCUMENT(&iter)) {
            CLIENT_ERR("explain value is not a document");
            return false;
        }
        if (!bson_iter_recurse(&iter, &target_coll_iter)) {
            CLIENT_ERR("malformed BSON for encrypt command");
            return false;
        }
        if (!bson_iter_next(&target_coll_iter)) {
            CLIENT_ERR("invalid empty BSON");
            return false;
        }
    } else {
        memcpy(&target_coll_iter, &iter, sizeof(iter));
    }

    if (BSON_ITER_HOLDS_UTF8(&target_coll_iter)) {
        *target_coll = bson_strdup(bson_iter_utf8(&target_coll_iter, NULL));
    } else {
        *target_coll = NULL;
    }

    /* check if command is eligible for auto encryption, bypassed, or ineligible.
     */
    if (0 == strcmp(cmd_name, "aggregate")) {
        /* collection level aggregate ok, database/client is not. */
        eligible = true;
    } else if (0 == strcmp(cmd_name, "count")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "distinct")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "delete")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "find")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "findAndModify")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "getMore")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "insert")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "update")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "authenticate")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "getnonce")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "logout")) {
        *bypass = true;
    } else if (0 == bson_strcasecmp(cmd_name, "isMaster")) {
        /* use case insensitive compare for ismaster, since some drivers send
         * "ismaster" and others send "isMaster" */
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "abortTransaction")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "commitTransaction")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "endSessions")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "startSession")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "create")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "createIndexes")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "drop")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "dropDatabase")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "dropIndexes")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "killCursors")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "listCollections")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "listDatabases")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "listIndexes")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "renameCollection")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "explain")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "ping")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "saslStart")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "saslContinue")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "killAllSessions")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "killSessions")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "killAllSessionsByPattern")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "refreshSessions")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "compactStructuredEncryptionData")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "cleanupStructuredEncryptionData")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "collMod")) {
        eligible = true;
    } else if (0 == strcmp(cmd_name, "hello")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "buildInfo") || 0 == strcmp(cmd_name, "buildinfo")) {
        // Accept either case form to match server behavior.
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "getCmdLineOpts")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "getLog")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "createSearchIndexes")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "listSearchIndexes")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "dropSearchIndex")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "updateSearchIndex")) {
        *bypass = true;
    } else if (0 == strcmp(cmd_name, "serverStatus")) {
        *bypass = true;
    }

    /* database/client commands are ineligible. */
    if (eligible) {
        if (!*target_coll) {
            CLIENT_ERR("non-collection command not supported for auto encryption: %s", cmd_name);
            return false;
        }
        if (0 == strlen(*target_coll)) {
            CLIENT_ERR("empty collection name on command: %s", cmd_name);
            return false;
        }
    }

    if (eligible || *bypass) {
        return true;
    }

    CLIENT_ERR("command not supported for auto encryption: %s", cmd_name);
    return false;
}

static bool needs_ismaster_check(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    bool using_mongocryptd = !ectx->bypass_query_analysis && !ctx->crypt->csfle.okay;

    if (!using_mongocryptd) {
        return false;
    }

    if (mc_schema_broker_has_multiple_requests(ectx->sb)) {
        // Only mongocryptd 8.1 (wire version 26) supports multiple schemas with csfleEncryptionSchemas.
        return true;
    }
    // MONGOCRYPT-429: The "create" and "createIndexes" command are only supported on mongocrypt 6.0 (wire version 17).
    if (0 == strcmp(ectx->cmd_name, "create") || 0 == strcmp(ectx->cmd_name, "createIndexes")) {
        return true;
    }

    return false;
}

// `find_collections_in_pipeline` finds other collection names in an aggregate pipeline that may need schemas.
static bool find_collections_in_pipeline(mc_schema_broker_t *sb,
                                         bson_iter_t *pipeline_iter_ptr,
                                         const char *db,
                                         mstr_view path,
                                         mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(sb);
    BSON_ASSERT_PARAM(pipeline_iter_ptr);
    BSON_ASSERT_PARAM(db);

    bson_iter_t pipeline_iter = *pipeline_iter_ptr; // Operate on a copy.

    bson_iter_t array_iter;
    if (!BSON_ITER_HOLDS_ARRAY(&pipeline_iter) || !bson_iter_recurse(&pipeline_iter, &array_iter)) {
        CLIENT_ERR("failed to recurse pipeline at path: %s", path.data);
        return false;
    }

    while (bson_iter_next(&array_iter)) {
        bson_iter_t stage_iter;
        const char *stage_key = bson_iter_key(&array_iter);

        if (!BSON_ITER_HOLDS_DOCUMENT(&array_iter) || !bson_iter_recurse(&array_iter, &stage_iter)
            || !bson_iter_next(&stage_iter)) {
            CLIENT_ERR("failed to recurse stage at path: %s.%s", path.data, stage_key);
            return false;
        }

        const char *stage = bson_iter_key(&stage_iter);
        // Check for $lookup.
        if (0 == strcmp(stage, "$lookup")) {
            bson_iter_t lookup_iter;
            if (!BSON_ITER_HOLDS_DOCUMENT(&stage_iter) || !bson_iter_recurse(&stage_iter, &lookup_iter)) {
                CLIENT_ERR("failed to recurse $lookup at path: %s.%s", path.data, stage_key);
                return false;
            }

            while (bson_iter_next(&lookup_iter)) {
                const char *field = bson_iter_key(&lookup_iter);
                if (0 == strcmp(field, "from")) {
                    if (!BSON_ITER_HOLDS_UTF8(&lookup_iter)) {
                        CLIENT_ERR("expected string, but '%s' for 'from' field at path: %s.%s",
                                   mc_bson_type_to_string(bson_iter_type(&lookup_iter)),
                                   path.data,
                                   stage_key);
                        return false;
                    }
                    const char *from = bson_iter_utf8(&lookup_iter, NULL);
                    if (!mc_schema_broker_request(sb, db, from, status)) {
                        return false;
                    }
                }

                if (0 == strcmp(field, "pipeline")) {
                    mstr subpath = mstr_append(path, mstrv_lit("."));
                    mstr_inplace_append(&subpath, mstrv_view_cstr(stage_key));
                    mstr_inplace_append(&subpath, mstrv_lit(".$lookup.pipeline"));
                    if (!find_collections_in_pipeline(sb, &lookup_iter, db, subpath.view, status)) {
                        mstr_free(subpath);
                        return false;
                    }
                    mstr_free(subpath);
                }
            }
        }

        // Check for $facet.
        if (0 == strcmp(stage, "$facet")) {
            bson_iter_t facet_iter;
            if (!BSON_ITER_HOLDS_DOCUMENT(&stage_iter) || !bson_iter_recurse(&stage_iter, &facet_iter)) {
                CLIENT_ERR("failed to recurse $facet at path: %s.%s", path.data, stage_key);
                return false;
            }

            while (bson_iter_next(&facet_iter)) {
                const char *field = bson_iter_key(&facet_iter);
                mstr subpath = mstr_append(path, mstrv_lit("."));
                mstr_inplace_append(&subpath, mstrv_view_cstr(stage_key));
                mstr_inplace_append(&subpath, mstrv_lit(".$facet."));
                mstr_inplace_append(&subpath, mstrv_view_cstr(field));
                if (!find_collections_in_pipeline(sb, &facet_iter, db, subpath.view, status)) {
                    mstr_free(subpath);
                    return false;
                }
                mstr_free(subpath);
            }
        }

        // Check for $unionWith.
        if (0 == strcmp(stage, "$unionWith")) {
            bson_iter_t unionWith_iter;
            if (!BSON_ITER_HOLDS_DOCUMENT(&stage_iter) || !bson_iter_recurse(&stage_iter, &unionWith_iter)) {
                CLIENT_ERR("failed to recurse $unionWith at path: %s.%s", path.data, stage_key);
                return false;
            }

            while (bson_iter_next(&unionWith_iter)) {
                const char *field = bson_iter_key(&unionWith_iter);
                if (0 == strcmp(field, "coll")) {
                    if (!BSON_ITER_HOLDS_UTF8(&unionWith_iter)) {
                        CLIENT_ERR("expected string, but got '%s' for 'coll' field at path: %s.%s",
                                   mc_bson_type_to_string(bson_iter_type(&unionWith_iter)),
                                   path.data,
                                   stage_key);
                        return false;
                    }
                    const char *coll = bson_iter_utf8(&unionWith_iter, NULL);
                    if (!mc_schema_broker_request(sb, db, coll, status)) {
                        return false;
                    }
                }

                if (0 == strcmp(field, "pipeline")) {
                    mstr subpath = mstr_append(path, mstrv_lit("."));
                    mstr_inplace_append(&subpath, mstrv_view_cstr(stage_key));
                    mstr_inplace_append(&subpath, mstrv_lit(".$unionWith.pipeline"));
                    if (!find_collections_in_pipeline(sb, &unionWith_iter, db, subpath.view, status)) {
                        mstr_free(subpath);
                        return false;
                    }
                    mstr_free(subpath);
                }
            }
        }
    }

    return true;
}

static bool
find_collections_in_agg(mongocrypt_binary_t *cmd, mc_schema_broker_t *sb, const char *db, mongocrypt_status_t *status) {
    bson_t cmd_bson;
    if (!_mongocrypt_binary_to_bson(cmd, &cmd_bson)) {
        CLIENT_ERR("failed to convert command to BSON");
        return false;
    }

    bson_iter_t iter;
    if (!bson_iter_init_find(&iter, &cmd_bson, "pipeline")) {
        // Command may be malformed. Let server error.
        return true;
    }

    return find_collections_in_pipeline(sb, &iter, db, mstrv_lit("aggregate.pipeline"), status);
}

bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t db_len, mongocrypt_binary_t *cmd) {
    _mongocrypt_ctx_encrypt_t *ectx;
    _mongocrypt_ctx_opts_spec_t opts_spec;

    if (!ctx) {
        return false;
    }

    if (!db) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid db");
    }

    memset(&opts_spec, 0, sizeof(opts_spec));
    opts_spec.schema = OPT_OPTIONAL;
    if (!_mongocrypt_ctx_init(ctx, &opts_spec)) {
        return false;
    }

    ectx = (_mongocrypt_ctx_encrypt_t *)ctx;
    ctx->type = _MONGOCRYPT_TYPE_ENCRYPT;
    ectx->explicit = false;
    ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
    ctx->vtable.mongo_feed_collinfo = _mongo_feed_collinfo;
    ctx->vtable.mongo_done_collinfo = _mongo_done_collinfo;
    ctx->vtable.mongo_db_collinfo = _mongo_db_collinfo;
    ctx->vtable.mongo_op_collinfo = _mongo_op_collinfo;
    ctx->vtable.mongo_op_markings = _mongo_op_markings;
    ctx->vtable.mongo_feed_markings = _mongo_feed_markings;
    ctx->vtable.mongo_done_markings = _mongo_done_markings;
    ctx->vtable.mongo_done_keys = _mongo_done_keys;
    ctx->vtable.kms_done = _kms_done;
    ctx->vtable.finalize = _finalize;
    ctx->vtable.cleanup = _cleanup;
    ectx->bypass_query_analysis = ctx->crypt->opts.bypass_query_analysis;
    ectx->sb = mc_schema_broker_new();

    if (!cmd || !cmd->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid command");
    }

    _mongocrypt_buffer_copy_from_binary(&ectx->original_cmd, cmd);

    ectx->cmd_name = get_command_name(&ectx->original_cmd, ctx->status);
    if (!ectx->cmd_name) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (!_mongocrypt_validate_and_copy_string(db, db_len, &ectx->cmd_db) || 0 == strlen(ectx->cmd_db)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid db");
    }

    if (0 == strcmp(ectx->cmd_name, "bulkWrite")) {
        // Handle `bulkWrite` as a special case.
        // `bulkWrite` includes the target namespaces in an `nsInfo` field.
        // Only one target namespace is supported.
        if (!_check_cmd_for_auto_encrypt_bulkWrite(cmd, &ectx->target_db, &ectx->target_coll, ctx->status)) {
            return _mongocrypt_ctx_fail(ctx);
        }

        ectx->target_ns = bson_strdup_printf("%s.%s", ectx->target_db, ectx->target_coll);

        if (!mc_schema_broker_request(ectx->sb, ectx->target_db, ectx->target_coll, ctx->status)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    } else {
        bool bypass;
        if (!_check_cmd_for_auto_encrypt(cmd, &bypass, &ectx->target_coll, ctx->status)) {
            return _mongocrypt_ctx_fail(ctx);
        }

        if (bypass) {
            ctx->nothing_to_do = true;
            ctx->state = MONGOCRYPT_CTX_READY;
            return true;
        }

        /* if _check_cmd_for_auto_encrypt did not bypass or error, a collection name
         * must have been set. */
        if (!ectx->target_coll) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "unexpected error: did not bypass or error but no collection name");
        }
        ectx->target_ns = bson_strdup_printf("%s.%s", ectx->cmd_db, ectx->target_coll);
        if (!mc_schema_broker_request(ectx->sb, ectx->cmd_db, ectx->target_coll, ctx->status)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    if (0 == strcmp(ectx->cmd_name, "aggregate")) {
        if (!find_collections_in_agg(cmd, ectx->sb, ectx->cmd_db, ctx->status)) {
            _mongocrypt_ctx_fail(ctx);
            return false;
        }

        if (mc_schema_broker_has_multiple_requests(ectx->sb)) {
            if (!ctx->crypt->multiple_collinfo_enabled) {
                return _mongocrypt_ctx_fail_w_msg(ctx,
                                                  "aggregate includes a $lookup stage, but libmongocrypt is not "
                                                  "configured to support encrypting a "
                                                  "command with multiple collections");
            }
        }
    }

    if (ctx->opts.kek.provider.aws.region || ctx->opts.kek.provider.aws.cmk) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "aws masterkey options must not be set");
    }

    if (!_mongocrypt_buffer_empty(&ctx->opts.key_id)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "key_id must not be set for auto encryption");
    }

    if (ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "algorithm must not be set for auto encryption");
    }

    // Check if an isMaster request to mongocryptd is needed to detect feature support:
    if (needs_ismaster_check(ctx)) {
        ectx->ismaster.needed = true;
        ctx->state = MONGOCRYPT_CTX_NEED_MONGO_MARKINGS;
        return true;
    }

    return mongocrypt_ctx_encrypt_ismaster_done(ctx);
}

static bool _all_key_requests_satisfied(_mongocrypt_key_broker_t *kb) {
    key_request_t *key_request;

    BSON_ASSERT_PARAM(kb);

    for (key_request = kb->key_requests; NULL != key_request; key_request = key_request->next) {
        if (!key_request->satisfied) {
            return false;
        }
    }
    return true;
}

#define WIRE_VERSION_SERVER_6 17
#define WIRE_VERSION_SERVER_8_1 26
#define WIRE_VERSION_SERVER_8_2 27
// The crypt_shared version format is defined in mongo_crypt-v1.h.
// Example: server 6.2.1 is encoded as 0x0006000200010000
#define CRYPT_SHARED_8_1 0x0008000100000000ull
#define CRYPT_SHARED_8_2 0x0008000200000000ull

/* mongocrypt_ctx_encrypt_ismaster_done is called when:
 * 1. The max wire version of mongocryptd is known.
 * 2. The max wire version of mongocryptd is not required for the command.
 */
static bool mongocrypt_ctx_encrypt_ismaster_done(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_encrypt_t *ectx = (_mongocrypt_ctx_encrypt_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    ectx->ismaster.needed = false;

    if (needs_ismaster_check(ctx)) {
        // MONGOCRYPT-429: "create" and "createIndexes" require bypassing on mongocryptd older than version 6.0.
        if (0 == strcmp(ectx->cmd_name, "create") || 0 == strcmp(ectx->cmd_name, "createIndexes")) {
            if (ectx->ismaster.maxwireversion < WIRE_VERSION_SERVER_6) {
                // Bypass auto encryption.
                // Satisfy schema request with an empty schema.
                if (!mc_schema_broker_satisfy_remaining_with_empty_schemas(ectx->sb,
                                                                           NULL /* do not cache */,
                                                                           ctx->status)) {
                    return _mongocrypt_ctx_fail(ctx);
                }
                ctx->nothing_to_do = true;
                ctx->state = MONGOCRYPT_CTX_READY;
                return true;
            }
        }

        if (mc_schema_broker_has_multiple_requests(ectx->sb)) {
            // Ensure mongocryptd supports multiple schemas.
            if (ectx->ismaster.maxwireversion < WIRE_VERSION_SERVER_8_1) {
                mongocrypt_status_t *status = ctx->status;
                CLIENT_ERR("Encrypting '%s' requires multiple schemas. Detected mongocryptd with wire version %" PRId32
                           ", but need %" PRId32 ". Upgrade mongocryptd to 8.1 or newer.",
                           ectx->cmd_name,
                           ectx->ismaster.maxwireversion,
                           WIRE_VERSION_SERVER_8_1);
                _mongocrypt_ctx_fail(ctx);
                return false;
            }

            if (ectx->ismaster.maxwireversion >= WIRE_VERSION_SERVER_8_2) {
                mc_schema_broker_support_mixing_schemas(ectx->sb);
            }
        }
    }

    if (ctx->crypt->csfle.okay) {
        if (mc_schema_broker_has_multiple_requests(ectx->sb)) {
            // Ensure crypt_shared supports multiple schemas.
            uint64_t version = ctx->crypt->csfle.get_version();
            const char *version_str = ctx->crypt->csfle.get_version_str();
            if (version < CRYPT_SHARED_8_1) {
                mongocrypt_status_t *status = ctx->status;
                CLIENT_ERR("Encrypting '%s' requires multiple schemas. Detected crypt_shared with version %s, but "
                           "need 8.1. Upgrade crypt_shared to 8.1 or newer.",
                           ectx->cmd_name,
                           version_str);
                _mongocrypt_ctx_fail(ctx);
                return false;
            }

            if (version >= CRYPT_SHARED_8_2) {
                mc_schema_broker_support_mixing_schemas(ectx->sb);
            }
        }
    }

    if (!_fle2_try_encrypted_field_config_from_map(ctx)) {
        return false;
    }
    if (mc_schema_broker_need_more_schemas(ectx->sb)) {
        if (!_try_schema_from_create_or_collMod_cmd(ctx)) {
            return false;
        }

        /* Check if we have a local schema from schema_map */
        if (mc_schema_broker_need_more_schemas(ectx->sb)) {
            if (!_try_schema_from_schema_map(ctx)) {
                return false;
            }
        }

        /* If we didn't have a local schema, try the cache. */
        if (mc_schema_broker_need_more_schemas(ectx->sb)) {
            if (!_try_schema_from_cache(ctx)) {
                return false;
            }
        }

        /* If we did not have a local or cached schema, check if this is a
         * "create" command. If it is a "create" command, do not run
         * "listCollections" to get a server-side schema. */
        if (mc_schema_broker_need_more_schemas(ectx->sb) && !_try_empty_schema_for_create(ctx)) {
            return false;
        }

        /* Otherwise, we need the the driver to fetch the schema. */
        if (mc_schema_broker_need_more_schemas(ectx->sb)) {
            ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO;
            if (ectx->target_db) {
                if (!ctx->crypt->opts.use_need_mongo_collinfo_with_db_state) {
                    _mongocrypt_ctx_fail_w_msg(
                        ctx,
                        "Fetching remote collection information on separate databases is not supported. Try "
                        "upgrading driver, or specify a local schemaMap or encryptedFieldsMap.");
                    return false;
                }
                // Target database may differ from command database. Request collection info from target database.
                ctx->state = MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB;
            }
        }
    }

    /* If an encrypted_field_config was set, check if keys are required for
     * compactionTokens. */

    if (!mc_schema_broker_need_more_schemas(ectx->sb) && !_fle2_collect_keys_for_compaction(ctx)) {
        return false;
    }

    const int need_keys = _fle2_collect_keys_for_encrypted_fields(ctx);
    if (need_keys == -1) {
        return false;
    } else if (need_keys == 1) {
        ectx->need_keys_for_encryptedFields = true;
    }

    if (ctx->state == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS) {
        if (ectx->bypass_query_analysis || need_keys == 1) {
            /* Keys may have been requested for compactionTokens or keyAltName
             * Finish key requests.
             */
            if (_all_key_requests_satisfied(&ctx->kb) && ectx->need_keys_for_encryptedFields) {
                return _try_run_csfle_marking(ctx);
            }
            _mongocrypt_key_broker_requests_done(&ctx->kb);
            return _mongocrypt_ctx_state_from_key_broker(ctx);
        }
        // We're ready for markings. Try to generate them ourself.
        return _try_run_csfle_marking(ctx);
    } else {
        // Other state, return to caller.
        return true;
    }
}
libmongocrypt-1.19.0/src/mongocrypt-ctx-private.h000066400000000000000000000312431521103432300220740ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_CTX_PRIVATE_H
#define MONGOCRYPT_CTX_PRIVATE_H

#include "mc-efc-private.h"
#include "mc-optional-private.h"
#include "mc-rangeopts-private.h"
#include "mc-schema-broker-private.h"
#include "mc-textopts-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-endpoint-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-key-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"

typedef enum {
    _MONGOCRYPT_TYPE_NONE,
    _MONGOCRYPT_TYPE_ENCRYPT,
    _MONGOCRYPT_TYPE_DECRYPT,
    _MONGOCRYPT_TYPE_CREATE_DATA_KEY,
    _MONGOCRYPT_TYPE_REWRAP_MANY_DATAKEY,
    _MONGOCRYPT_TYPE_COMPACT,
} _mongocrypt_ctx_type_t;

const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val);

typedef enum _mongocrypt_query_type_t {
    MONGOCRYPT_QUERY_TYPE_EQUALITY = 1,
    MONGOCRYPT_QUERY_TYPE_RANGE = 2,
    MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED = 3,
    MONGOCRYPT_QUERY_TYPE_PREFIX = 4,
    MONGOCRYPT_QUERY_TYPE_SUFFIX = 5,
    MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW = 6,
} mongocrypt_query_type_t;

const char *_mongocrypt_query_type_to_string(mongocrypt_query_type_t val);

/* Option values are validated when set.
 * Different contexts accept/require different options,
 * validated when a context is initialized.
 */
typedef struct __mongocrypt_ctx_opts_t {
    _mongocrypt_buffer_t key_id;
    _mongocrypt_key_alt_name_t *key_alt_names;
    _mongocrypt_buffer_t key_material;
    mongocrypt_encryption_algorithm_t algorithm;
    _mongocrypt_kek_t kek;
    bool retry_enabled;

    struct {
        mongocrypt_index_type_t value;
        bool set;
    } index_type;

    _mongocrypt_buffer_t index_key_id;

    struct {
        int64_t value;
        bool set;
    } contention_factor;

    struct {
        mongocrypt_query_type_t value;
        bool set;
    } query_type;

    struct {
        mc_RangeOpts_t value;
        bool set;
    } rangeopts;

    struct {
        mc_TextOpts_t value;
        bool set;
    } textopts;
} _mongocrypt_ctx_opts_t;

// `_mongocrypt_ctx_opts_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_opts_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_opts_t)
                        >= BSON_MAX(BSON_ALIGNOF(_mongocrypt_key_alt_name_t), BSON_ALIGNOF(mc_RangeOpts_t)));

/* All derived contexts may override these methods. */
typedef struct {
    const char *(*mongo_db_collinfo)(mongocrypt_ctx_t *ctx);
    bool (*mongo_op_collinfo)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);
    bool (*mongo_feed_collinfo)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in);
    bool (*mongo_done_collinfo)(mongocrypt_ctx_t *ctx);
    bool (*mongo_op_markings)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);
    bool (*mongo_feed_markings)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in);
    bool (*mongo_done_markings)(mongocrypt_ctx_t *ctx);
    bool (*mongo_op_keys)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);
    bool (*mongo_feed_keys)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in);
    bool (*mongo_done_keys)(mongocrypt_ctx_t *ctx);
    bool (*after_kms_credentials_provided)(mongocrypt_ctx_t *ctx);
    mongocrypt_kms_ctx_t *(*next_kms_ctx)(mongocrypt_ctx_t *ctx);
    bool (*kms_done)(mongocrypt_ctx_t *ctx);
    bool (*finalize)(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);
    void (*cleanup)(mongocrypt_ctx_t *ctx);
} _mongocrypt_vtable_t;

struct _mongocrypt_ctx_t {
    mongocrypt_t *crypt;
    mongocrypt_ctx_state_t state;
    _mongocrypt_ctx_type_t type;
    mongocrypt_status_t *status;
    _mongocrypt_key_broker_t kb;
    _mongocrypt_vtable_t vtable;
    _mongocrypt_ctx_opts_t opts;
    _mongocrypt_opts_kms_providers_t per_ctx_kms_providers; /* owned */
    _mongocrypt_opts_kms_providers_t kms_providers;         /* not owned, is merged from per-ctx / per-mongocrypt_t */
    bool initialized;
    /* nothing_to_do is set to true under these conditions:
     * 1. No keys are requested
     * 2. The command is bypassed for automatic encryption (e.g. ping).
     * 3. bypass_query_analysis is true.
     * TODO (MONGOCRYPT-422) replace nothing_to_do.
     */
    bool nothing_to_do;
};

/* Transition to the error state. An error status must have been set. */
bool _mongocrypt_ctx_fail(mongocrypt_ctx_t *ctx);

/* Set an error status and transition to the error state. */
bool _mongocrypt_ctx_fail_w_msg(mongocrypt_ctx_t *ctx, const char *msg);

typedef struct {
    mongocrypt_ctx_t parent;
    bool explicit;

    // `cmd_db` is the command database (appended as `$db`).
    char *cmd_db;

    // `target_ns` is the target namespace "." for the operation. May be associated with
    // jsonSchema (CSFLE) or encryptedFields (QE). For `bulkWrite`, the target namespace database may differ from
    // `cmd_db`.
    char *target_ns;

    // `target_db` is the target database for the operation. For `bulkWrite`, the target namespace database may differ
    // from `cmd_db`. If `target_db` is NULL, the target namespace database is the same as `cmd_db`.
    char *target_db;

    // `target_coll` is the target namespace collection name.
    char *target_coll;

    _mongocrypt_buffer_t list_collections_filter;

    // `sb` manages encryption schemas (JSONSchema for CSFLE and encryptedFields for QE).
    mc_schema_broker_t *sb;

    /* TODO CDRIVER-3150: audit + rename these buffers.
     * original_cmd for explicit is {v: }, for auto is the command to
     * be encrypted.
     *
     * mongocryptd_cmd is only applicable for auto encryption. It is the original
     * command with JSONSchema appended.
     *
     * marked_cmd is the value of the 'result' field in mongocryptd response
     *
     * encrypted_cmd is the final output, the original command encrypted, or for
     * explicit, the {v: } doc.
     */
    _mongocrypt_buffer_t original_cmd;
    _mongocrypt_buffer_t mongocryptd_cmd;
    _mongocrypt_buffer_t marked_cmd;
    _mongocrypt_buffer_t encrypted_cmd;
    _mongocrypt_buffer_t key_id;
    /* bypass_query_analysis is set to true to skip the
     * MONGOCRYPT_CTX_NEED_MONGO_MARKINGS state. */
    bool bypass_query_analysis;

    struct {
        bool needed;
        _mongocrypt_buffer_t cmd;
        int32_t maxwireversion;
    } ismaster;

    // cmd_name is the first BSON field in original_cmd for auto encryption.
    const char *cmd_name;

    // need_keys_for_encryptedFields is set to true when keys are requested for
    // keyAltName translation in encryptedFields.
    bool need_keys_for_encryptedFields;
} _mongocrypt_ctx_encrypt_t;

// `_mongocrypt_ctx_encrypt_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_encrypt_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_encrypt_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));

typedef struct {
    mongocrypt_ctx_t parent;
    /* TODO CDRIVER-3150: audit + rename these buffers.
     * Unlike ctx_encrypt, unwrapped_doc holds the binary value of the {v:
     * } doc.
     * */
    _mongocrypt_buffer_t original_doc;
    _mongocrypt_buffer_t decrypted_doc;
} _mongocrypt_ctx_decrypt_t;

// `_mongocrypt_ctx_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_decrypt_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_decrypt_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));

typedef struct {
    mongocrypt_ctx_t parent;
    mongocrypt_kms_ctx_t kms;
    bool kms_returned;
    _mongocrypt_buffer_t key_doc;
    _mongocrypt_buffer_t plaintext_key_material;
    _mongocrypt_buffer_t encrypted_key_material;

    const char *kmip_unique_identifier;
    bool kmip_activated;
    _mongocrypt_buffer_t kmip_secretdata;
} _mongocrypt_ctx_datakey_t;

// `_mongocrypt_ctx_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_datakey_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_datakey_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));

typedef struct _mongocrypt_ctx_rmd_datakey_t _mongocrypt_ctx_rmd_datakey_t;

struct _mongocrypt_ctx_rmd_datakey_t {
    _mongocrypt_ctx_rmd_datakey_t *next;
    mongocrypt_ctx_t *dkctx;
    _mongocrypt_key_doc_t *doc;
};

typedef struct {
    mongocrypt_ctx_t parent;
    _mongocrypt_buffer_t filter;
    mongocrypt_kms_ctx_t kms;
    _mongocrypt_ctx_rmd_datakey_t *datakeys;
    _mongocrypt_ctx_rmd_datakey_t *datakeys_iter;
    _mongocrypt_buffer_t results;
} _mongocrypt_ctx_rewrap_many_datakey_t;

// `_mongocrypt_ctx_rewrap_many_datakey_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_rewrap_many_datakey_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_rewrap_many_datakey_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));

typedef struct {
    mongocrypt_ctx_t parent;
    _mongocrypt_buffer_t result;
    mc_EncryptedFieldConfig_t efc;
} _mongocrypt_ctx_compact_t;

// `_mongocrypt_ctx_compact_t` inherits extended alignment from libbson. To dynamically allocate, use aligned
// allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_ctx_compact_t,
                    BSON_ALIGNOF(_mongocrypt_ctx_compact_t) >= BSON_ALIGNOF(mongocrypt_ctx_t));

#define MONGOCRYPT_CTX_ALLOC_SIZE                                                                                      \
    BSON_MAX(sizeof(_mongocrypt_ctx_encrypt_t),                                                                        \
             BSON_MAX(sizeof(_mongocrypt_ctx_decrypt_t),                                                               \
                      BSON_MAX(sizeof(_mongocrypt_ctx_datakey_t),                                                      \
                               BSON_MAX(sizeof(_mongocrypt_ctx_rewrap_many_datakey_t),                                 \
                                        sizeof(_mongocrypt_ctx_compact_t)))))

#define MONGOCRYPT_CTX_ALLOC_ALIGNMENT                                                                                 \
    BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_encrypt_t),                                                                  \
             BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_decrypt_t),                                                         \
                      BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_datakey_t),                                                \
                               BSON_MAX(BSON_ALIGNOF(_mongocrypt_ctx_rewrap_many_datakey_t),                           \
                                        BSON_ALIGNOF(_mongocrypt_ctx_compact_t)))))

// `_mongocrypt_ctx_t` inherits extended alignment from libbson. To dynamically allocate, use
// aligned allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof_mongocrypt_ctx_t, BSON_ALIGNOF(mongocrypt_ctx_t) >= MONGOCRYPT_CTX_ALLOC_ALIGNMENT);

/* Used for option validation. True means required. False means prohibited. */
typedef enum { OPT_PROHIBITED = 0, OPT_REQUIRED, OPT_OPTIONAL } _mongocrypt_ctx_opt_spec_t;

typedef struct {
    _mongocrypt_ctx_opt_spec_t kek;
    _mongocrypt_ctx_opt_spec_t schema;
    _mongocrypt_ctx_opt_spec_t key_descriptor; /* a key_id or key_alt_name */
    _mongocrypt_ctx_opt_spec_t key_alt_names;
    _mongocrypt_ctx_opt_spec_t key_material;
    _mongocrypt_ctx_opt_spec_t algorithm;
    _mongocrypt_ctx_opt_spec_t rangeopts;
} _mongocrypt_ctx_opts_spec_t;

/* Common initialization. */
bool _mongocrypt_ctx_init(mongocrypt_ctx_t *ctx, _mongocrypt_ctx_opts_spec_t *opt_spec) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Set the state of the context from the state of keys in the key broker. */
bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Get the KMS providers for the current context, fall back to the ones
 * from mongocrypt_t if none are provided for the context specifically. */
_mongocrypt_opts_kms_providers_t *_mongocrypt_ctx_kms_providers(mongocrypt_ctx_t *ctx);

#endif /* MONGOCRYPT_CTX_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-ctx-rewrap-many-datakey.c000066400000000000000000000320431521103432300242760ustar00rootroot00000000000000/*
 * Copyright 2022-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-ctx-private.h"

static bool _finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    bson_t doc = BSON_INITIALIZER;
    bson_t array = BSON_INITIALIZER;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    BSON_ASSERT(BSON_APPEND_ARRAY_UNSAFE_BEGIN(&doc, "v", &array));
    {
        _mongocrypt_ctx_rmd_datakey_t *iter = NULL;
        size_t idx = 0u;

        for (iter = rmdctx->datakeys; iter; (iter = iter->next), (++idx)) {
            mongocrypt_binary_t bin;
            bson_t bson;
            bson_t elem = BSON_INITIALIZER;

            if (!mongocrypt_ctx_finalize(iter->dkctx, &bin)) {
                BSON_ASSERT(bson_append_array_end(&doc, &array));
                bson_destroy(&doc);
                bson_destroy(&elem);
                return _mongocrypt_ctx_fail_w_msg(ctx, "failed to encrypt datakey with new provider");
            }

            BSON_ASSERT(bson_init_static(&bson, bin.data, bin.len));

            /* Among all (possible) fields in key document, the only fields
             * required by caller to construct the corresponding bulk write
             * operations to update the key document with rewrapped key material
             * are:
             *   - _id (same as original key)
             *   - keyMaterial (updated)
             *   - masterKey (updated)
             * Which means the following fields can be excluded:
             *   - _id (discard new ID generated during rewrapping)
             *   - creationDate
             *   - updateDate (updated via the $currentDate operator)
             *   - status
             *   - keyAltNames
             */
            bson_copy_to_excluding_noinit(&bson,
                                          &elem,
                                          "_id",
                                          "creationDate",
                                          "updateDate",
                                          "status",
                                          "keyAltNames",
                                          NULL);

            /* Preserve key ID of original document. */
            BSON_ASSERT(iter->doc);
            BSON_ASSERT(BSON_APPEND_BINARY(&elem, "_id", BSON_SUBTYPE_UUID, iter->doc->id.data, iter->doc->id.len));

            /* Array indicies must be specified manually. */
            {
                char *idx_str = bson_strdup_printf("%zu", idx);
                BSON_ASSERT(BSON_APPEND_DOCUMENT(&array, idx_str, &elem));
                bson_free(idx_str);
            }

            bson_destroy(&elem);
        }
    }
    BSON_ASSERT(bson_append_array_end(&doc, &array));

    /* Extend lifetime of bson so it can be referenced by out parameter. */
    _mongocrypt_buffer_steal_from_bson(&rmdctx->results, &doc);

    out->data = rmdctx->results.data;
    out->len = rmdctx->results.len;

    ctx->state = MONGOCRYPT_CTX_DONE;

    return true;
}

static bool _kms_done_encrypt(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    {
        _mongocrypt_ctx_rmd_datakey_t *iter;

        for (iter = rmdctx->datakeys; iter; iter = iter->next) {
            if (iter->dkctx->state == MONGOCRYPT_CTX_NEED_KMS && !mongocrypt_ctx_kms_done(iter->dkctx)) {
                _mongocrypt_status_copy_to(iter->dkctx->status, ctx->status);
                return _mongocrypt_ctx_fail(ctx);
            }
        }
    }

    /* Some providers may require multiple rounds of KMS requests. Reiterate
     * through datakey contexts to verify if more work needs to be done. */
    rmdctx->datakeys_iter = rmdctx->datakeys;

    while (rmdctx->datakeys_iter && rmdctx->datakeys_iter->dkctx->state != MONGOCRYPT_CTX_NEED_KMS) {
        rmdctx->datakeys_iter = rmdctx->datakeys_iter->next;
    }

    if (rmdctx->datakeys_iter) {
        /* More work to be done, remain in MONGOCRYPT_CTX_NEED_KMS state. */
        return true;
    }

    /* All datakeys have been encrypted and are ready to be finalized. */
    ctx->state = MONGOCRYPT_CTX_READY;
    ctx->vtable.finalize = _finalize;

    return true;
}

static mongocrypt_kms_ctx_t *_next_kms_ctx_encrypt(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    mongocrypt_ctx_t *dkctx = NULL;

    BSON_ASSERT_PARAM(ctx);
    /* Check if any need retry */
    {
        _mongocrypt_ctx_rmd_datakey_t *it = rmdctx->datakeys;
        while (it != NULL) {
            _mongocrypt_ctx_datakey_t *dkctx = (_mongocrypt_ctx_datakey_t *)it->dkctx;
            if (dkctx->kms.should_retry) {
                dkctx->kms.should_retry = false; // Reset retry state.
                return &dkctx->kms;
            }
            it = it->next;
        }
    }

    /* No more datakey contexts requiring KMS. */
    if (!rmdctx->datakeys_iter) {
        return NULL;
    }

    dkctx = rmdctx->datakeys_iter->dkctx;

    /* Skip next iterator ahead to next datakey context that needs KMS. */
    do {
        rmdctx->datakeys_iter = rmdctx->datakeys_iter->next;
    } while (rmdctx->datakeys_iter && rmdctx->datakeys_iter->dkctx->state != MONGOCRYPT_CTX_NEED_KMS);

    return mongocrypt_ctx_next_kms_ctx(dkctx);
}

static bool _add_new_datakey(mongocrypt_ctx_t *ctx, key_returned_t *key) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(key);

    /* Datakey should be fully decrypted at this stage. */
    BSON_ASSERT(key->decrypted);

    _mongocrypt_ctx_rmd_datakey_t *const datakey = bson_malloc0(sizeof(_mongocrypt_ctx_rmd_datakey_t));

    datakey->dkctx = mongocrypt_ctx_new(ctx->crypt);
    datakey->next = rmdctx->datakeys;
    datakey->doc = key->doc;
    rmdctx->datakeys = datakey; /* Ownership transfer. */

    /* Set new provider and master key (rewrapManyDataKeyOpts). */
    if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE) {
        /* Reuse current KMS provider if option not set. */
        _mongocrypt_kek_copy_to(&key->doc->kek, &datakey->dkctx->opts.kek);
    } else {
        /* Apply new KMS provider as given by options. */
        _mongocrypt_kek_copy_to(&ctx->opts.kek, &datakey->dkctx->opts.kek);
    }

    /* Preserve alt names. */
    datakey->dkctx->opts.key_alt_names = _mongocrypt_key_alt_name_copy_all(key->doc->key_alt_names);

    /* Preserve key material. */
    _mongocrypt_buffer_copy_to(&key->decrypted_key_material, &datakey->dkctx->opts.key_material);

    if (!mongocrypt_ctx_datakey_init(datakey->dkctx)) {
        _mongocrypt_status_copy_to(datakey->dkctx->status, ctx->status);
        return _mongocrypt_ctx_fail(ctx);
    }

    /* Reuse KMS credentials provided during decryption. */
    if (datakey->dkctx->state == MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS) {
        memcpy(&datakey->dkctx->kms_providers,
               _mongocrypt_ctx_kms_providers(ctx),
               sizeof(_mongocrypt_opts_kms_providers_t));
        if (!datakey->dkctx->vtable.after_kms_credentials_provided(datakey->dkctx)) {
            _mongocrypt_status_copy_to(datakey->dkctx->status, ctx->status);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    return true;
}

static bool _start_kms_encrypt(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    /* Finish KMS requests for decryption if there were any. */
    if (ctx->state == MONGOCRYPT_CTX_NEED_KMS) {
        _mongocrypt_opts_kms_providers_t *const providers = _mongocrypt_ctx_kms_providers(ctx);

        if (!_mongocrypt_key_broker_kms_done(&ctx->kb, providers)) {
            _mongocrypt_status_copy_to(ctx->kb.status, ctx->status);
            return _mongocrypt_ctx_fail(ctx);
        }

        if (!_mongocrypt_ctx_state_from_key_broker(ctx)) {
            return _mongocrypt_ctx_fail(ctx);
        }

        /* Some providers may require multiple rounds of KMS requests. */
        if (ctx->state == MONGOCRYPT_CTX_NEED_KMS) {
            return true;
        }
    }

    /* All datakeys should be decrypted at this point. */
    BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_READY);

    /* For all decrypted datakeys, initialize a corresponding datakey context. */
    {
        key_returned_t *key;

        /* Some decrypted keys may have been cached. */
        for (key = ctx->kb.keys_cached; key; key = key->next) {
            if (!_add_new_datakey(ctx, key)) {
                return _mongocrypt_ctx_fail(ctx);
            }
        }

        /* Remaining keys were decrypted via KMS requests. */
        for (key = ctx->kb.keys_returned; key; key = key->next) {
            if (!_add_new_datakey(ctx, key)) {
                return _mongocrypt_ctx_fail(ctx);
            }
        }
    }

    /* Prepare iterator used by _next_kms_ctx_encrypt. */
    rmdctx->datakeys_iter = rmdctx->datakeys;

    /* Skip datakeys that do not require a KMS request. */
    while (rmdctx->datakeys_iter && rmdctx->datakeys_iter->dkctx->state == MONGOCRYPT_CTX_READY) {
        rmdctx->datakeys_iter = rmdctx->datakeys_iter->next;
    }

    /* Skip to READY state if no KMS requests are required. */
    if (!rmdctx->datakeys_iter) {
        ctx->state = MONGOCRYPT_CTX_READY;
        ctx->vtable.finalize = _finalize;
        return true;
    }

    ctx->state = MONGOCRYPT_CTX_NEED_KMS;
    ctx->vtable.next_kms_ctx = _next_kms_ctx_encrypt;
    ctx->vtable.kms_done = _kms_done_encrypt;

    return true;
}

static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    if (!_mongocrypt_key_broker_docs_done(&ctx->kb) || !_mongocrypt_ctx_state_from_key_broker(ctx)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    /* No keys to rewrap, no work to be done. */
    if (!ctx->kb.key_requests) {
        ctx->state = MONGOCRYPT_CTX_DONE;
        return true;
    }

    /* No KMS required, skip straight to encryption. */
    if (ctx->state == MONGOCRYPT_CTX_READY) {
        return _start_kms_encrypt(ctx);
    }

    /* KMS requests needed to decrypt keys. */
    BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_NEED_KMS);

    return true;
}

static bool _mongo_op_keys(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_buffer_to_binary(&rmdctx->filter, out);

    return true;
}

static bool _kms_start_decrypt(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    return _mongocrypt_key_broker_request_any(&ctx->kb) && _mongocrypt_ctx_state_from_key_broker(ctx);
}

static void _cleanup(mongocrypt_ctx_t *ctx) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    BSON_ASSERT_PARAM(ctx);

    _mongocrypt_buffer_cleanup(&rmdctx->results);

    while (rmdctx->datakeys) {
        _mongocrypt_ctx_rmd_datakey_t *result = rmdctx->datakeys;
        rmdctx->datakeys = result->next;

        mongocrypt_ctx_destroy(result->dkctx);
        bson_free(result);
    }

    _mongocrypt_kms_ctx_cleanup(&rmdctx->kms);
    _mongocrypt_buffer_cleanup(&rmdctx->filter);
}

bool mongocrypt_ctx_rewrap_many_datakey_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *filter) {
    _mongocrypt_ctx_rewrap_many_datakey_t *const rmdctx = (_mongocrypt_ctx_rewrap_many_datakey_t *)ctx;

    if (!ctx) {
        return false;
    }

    if (!filter) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "filter must not be null");
    }

    {
        _mongocrypt_ctx_opts_spec_t opts_spec;
        memset(&opts_spec, 0, sizeof(opts_spec));

        opts_spec.kek = OPT_OPTIONAL;

        if (!_mongocrypt_ctx_init(ctx, &opts_spec)) {
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    ctx->type = _MONGOCRYPT_TYPE_REWRAP_MANY_DATAKEY;
    ctx->state = MONGOCRYPT_CTX_NEED_MONGO_KEYS;
    ctx->vtable.cleanup = _cleanup;
    ctx->vtable.kms_done = _start_kms_encrypt;
    ctx->vtable.mongo_op_keys = _mongo_op_keys;
    ctx->vtable.mongo_done_keys = _mongo_done_keys;

    _mongocrypt_buffer_copy_from_binary(&rmdctx->filter, filter);

    /* Obtain KMS credentials for use during decryption and encryption. */
    if (_mongocrypt_needs_credentials(ctx->crypt)) {
        ctx->state = MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS;
        ctx->vtable.after_kms_credentials_provided = _kms_start_decrypt;
        return true;
    }

    return _kms_start_decrypt(ctx);
}
libmongocrypt-1.19.0/src/mongocrypt-ctx.c000066400000000000000000001144711521103432300204240ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 

#include "mc-mlib/str.h"
#include "mc-textopts-private.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-private.h"

bool _mongocrypt_ctx_fail_w_msg(mongocrypt_ctx_t *ctx, const char *msg) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(msg);

    _mongocrypt_set_error(ctx->status, MONGOCRYPT_STATUS_ERROR_CLIENT, MONGOCRYPT_GENERIC_ERROR_CODE, "%s", msg);
    return _mongocrypt_ctx_fail(ctx);
}

/* A failure status has already been set. */
bool _mongocrypt_ctx_fail(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    if (mongocrypt_status_ok(ctx->status)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "unexpected, failing but no error status set");
    }
    ctx->state = MONGOCRYPT_CTX_ERROR;
    return false;
}

static bool
_set_binary_opt(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *binary, _mongocrypt_buffer_t *buf, bson_subtype_t subtype) {
    BSON_ASSERT_PARAM(ctx);

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (!binary || !binary->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "option must be non-NULL");
    }

    if (!_mongocrypt_buffer_empty(buf)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "option already set");
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (subtype == BSON_SUBTYPE_UUID && binary->len != 16) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "expected 16 byte UUID");
    }

    _mongocrypt_buffer_copy_from_binary(buf, binary);
    buf->subtype = subtype;

    return true;
}

bool mongocrypt_ctx_setopt_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id) {
    if (!ctx) {
        return false;
    }

    return _set_binary_opt(ctx, key_id, &ctx->opts.key_id, BSON_SUBTYPE_UUID);
}

bool mongocrypt_ctx_setopt_key_alt_name(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_alt_name) {
    bson_t as_bson;
    bson_iter_t iter;
    _mongocrypt_key_alt_name_t *new_key_alt_name;
    const char *key;

    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (!key_alt_name || !key_alt_name->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "option must be non-NULL");
    }

    if (!_mongocrypt_binary_to_bson(key_alt_name, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid keyAltName bson object");
    }

    if (!bson_iter_init(&iter, &as_bson) || !bson_iter_next(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid bson");
    }

    key = bson_iter_key(&iter);
    BSON_ASSERT(key);
    if (0 != strcmp(key, "keyAltName")) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "keyAltName must have field 'keyAltName'");
    }

    if (!BSON_ITER_HOLDS_UTF8(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "keyAltName expected to be UTF8");
    }

    new_key_alt_name = _mongocrypt_key_alt_name_new(bson_iter_value(&iter));

    if (ctx->opts.key_alt_names && _mongocrypt_key_alt_name_intersects(ctx->opts.key_alt_names, new_key_alt_name)) {
        _mongocrypt_key_alt_name_destroy_all(new_key_alt_name);
        return _mongocrypt_ctx_fail_w_msg(ctx, "duplicate keyAltNames found");
    }
    new_key_alt_name->next = ctx->opts.key_alt_names;
    ctx->opts.key_alt_names = new_key_alt_name;

    if (bson_iter_next(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "unrecognized field, only keyAltName expected");
    }

    return true;
}

bool mongocrypt_ctx_setopt_key_material(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_material) {
    bson_t as_bson;
    bson_iter_t iter;
    const char *key;
    _mongocrypt_buffer_t buffer;

    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->opts.key_material.owned) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "keyMaterial already set");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (!key_material || !key_material->data) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "option must be non-NULL");
    }

    if (!_mongocrypt_binary_to_bson(key_material, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid keyMaterial bson object");
    }

    /* TODO: use _mongocrypt_parse_required_binary once MONGOCRYPT-380 is
     * resolved.*/
    if (!bson_iter_init(&iter, &as_bson) || !bson_iter_next(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid bson");
    }

    key = bson_iter_key(&iter);
    BSON_ASSERT(key);
    if (0 != strcmp(key, "keyMaterial")) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "keyMaterial must have field 'keyMaterial'");
    }

    if (!_mongocrypt_buffer_from_binary_iter(&buffer, &iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "keyMaterial must be binary data");
    }

    if (buffer.len != MONGOCRYPT_KEY_LEN) {
        _mongocrypt_set_error(ctx->status,
                              MONGOCRYPT_STATUS_ERROR_CLIENT,
                              MONGOCRYPT_GENERIC_ERROR_CODE,
                              "keyMaterial should have length %d, but has length %" PRIu32,
                              MONGOCRYPT_KEY_LEN,
                              buffer.len);
        return _mongocrypt_ctx_fail(ctx);
    }

    _mongocrypt_buffer_steal(&ctx->opts.key_material, &buffer);

    if (bson_iter_next(&iter)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "unrecognized field, only keyMaterial expected");
    }

    return true;
}

bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorithm, int len) {
    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE || ctx->opts.index_type.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "already set algorithm");
    }

    if (len < -1) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid algorithm length");
    }

    if (!algorithm) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "passed null algorithm");
    }

    const size_t calculated_len = len == -1 ? strlen(algorithm) : (size_t)len;

    mstr_view algo_str = mstrv_view_data(algorithm, calculated_len);
    if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR))) {
        ctx->opts.algorithm = MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANDOM_STR))) {
        ctx->opts.algorithm = MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_INDEXED_STR))) {
        ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_EQUALITY;
        ctx->opts.index_type.set = true;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_UNINDEXED_STR))) {
        ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_NONE;
        ctx->opts.index_type.set = true;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGE_STR))) {
        ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_RANGE;
        ctx->opts.index_type.set = true;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR))) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Algorithm 'rangePreview' is deprecated, please use 'range'");
        return false;
    } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_STRING_STR))) {
        ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_STRING;
        ctx->opts.index_type.set = true;
    } else {
        char *error = bson_strdup_printf("unsupported algorithm string \"%.*s\"",
                                         algo_str.len <= (size_t)INT_MAX ? (int)algo_str.len : INT_MAX,
                                         algo_str.data);
        _mongocrypt_ctx_fail_w_msg(ctx, error);
        bson_free(error);
        return false;
    }

    return true;
}

mongocrypt_ctx_t *mongocrypt_ctx_new(mongocrypt_t *crypt) {
    mongocrypt_ctx_t *ctx;

    if (!crypt) {
        return NULL;
    }
    if (!crypt->initialized) {
        mongocrypt_status_t *status;

        status = crypt->status;
        CLIENT_ERR("cannot create context from uninitialized crypt");
        return NULL;
    }

    // Allocate with memory and alignment large enough for any possible context type.
    static const size_t ctx_alignment = MONGOCRYPT_CTX_ALLOC_ALIGNMENT;
    static const size_t ctx_size = MONGOCRYPT_CTX_ALLOC_SIZE;
    ctx = bson_aligned_alloc0(ctx_alignment, ctx_size);
    BSON_ASSERT(ctx);

    ctx->crypt = crypt;
    ctx->status = mongocrypt_status_new();
    ctx->opts.algorithm = MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE;
    ctx->opts.retry_enabled = crypt->retry_enabled;
    ctx->state = MONGOCRYPT_CTX_DONE;
    return ctx;
}

#define CHECK_AND_CALL(fn, ...)                                                                                        \
    do {                                                                                                               \
        if (!ctx->vtable.fn) {                                                                                         \
            return _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");                                       \
        }                                                                                                              \
        return ctx->vtable.fn(__VA_ARGS__);                                                                            \
    } while (0)

/* Common to both encrypt and decrypt context. */
static bool _mongo_op_keys(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(out);

    /* Construct the find filter to fetch keys. */
    if (!_mongocrypt_key_broker_filter(&ctx->kb, out)) {
        BSON_ASSERT(!_mongocrypt_key_broker_status(&ctx->kb, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }
    return true;
}

static bool _mongo_feed_keys(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
    _mongocrypt_buffer_t buf;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(in);

    _mongocrypt_buffer_from_binary(&buf, in);
    if (!_mongocrypt_key_broker_add_doc(&ctx->kb, _mongocrypt_ctx_kms_providers(ctx), &buf)) {
        BSON_ASSERT(!_mongocrypt_key_broker_status(&ctx->kb, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }
    return true;
}

static bool _mongo_done_keys(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    (void)_mongocrypt_key_broker_docs_done(&ctx->kb);
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

static mongocrypt_kms_ctx_t *_next_kms_ctx(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    return _mongocrypt_key_broker_next_kms(&ctx->kb);
}

static bool _kms_done(mongocrypt_ctx_t *ctx) {
    _mongocrypt_opts_kms_providers_t *kms_providers;

    BSON_ASSERT_PARAM(ctx);

    kms_providers = _mongocrypt_ctx_kms_providers(ctx);

    if (!_mongocrypt_key_broker_kms_done(&ctx->kb, kms_providers)) {
        BSON_ASSERT(!_mongocrypt_key_broker_status(&ctx->kb, ctx->status));
        return _mongocrypt_ctx_fail(ctx);
    }
    return _mongocrypt_ctx_state_from_key_broker(ctx);
}

bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    if (!ctx) {
        return false;
    }
    if (!ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
    }

    if (!out) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid NULL output");
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_op_collinfo, ctx, out);
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_op_markings, ctx, out);
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_op_keys, ctx, out);
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_KMS:
    case MONGOCRYPT_CTX_READY:
    default: return _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
    }
}

const char *mongocrypt_ctx_mongo_db(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return NULL;
    }
    if (!ctx->initialized) {
        _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
        return NULL;
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: {
        if (!ctx->vtable.mongo_db_collinfo) {
            _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");
            return NULL;
        }
        return ctx->vtable.mongo_db_collinfo(ctx);
    }
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_KMS:
    case MONGOCRYPT_CTX_READY:
    default: {
        _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
        return NULL;
    }
    }
}

bool mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *in) {
    if (!ctx) {
        return false;
    }
    if (!ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
    }

    if (!in) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid NULL input");
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_feed_collinfo, ctx, in);
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_feed_markings, ctx, in);
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_feed_keys, ctx, in);
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_KMS:
    case MONGOCRYPT_CTX_READY:
    default: return _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
    }
}

bool mongocrypt_ctx_mongo_done(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return false;
    }
    if (!ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: CHECK_AND_CALL(mongo_done_collinfo, ctx);
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: CHECK_AND_CALL(mongo_done_markings, ctx);
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: CHECK_AND_CALL(mongo_done_keys, ctx);
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_KMS:
    case MONGOCRYPT_CTX_READY:
    default: return _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
    }
}

mongocrypt_ctx_state_t mongocrypt_ctx_state(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return MONGOCRYPT_CTX_ERROR;
    }
    if (!ctx->initialized) {
        _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
        return MONGOCRYPT_CTX_ERROR;
    }

    return ctx->state;
}

mongocrypt_kms_ctx_t *mongocrypt_ctx_next_kms_ctx(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return NULL;
    }
    if (!ctx->initialized) {
        _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
        return NULL;
    }

    if (!ctx->vtable.next_kms_ctx) {
        _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");
        return NULL;
    }

    mongocrypt_kms_ctx_t *ret;
    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_KMS: ret = ctx->vtable.next_kms_ctx(ctx); break;
    case MONGOCRYPT_CTX_ERROR: return NULL;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
    case MONGOCRYPT_CTX_READY:
    default: _mongocrypt_ctx_fail_w_msg(ctx, "wrong state"); return NULL;
    }

    if (ret) {
        ret->retry_enabled = ctx->opts.retry_enabled;
    }
    return ret;
}

bool mongocrypt_ctx_provide_kms_providers(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *kms_providers_definition) {
    if (!ctx) {
        return false;
    }

    if (!ctx->initialized) {
        _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
        return false;
    }

    if (ctx->state != MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS) {
        _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
        return false;
    }

    if (!kms_providers_definition) {
        _mongocrypt_ctx_fail_w_msg(ctx, "KMS provider credential mapping not provided");
        return false;
    }

    _mongocrypt_opts_kms_providers_init(&ctx->per_ctx_kms_providers);

    if (!_mongocrypt_parse_kms_providers(kms_providers_definition,
                                         &ctx->per_ctx_kms_providers,
                                         ctx->status,
                                         &ctx->crypt->log)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    if (!_mongocrypt_opts_kms_providers_validate(&ctx->crypt->opts, &ctx->per_ctx_kms_providers, ctx->status)) {
        /* Remove the parsed KMS providers if they are invalid */
        _mongocrypt_opts_kms_providers_cleanup(&ctx->per_ctx_kms_providers);
        memset(&ctx->per_ctx_kms_providers, 0, sizeof(ctx->per_ctx_kms_providers));
        return _mongocrypt_ctx_fail(ctx);
    }

    memcpy(&ctx->kms_providers, &ctx->crypt->opts.kms_providers, sizeof(_mongocrypt_opts_kms_providers_t));
    _mongocrypt_opts_merge_kms_providers(&ctx->kms_providers, &ctx->per_ctx_kms_providers);

    ctx->state = ctx->kb.state == KB_ADDING_DOCS ? MONGOCRYPT_CTX_NEED_MONGO_KEYS : MONGOCRYPT_CTX_NEED_KMS;
    if (ctx->vtable.after_kms_credentials_provided) {
        return ctx->vtable.after_kms_credentials_provided(ctx);
    }
    return true;
}

bool mongocrypt_ctx_kms_done(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return false;
    }
    if (!ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
    }

    if (!ctx->vtable.kms_done) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_NEED_KMS: return ctx->vtable.kms_done(ctx);
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
    case MONGOCRYPT_CTX_READY:
    default: return _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
    }
}

bool mongocrypt_ctx_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out) {
    if (!ctx) {
        return false;
    }
    if (!ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "ctx NULL or uninitialized");
    }

    if (!out) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid NULL output");
    }

    if (!ctx->vtable.finalize) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "not applicable to context");
    }

    switch (ctx->state) {
    case MONGOCRYPT_CTX_READY: return ctx->vtable.finalize(ctx, out);
    case MONGOCRYPT_CTX_ERROR: return false;
    case MONGOCRYPT_CTX_DONE:
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
    case MONGOCRYPT_CTX_NEED_KMS:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
    default: return _mongocrypt_ctx_fail_w_msg(ctx, "wrong state");
    }
}

bool mongocrypt_ctx_status(mongocrypt_ctx_t *ctx, mongocrypt_status_t *out) {
    if (!ctx) {
        return false;
    }

    if (!out) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid NULL output");
    }

    if (!mongocrypt_status_ok(ctx->status)) {
        _mongocrypt_status_copy_to(ctx->status, out);
        return false;
    }
    _mongocrypt_status_reset(out);
    return true;
}

void mongocrypt_ctx_destroy(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return;
    }

    if (ctx->vtable.cleanup) {
        ctx->vtable.cleanup(ctx);
    }

    mc_RangeOpts_cleanup(&ctx->opts.rangeopts.value);
    _mongocrypt_opts_kms_providers_cleanup(&ctx->per_ctx_kms_providers);
    _mongocrypt_kek_cleanup(&ctx->opts.kek);
    mongocrypt_status_destroy(ctx->status);
    _mongocrypt_key_broker_cleanup(&ctx->kb);
    _mongocrypt_buffer_cleanup(&ctx->opts.key_material);
    _mongocrypt_key_alt_name_destroy_all(ctx->opts.key_alt_names);
    _mongocrypt_buffer_cleanup(&ctx->opts.key_id);
    _mongocrypt_buffer_cleanup(&ctx->opts.index_key_id);
    bson_free(ctx);
    return;
}

bool mongocrypt_ctx_setopt_masterkey_aws(mongocrypt_ctx_t *ctx,
                                         const char *region,
                                         int32_t region_len,
                                         const char *cmk,
                                         int32_t cmk_len) {
    mongocrypt_binary_t *bin;
    bson_t as_bson;
    bool ret;
    char *temp = NULL;

    if (!ctx) {
        return false;
    }
    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_AWS
        && ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "master key already set");
    }

    if (ctx->opts.kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS && ctx->opts.kek.provider.aws.region) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "master key already set");
    }

    if (!_mongocrypt_validate_and_copy_string(region, region_len, &temp) || region_len == 0) {
        bson_free(temp);
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid region");
    }
    bson_free(temp);

    temp = NULL;
    if (!_mongocrypt_validate_and_copy_string(cmk, cmk_len, &temp) || cmk_len == 0) {
        bson_free(temp);
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid cmk");
    }
    bson_free(temp);

    bson_init(&as_bson);
    bson_append_utf8(&as_bson, MONGOCRYPT_STR_AND_LEN("provider"), MONGOCRYPT_STR_AND_LEN("aws"));
    BSON_ASSERT(region_len <= INT_MAX);
    bson_append_utf8(&as_bson, MONGOCRYPT_STR_AND_LEN("region"), region, region_len);
    BSON_ASSERT(cmk_len <= INT_MAX);
    bson_append_utf8(&as_bson, MONGOCRYPT_STR_AND_LEN("key"), cmk, cmk_len);
    bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&as_bson), as_bson.len);

    ret = mongocrypt_ctx_setopt_key_encryption_key(ctx, bin);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&as_bson);

    return ret;
}

bool mongocrypt_ctx_setopt_masterkey_local(mongocrypt_ctx_t *ctx) {
    if (!ctx) {
        return false;
    }
    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.kek.kms_provider) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "master key already set");
    }

    ctx->opts.kek.kms_provider = MONGOCRYPT_KMS_PROVIDER_LOCAL;
    ctx->opts.kek.kmsid = bson_strdup("local");
    return true;
}

bool _mongocrypt_ctx_init(mongocrypt_ctx_t *ctx, _mongocrypt_ctx_opts_spec_t *opts_spec) {
    bool has_id = false, has_alt_name = false, has_multiple_alt_names = false;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(opts_spec);

    // This condition is guarded in setopt_algorithm:
    BSON_ASSERT(!(ctx->opts.index_type.set && ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE)
                && "Both an encryption algorithm and an index_type were set.");

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot double initialize");
    }
    ctx->initialized = true;

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }
    /* Set some default functions */
    ctx->vtable.mongo_op_keys = _mongo_op_keys;
    ctx->vtable.mongo_feed_keys = _mongo_feed_keys;
    ctx->vtable.mongo_done_keys = _mongo_done_keys;
    ctx->vtable.next_kms_ctx = _next_kms_ctx;
    ctx->vtable.kms_done = _kms_done;

    /* Check that required options are included and prohibited options are
     * not.
     */

    if (opts_spec->kek == OPT_REQUIRED) {
        if (!ctx->opts.kek.kms_provider) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "master key required");
        }
        mc_kms_creds_t unused;
        bool is_configured =
            _mongocrypt_opts_kms_providers_lookup(_mongocrypt_ctx_kms_providers(ctx), ctx->opts.kek.kmsid, &unused);
        if (!ctx->crypt->opts.use_need_kms_credentials_state && !is_configured) {
            mongocrypt_status_t *status = ctx->status;
            CLIENT_ERR("requested kms provider not configured: `%s`", ctx->opts.kek.kmsid);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    if (opts_spec->kek == OPT_PROHIBITED && ctx->opts.kek.kms_provider) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "master key prohibited");
    }

    /* Check that the kms provider required by the datakey is configured.  */
    if (ctx->opts.kek.kms_provider) {
        mc_kms_creds_t unused;
        bool is_configured =
            _mongocrypt_opts_kms_providers_lookup(_mongocrypt_ctx_kms_providers(ctx), ctx->opts.kek.kmsid, &unused);
        bool needs = _mongocrypt_needs_credentials_for_provider(ctx->crypt,
                                                                ctx->opts.kek.kms_provider,
                                                                ctx->opts.kek.kmsid_name);
        if (!is_configured && !needs) {
            mongocrypt_status_t *status = ctx->status;
            CLIENT_ERR("requested kms provider required by datakey is not configured: `%s`", ctx->opts.kek.kmsid);
            return _mongocrypt_ctx_fail(ctx);
        }
    }

    /* Special case. key_descriptor applies to explicit encryption. It must be
     * either a key id or *one* key alt name, but not both.
     * key_alt_names applies to creating a data key. It may be one or multiple
     * key alt names.
     */
    has_id = !_mongocrypt_buffer_empty(&ctx->opts.key_id);
    has_alt_name = !!(ctx->opts.key_alt_names);
    has_multiple_alt_names = has_alt_name && !!(ctx->opts.key_alt_names->next);

    if (opts_spec->key_descriptor == OPT_REQUIRED) {
        if (!has_id && !has_alt_name) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "either key id or key alt name required");
        }

        if (has_id && has_alt_name) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "cannot have both key id and key alt name");
        }

        if (has_multiple_alt_names) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "must not specify multiple key alt names");
        }
    }

    if (opts_spec->key_descriptor == OPT_PROHIBITED) {
        /* still okay if key_alt_names are allowed and only alt names were
         * specified. */
        if ((opts_spec->key_alt_names == OPT_PROHIBITED && has_alt_name) || has_id) {
            return _mongocrypt_ctx_fail_w_msg(ctx, "key id and alt name prohibited");
        }
    }

    if (opts_spec->key_material == OPT_PROHIBITED && ctx->opts.key_material.owned) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "key material prohibited");
    }

    if (opts_spec->algorithm == OPT_REQUIRED && ctx->opts.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "algorithm required");
    }

    if (opts_spec->algorithm == OPT_PROHIBITED && ctx->opts.algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "algorithm prohibited");
    }

    if (opts_spec->rangeopts == OPT_PROHIBITED && ctx->opts.rangeopts.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "range opts are prohibited on this context");
    }

    _mongocrypt_key_broker_init(&ctx->kb, ctx->crypt);
    return true;
}

bool _mongocrypt_ctx_state_from_key_broker(mongocrypt_ctx_t *ctx) {
    _mongocrypt_key_broker_t *kb;
    mongocrypt_status_t *status;
    mongocrypt_ctx_state_t new_state = MONGOCRYPT_CTX_ERROR;
    bool ret = false;

    BSON_ASSERT_PARAM(ctx);

    status = ctx->status;
    kb = &ctx->kb;

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    switch (kb->state) {
    case KB_ERROR:
        _mongocrypt_status_copy_to(kb->status, status);
        new_state = MONGOCRYPT_CTX_ERROR;
        ret = false;
        break;
    case KB_ADDING_DOCS:
        /* Encrypted keys need KMS, which need to be provided before
         * adding docs. */
        if (_mongocrypt_needs_credentials(ctx->crypt)) {
            new_state = MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS;
        } else {
            /* Require key documents from driver. */
            new_state = MONGOCRYPT_CTX_NEED_MONGO_KEYS;
        }
        ret = true;
        break;
    case KB_ADDING_DOCS_ANY:
        /* Assume KMS credentials have been provided. */
        new_state = MONGOCRYPT_CTX_NEED_MONGO_KEYS;
        ret = true;
        break;
    case KB_AUTHENTICATING:
    case KB_DECRYPTING_KEY_MATERIAL:
        new_state = MONGOCRYPT_CTX_NEED_KMS;
        ret = true;
        break;
    case KB_DONE:
        new_state = MONGOCRYPT_CTX_READY;
        if (kb->key_requests == NULL) {
            /* No key requests were ever added. */
            ctx->nothing_to_do = true; /* nothing to encrypt/decrypt */
        }
        ret = true;
        break;
    /* As currently implemented, we do not expect to ever be in KB_REQUESTING
     * or KB_REQUESTING_ANY state when calling this function. */
    case KB_REQUESTING:
    default:
        CLIENT_ERR("key broker in unexpected state");
        new_state = MONGOCRYPT_CTX_ERROR;
        ret = false;
        break;
    }

    if (new_state != ctx->state) {
        ctx->state = new_state;
    }

    return ret;
}

bool mongocrypt_ctx_setopt_masterkey_aws_endpoint(mongocrypt_ctx_t *ctx, const char *endpoint, int32_t endpoint_len) {
    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_AWS
        && ctx->opts.kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_NONE) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "endpoint prohibited");
    }

    ctx->opts.kek.kms_provider = MONGOCRYPT_KMS_PROVIDER_AWS;

    if (ctx->opts.kek.provider.aws.endpoint) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "already set masterkey endpoint");
    }

    ctx->opts.kek.provider.aws.endpoint =
        _mongocrypt_endpoint_new(endpoint, endpoint_len, NULL /* opts */, ctx->status);
    if (!ctx->opts.kek.provider.aws.endpoint) {
        return _mongocrypt_ctx_fail(ctx);
    }

    return true;
}

bool mongocrypt_ctx_setopt_key_encryption_key(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *bin) {
    bson_t as_bson;

    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.kek.kms_provider) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "key encryption key already set");
    }

    if (!bin) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid NULL key encryption key document");
    }

    if (!_mongocrypt_binary_to_bson(bin, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid BSON");
    }

    if (!_mongocrypt_kek_parse_owned(&as_bson, &ctx->opts.kek, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    return true;
}

_mongocrypt_opts_kms_providers_t *_mongocrypt_ctx_kms_providers(mongocrypt_ctx_t *ctx) {
    BSON_ASSERT_PARAM(ctx);

    return ctx->kms_providers.configured_providers ? &ctx->kms_providers : &ctx->crypt->opts.kms_providers;
}

bool mongocrypt_ctx_setopt_contention_factor(mongocrypt_ctx_t *ctx, int64_t contention_factor) {
    if (!ctx) {
        return false;
    }
    ctx->opts.contention_factor.value = contention_factor;
    ctx->opts.contention_factor.set = true;
    return true;
}

bool mongocrypt_ctx_setopt_index_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id) {
    if (!ctx) {
        return false;
    }

    return _set_binary_opt(ctx, key_id, &ctx->opts.index_key_id, BSON_SUBTYPE_UUID);
}

bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_type, int len) {
    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "Cannot set options after init");
    }
    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }
    if (len < -1) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "Invalid query_type string length");
    }
    if (!query_type) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "Invalid null query_type string");
    }

    const size_t calc_len = len == -1 ? strlen(query_type) : (size_t)len;
    mstr_view qt_str = mstrv_view_data(query_type, calc_len);
    if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR))) {
        ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_EQUALITY;
        ctx->opts.query_type.set = true;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGE_STR))) {
        ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_RANGE;
        ctx->opts.query_type.set = true;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR))) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'rangePreview' is deprecated, please use 'range'");
        return false;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIX_STR))) {
        ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_PREFIX;
        ctx->opts.query_type.set = true;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIX_STR))) {
        ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_SUFFIX;
        ctx->opts.query_type.set = true;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR))) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'prefixPreview' is deprecated, please use 'prefix'");
        return false;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR))) {
        _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'suffixPreview' is deprecated, please use 'suffix'");
        return false;
    } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR))) {
        ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW;
        ctx->opts.query_type.set = true;
    } else {
        /* don't check if qt_str.len fits in int; we want the diagnostic output */
        char *error = bson_strdup_printf("Unsupported query_type \"%.*s\"",
                                         qt_str.len <= (size_t)INT_MAX ? (int)qt_str.len : INT_MAX,
                                         qt_str.data);
        _mongocrypt_ctx_fail_w_msg(ctx, error);
        bson_free(error);
        return false;
    }
    return true;
}

const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val) {
    switch (val) {
    case MONGOCRYPT_INDEX_TYPE_NONE: return "None";
    case MONGOCRYPT_INDEX_TYPE_EQUALITY: return "Equality";
    case MONGOCRYPT_INDEX_TYPE_RANGE: return "Range";
    case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview";
    case MONGOCRYPT_INDEX_TYPE_STRING: return "String";
    default: return "Unknown";
    }
}

const char *_mongocrypt_query_type_to_string(mongocrypt_query_type_t val) {
    switch (val) {
    case MONGOCRYPT_QUERY_TYPE_EQUALITY: return "Equality";
    case MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview";
    case MONGOCRYPT_QUERY_TYPE_RANGE: return "Range";
    case MONGOCRYPT_QUERY_TYPE_PREFIX: return "Prefix";
    case MONGOCRYPT_QUERY_TYPE_SUFFIX: return "Suffix";
    case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW: return "SubstringPreview";
    default: return "Unknown";
    }
}

bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts) {
    bson_t as_bson;

    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.rangeopts.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "RangeOpts already set");
    }

    if (!_mongocrypt_binary_to_bson(opts, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid BSON");
    }

    if (!mc_RangeOpts_parse(&ctx->opts.rangeopts.value, &as_bson, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }

    ctx->opts.rangeopts.set = true;
    return true;
}

bool mongocrypt_ctx_setopt_algorithm_text(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts) {
    bson_t as_bson;
    if (!ctx) {
        return false;
    }

    if (ctx->initialized) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set options after init");
    }

    if (ctx->state == MONGOCRYPT_CTX_ERROR) {
        return false;
    }

    if (ctx->opts.textopts.set) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "TextOpts already set");
    }

    if (!_mongocrypt_binary_to_bson(opts, &as_bson)) {
        return _mongocrypt_ctx_fail_w_msg(ctx, "invalid BSON");
    }

    if (!mc_TextOpts_parse(&ctx->opts.textopts.value, &as_bson, ctx->status)) {
        return _mongocrypt_ctx_fail(ctx);
    }
    ctx->opts.textopts.set = true;
    return true;
}
libmongocrypt-1.19.0/src/mongocrypt-dll-private.h000066400000000000000000000074471521103432300220620ustar00rootroot00000000000000#ifndef MONGOCRYPT_DLL_PRIVATE_H
#define MONGOCRYPT_DLL_PRIVATE_H

#include 
#include 

#include 

#if defined(_WIN32)
#define MCR_DLL_SUFFIX ".dll"
#elif defined(__APPLE__)
#define MCR_DLL_SUFFIX ".dylib"
#else
#define MCR_DLL_SUFFIX ".so"
#endif

#define MCR_DLL_NULL ((mcr_dll){._native_handle = NULL, .error_string = MSTR_NULL})

/**
 * @brief A dynamically-loaded library i.e. returned by LoadLibrary() or
 * dlopen()
 */
typedef struct mcr_dll {
    // (All supported platforms use a void* as the library handle type)
    void *_native_handle;
    mstr error_string;
} mcr_dll;

/**
 * @brief Open and load a dynamic library
 *
 * @param lib A path or name of a library, suitable for encoding as a
 * filepath on the host system.
 *
 * @return mcr_dll A newly opened dynamic library, which must be
 * released using @ref mcr_dll_close()
 *
 * If the given `lib` is a qualified path (either relative or absolute) and not
 * just a filename, then the system will search for the library on the system's
 * default library search paths. If `lib` is a qualified relative path (not just
 * a filename), it will be resolved relative to the application's working
 * directory.
 */
mcr_dll mcr_dll_open(const char *lib);

/**
 * @brief Close a dynamic library opened with @ref mcr_dll_open
 *
 * @param dll A dynamic library handle
 */
static inline void mcr_dll_close(mcr_dll dll) {
    extern void mcr_dll_close_handle(mcr_dll);
    mcr_dll_close_handle(dll);
    mstr_free(dll.error_string);
}

// All currently supported platforms require casting to/from `void*` for dynamically loaded function symbols by
// necessity despite being undefined behavior according to the C standard specification.
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
// GCC requires silencing `-Wpedantic` for this cast.
#define MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE                                                                      \
    _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
#define MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE _Pragma("GCC diagnostic pop")
#elif defined(__clang__)
#define MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE                                                                      \
    _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wunknown-warning-option\"")                  \
        _Pragma("clang diagnostic ignored \"-Wcast-function-type-strict\"")
#define MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE _Pragma("clang diagnostic pop")
#else
#define MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE
#define MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE
#endif

/**
 * @brief Obtain a pointer to an exported entity from the given dynamic library.
 *
 * @param dll A library opened with @ref mcr_dll_open
 * @param symbol The name of a symbol to open
 * @return void* A pointer to that symbol, or NULL if not found
 */
void *mcr_dll_sym(mcr_dll dll, const char *symbol);

/**
 * @brief Determine whether the given DLL is a handle to an open library
 */
static inline bool mcr_dll_is_open(mcr_dll dll) {
    return dll._native_handle != NULL;
}

typedef struct mcr_dll_path_result {
    mstr path;
    mstr error_string;
} mcr_dll_path_result;

/**
 * @brief Obtain a filepath to the given loaded DLL
 *
 * @param dll The library loaded to inspect
 * @return mcr_dll_path_result A result containing the absolute path to a
 * library, or an error string.
 *
 * @note Caller must free both `retval.path` and `retval.error_string`.
 * @note Returns an error if not supported on this platform. Use
 * `mcr_dll_path_supported` to check before calling.
 */
mcr_dll_path_result mcr_dll_path(mcr_dll dll);

/**
 * @brief Return true if `mcr_dll_path` is supported on this platform.
 */
bool mcr_dll_path_supported(void);

#endif // MONGOCRYPT_DLL_PRIVATE_H
libmongocrypt-1.19.0/src/mongocrypt-endian-private.h000066400000000000000000000126171521103432300225400ustar00rootroot00000000000000/*
 * Copyright 2022-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.
 */

/* This file is copied and modified from kms_message kms_endian.h. */

#ifndef MONGOCRYPT_ENDIAN_PRIVATE_H
#define MONGOCRYPT_ENDIAN_PRIVATE_H

#include 
#include 

/* Define a fallback for __has_builtin for compatibility with non-clang
 * compilers. */
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif

#if defined(__clang__) && __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32)                         \
    && __has_builtin(__builtin_bswap64)
#define MONGOCRYPT_UINT16_SWAP_LE_BE(v) __builtin_bswap16(v)
#define MONGOCRYPT_UINT32_SWAP_LE_BE(v) __builtin_bswap32(v)
#define MONGOCRYPT_UINT64_SWAP_LE_BE(v) __builtin_bswap64(v)
#elif defined(__GNUC__) && (__GNUC__ >= 4)
#if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 3)
#define MONGOCRYPT_UINT32_SWAP_LE_BE(v) __builtin_bswap32((uint32_t)v)
#define MONGOCRYPT_UINT64_SWAP_LE_BE(v) __builtin_bswap64((uint64_t)v)
#endif
#if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 8)
#define MONGOCRYPT_UINT16_SWAP_LE_BE(v) __builtin_bswap16((uint32_t)v)
#endif
#endif

#ifndef MONGOCRYPT_UINT16_SWAP_LE_BE
#define MONGOCRYPT_UINT16_SWAP_LE_BE(v) __mongocrypt_uint16_swap_slow((uint16_t)v)
#endif

#ifndef MONGOCRYPT_UINT32_SWAP_LE_BE
#define MONGOCRYPT_UINT32_SWAP_LE_BE(v) __mongocrypt_uint32_swap_slow((uint32_t)v)
#endif

#ifndef MONGOCRYPT_UINT64_SWAP_LE_BE
#define MONGOCRYPT_UINT64_SWAP_LE_BE(v) __mongocrypt_uint64_swap_slow((uint64_t)v)
#endif

#if defined(MONGOCRYPT_LITTLE_ENDIAN)
#define MONGOCRYPT_UINT16_FROM_LE(v) ((uint16_t)v)
#define MONGOCRYPT_UINT16_TO_LE(v) ((uint16_t)v)
#define MONGOCRYPT_UINT16_FROM_BE(v) MONGOCRYPT_UINT16_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT16_TO_BE(v) MONGOCRYPT_UINT16_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT32_FROM_LE(v) ((uint32_t)v)
#define MONGOCRYPT_UINT32_TO_LE(v) ((uint32_t)v)
#define MONGOCRYPT_UINT32_FROM_BE(v) MONGOCRYPT_UINT32_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT32_TO_BE(v) MONGOCRYPT_UINT32_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT64_FROM_LE(v) ((uint64_t)v)
#define MONGOCRYPT_UINT64_TO_LE(v) ((uint64_t)v)
#define MONGOCRYPT_UINT64_FROM_BE(v) MONGOCRYPT_UINT64_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT64_TO_BE(v) MONGOCRYPT_UINT64_SWAP_LE_BE(v)
#elif defined(MONGOCRYPT_BIG_ENDIAN)
#define MONGOCRYPT_UINT16_FROM_LE(v) MONGOCRYPT_UINT16_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT16_TO_LE(v) MONGOCRYPT_UINT16_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT16_FROM_BE(v) ((uint16_t)v)
#define MONGOCRYPT_UINT16_TO_BE(v) ((uint16_t)v)
#define MONGOCRYPT_UINT32_FROM_LE(v) MONGOCRYPT_UINT32_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT32_TO_LE(v) MONGOCRYPT_UINT32_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT32_FROM_BE(v) ((uint32_t)v)
#define MONGOCRYPT_UINT32_TO_BE(v) ((uint32_t)v)
#define MONGOCRYPT_UINT64_FROM_LE(v) MONGOCRYPT_UINT64_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT64_TO_LE(v) MONGOCRYPT_UINT64_SWAP_LE_BE(v)
#define MONGOCRYPT_UINT64_FROM_BE(v) ((uint64_t)v)
#define MONGOCRYPT_UINT64_TO_BE(v) ((uint64_t)v)
#else
#error "The endianness of target architecture is unknown."
#endif

/*
 *--------------------------------------------------------------------------
 *
 * __mongocrypt_uint16_swap_slow --
 *
 *       Fallback endianness conversion for 16-bit integers.
 *
 * Returns:
 *       The endian swapped version.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

static inline uint16_t __mongocrypt_uint16_swap_slow(uint16_t v) /* IN */
{
    return (uint16_t)((v & 0x00FF) << 8) | (uint16_t)((v & 0xFF00) >> 8);
}

/*
 *--------------------------------------------------------------------------
 *
 * __mongocrypt_uint32_swap_slow --
 *
 *       Fallback endianness conversion for 32-bit integers.
 *
 * Returns:
 *       The endian swapped version.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

static inline uint32_t __mongocrypt_uint32_swap_slow(uint32_t v) /* IN */
{
    return ((v & 0x000000FFU) << 24) | ((v & 0x0000FF00U) << 8) | ((v & 0x00FF0000U) >> 8) | ((v & 0xFF000000U) >> 24);
}

/*
 *--------------------------------------------------------------------------
 *
 * __mongocrypt_uint64_swap_slow --
 *
 *       Fallback endianness conversion for 64-bit integers.
 *
 * Returns:
 *       The endian swapped version.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

static inline uint64_t __mongocrypt_uint64_swap_slow(uint64_t v) /* IN */
{
    return ((v & 0x00000000000000FFULL) << 56) | ((v & 0x000000000000FF00ULL) << 40)
         | ((v & 0x0000000000FF0000ULL) << 24) | ((v & 0x00000000FF000000ULL) << 8) | ((v & 0x000000FF00000000ULL) >> 8)
         | ((v & 0x0000FF0000000000ULL) >> 24) | ((v & 0x00FF000000000000ULL) >> 40)
         | ((v & 0xFF00000000000000ULL) >> 56);
}

#endif /* MONGOCRYPT_ENDIAN_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-endpoint-private.h000066400000000000000000000046221521103432300231170ustar00rootroot00000000000000/*
 * Copyright 2020-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.
 */

#ifndef MONGOCRYPT_ENDPOINT_PRIVATE_H
#define MONGOCRYPT_ENDPOINT_PRIVATE_H

#include "mongocrypt-status-private.h"

typedef struct {
    /* e.g. https://kevin.keyvault.azure.net:443/path/path/?query=value */
    char *original;
    char *protocol;  /* e.g. https */
    char *host;      /* e.g. kevin.keyvault.azure.net */
    char *port;      /* e.g. 443 */
    char *domain;    /* e.g. keyvault.azure.net */
    char *subdomain; /* e.g. kevin */
    char *path;      /* e.g. path/path */
    char *query;     /* e.g. query=value */
    /* host_and_port is the form that should be returned to drivers. */
    char *host_and_port; /* e.g. kevin.keyvault.azure.net:443 */
} _mongocrypt_endpoint_t;

_mongocrypt_endpoint_t *_mongocrypt_endpoint_copy(_mongocrypt_endpoint_t *src);

void _mongocrypt_endpoint_destroy(_mongocrypt_endpoint_t *endpoint);

typedef struct {
    /* allow_empty_subdomain does not require "host" to contain dot separators.
     * If allow_empty_subdomain is true, then "localhost" is a valid endpoint. */
    bool allow_empty_subdomain;
} _mongocrypt_endpoint_parse_opts_t;

/* Parses a subset of URIs of the form:
 * [protocol://][host[:port]][path][?query]
 */
_mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
                                                 int32_t len,
                                                 _mongocrypt_endpoint_parse_opts_t *opts,
                                                 mongocrypt_status_t *status);

/* _mongocrypt_apply_default_port checks if the endpoint string *endpoint_raw
 * contains a port. If *endpoint_raw does not contain a port, *endpoint_raw is
 * freed and overwritten to a copy of *endpoint_raw with ":" appended.
 */
void _mongocrypt_apply_default_port(char **endpoint_raw, char *port);

#endif /* MONGOCRYPT_ENDPOINT_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-endpoint.c000066400000000000000000000136311521103432300214420ustar00rootroot00000000000000/*
 * Copyright 2020-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-endpoint-private.h"

#include "mongocrypt-private.h"

void _mongocrypt_endpoint_destroy(_mongocrypt_endpoint_t *endpoint) {
    if (!endpoint) {
        return;
    }
    bson_free(endpoint->original);
    bson_free(endpoint->protocol);
    bson_free(endpoint->host);
    bson_free(endpoint->port);
    bson_free(endpoint->domain);
    bson_free(endpoint->subdomain);
    bson_free(endpoint->path);
    bson_free(endpoint->query);
    bson_free(endpoint->host_and_port);
    bson_free(endpoint);
}

/* Parses a subset of URIs of the form:
 * [protocol://][host[:port]][path][?query]
 */
_mongocrypt_endpoint_t *_mongocrypt_endpoint_new(const char *endpoint_raw,
                                                 int32_t len,
                                                 _mongocrypt_endpoint_parse_opts_t *opts,
                                                 mongocrypt_status_t *status) {
    _mongocrypt_endpoint_t *endpoint;
    bool ok = false;
    char *pos;
    char *prev;
    char *colon;
    char *qmark;
    char *slash;
    char *host_start;
    char *host_end;

    /* opts is checked where it is used below, to allow a more precise error */

    endpoint = bson_malloc0(sizeof(_mongocrypt_endpoint_t));
    _mongocrypt_status_reset(status);
    BSON_ASSERT(endpoint);
    if (!_mongocrypt_validate_and_copy_string(endpoint_raw, len, &endpoint->original)) {
        CLIENT_ERR("Invalid endpoint");
        goto fail;
    }

    /* Parse optional protocol. */
    pos = strstr(endpoint->original, "://");
    if (pos) {
        endpoint->protocol = bson_strndup(endpoint->original, (size_t)(pos - endpoint->original));
        pos += 3;
    } else {
        pos = endpoint->original;
    }
    host_start = pos;

    /* Parse subdomain. */
    prev = pos;
    pos = strstr(pos, ".");
    if (pos) {
        BSON_ASSERT(pos >= prev);
        endpoint->subdomain = bson_strndup(prev, (size_t)(pos - prev));
        pos += 1;
    } else {
        if (!opts || !opts->allow_empty_subdomain) {
            CLIENT_ERR("Invalid endpoint, expected dot separator in host, but got: %s", endpoint->original);
            goto fail;
        }
        /* OK, reset pos to the start of the host. */
        pos = prev;
    }

    /* Parse domain. */
    prev = pos;
    colon = strstr(pos, ":");
    qmark = strstr(pos, "?");
    slash = strstr(pos, "/");
    if (colon) {
        host_end = colon;
    } else if (slash) {
        host_end = slash;
    } else if (qmark) {
        host_end = qmark;
    } else {
        host_end = NULL;
    }

    if (host_end) {
        BSON_ASSERT(host_end >= prev);
        endpoint->domain = bson_strndup(prev, (size_t)(host_end - prev));
        BSON_ASSERT(host_end >= host_start);
        endpoint->host = bson_strndup(host_start, (size_t)(host_end - host_start));
    } else {
        endpoint->domain = bson_strdup(prev);
        endpoint->host = bson_strdup(host_start);
    }

    /* Parse optional port */
    if (colon) {
        prev = colon + 1;
        qmark = strstr(pos, "?");
        slash = strstr(pos, "/");
        if (slash) {
            endpoint->port = bson_strndup(prev, (size_t)(slash - prev));
        } else if (qmark) {
            BSON_ASSERT(qmark >= prev);
            endpoint->port = bson_strndup(prev, (size_t)(qmark - prev));
        } else {
            endpoint->port = bson_strdup(prev);
        }
    }

    /* Parse optional path */
    if (slash) {
        size_t path_len;

        prev = slash + 1;
        qmark = strstr(prev, "?");
        if (qmark) {
            endpoint->path = bson_strndup(prev, (size_t)(qmark - prev));
        } else {
            endpoint->path = bson_strdup(prev);
        }

        path_len = strlen(endpoint->path);
        /* Clear a trailing slash if it exists. */
        if (path_len > 0 && endpoint->path[path_len - 1] == '/') {
            endpoint->path[path_len - 1] = '\0';
        }
    }

    /* Parse optional query */
    if (qmark) {
        endpoint->query = bson_strdup(qmark + 1);
    }

    if (endpoint->port) {
        endpoint->host_and_port = bson_strdup_printf("%s:%s", endpoint->host, endpoint->port);
    } else {
        endpoint->host_and_port = bson_strdup(endpoint->host);
    }

    ok = true;
fail:
    if (!ok) {
        _mongocrypt_endpoint_destroy(endpoint);
        return NULL;
    }
    return endpoint;
}

_mongocrypt_endpoint_t *_mongocrypt_endpoint_copy(_mongocrypt_endpoint_t *src) {
    _mongocrypt_endpoint_t *endpoint;

    if (!src) {
        return NULL;
    }
    endpoint = bson_malloc0(sizeof(_mongocrypt_endpoint_t));
    endpoint->original = bson_strdup(src->original);
    endpoint->protocol = bson_strdup(src->protocol);
    endpoint->host = bson_strdup(src->host);
    endpoint->port = bson_strdup(src->port);
    endpoint->domain = bson_strdup(src->domain);
    endpoint->subdomain = bson_strdup(src->subdomain);
    endpoint->path = bson_strdup(src->path);
    endpoint->query = bson_strdup(src->query);
    endpoint->host_and_port = bson_strdup(src->host_and_port);
    return endpoint;
}

void _mongocrypt_apply_default_port(char **endpoint_raw, char *port) {
    BSON_ASSERT_PARAM(endpoint_raw);
    BSON_ASSERT_PARAM(port);
    BSON_ASSERT(*endpoint_raw);

    if (strstr(*endpoint_raw, ":") == NULL) {
        char *tmp = *endpoint_raw;
        *endpoint_raw = bson_strdup_printf("%s:%s", *endpoint_raw, port);
        bson_free(tmp);
    }
}
libmongocrypt-1.19.0/src/mongocrypt-kek-private.h000066400000000000000000000065201521103432300220500ustar00rootroot00000000000000/*
 * Copyright 2020-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.
 */

#ifndef MONGOCRYPT_KEK_PRIVATE_H
#define MONGOCRYPT_KEK_PRIVATE_H

#include 

#include "mongocrypt-endpoint-private.h"
#include "mongocrypt.h"

/* Defines structs for Key Encryption Keys (KEKs)
 * The KEK is used as part of envelope encryption. It encrypts a Data Encryption
 * Key (DEK). A KEK is specified when creating a new data encryption key, or
 * when parsing a key document from the key vault.
 */

/* KMS providers are used in a bit set.
 *
 * Check for set membership using bitwise and:
 *   int kms_set = fn();
 *   if (kms_set & MONGOCRYPT_KMS_PROVIDER_AWS)
 * Add to a set using bitwise or:
 *   kms_set |= MONGOCRYPT_KMS_PROVIDER_LOCAL
 */
typedef enum {
    MONGOCRYPT_KMS_PROVIDER_NONE = 0,
    MONGOCRYPT_KMS_PROVIDER_AWS = 1 << 0,
    MONGOCRYPT_KMS_PROVIDER_LOCAL = 1 << 1,
    MONGOCRYPT_KMS_PROVIDER_AZURE = 1 << 2,
    MONGOCRYPT_KMS_PROVIDER_GCP = 1 << 3,
    MONGOCRYPT_KMS_PROVIDER_KMIP = 1 << 4
} _mongocrypt_kms_provider_t;

typedef struct {
    _mongocrypt_endpoint_t *key_vault_endpoint;
    char *key_name;
    char *key_version;
} _mongocrypt_azure_kek_t;

typedef struct {
    char *project_id;
    char *location;
    char *key_ring;
    char *key_name;
    char *key_version;                /* optional */
    _mongocrypt_endpoint_t *endpoint; /* optional. */
} _mongocrypt_gcp_kek_t;

typedef struct {
    char *region;
    char *cmk;
    _mongocrypt_endpoint_t *endpoint; /* optional. */
} _mongocrypt_aws_kek_t;

typedef struct {
    char *key_id;                     /* optional on parsing, required on appending. */
    _mongocrypt_endpoint_t *endpoint; /* optional. */
    bool delegated;
} _mongocrypt_kmip_kek_t;

typedef struct {
    _mongocrypt_kms_provider_t kms_provider;

    union {
        _mongocrypt_azure_kek_t azure;
        _mongocrypt_gcp_kek_t gcp;
        _mongocrypt_aws_kek_t aws;
        _mongocrypt_kmip_kek_t kmip;
    } provider;

    char *kmsid;
    const char *kmsid_name;
} _mongocrypt_kek_t;

/* Parse a document describing a key encryption key.
 * This may can come from two places:
 * 1. The option passed for creating a data key via
 * mongocrypt_ctx_setopt_key_encryption_key
 * 2. The "masterKey" document from a data encryption key document.
 */
bool _mongocrypt_kek_parse_owned(const bson_t *bson,
                                 _mongocrypt_kek_t *out,
                                 mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kek_append(const _mongocrypt_kek_t *kek,
                            bson_t *out,
                            mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

void _mongocrypt_kek_copy_to(const _mongocrypt_kek_t *src, _mongocrypt_kek_t *dst);

void _mongocrypt_kek_cleanup(_mongocrypt_kek_t *kek);

#endif /* MONGOCRYPT_KEK_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-kek.c000066400000000000000000000317401521103432300203750ustar00rootroot00000000000000/*
 * Copyright 2020-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-kek-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"

static bool _mongocrypt_azure_kek_parse(_mongocrypt_azure_kek_t *azure,
                                        const char *kmsid,
                                        const bson_t *def,
                                        mongocrypt_status_t *status) {
    if (!_mongocrypt_parse_required_endpoint(def,
                                             "keyVaultEndpoint",
                                             &azure->key_vault_endpoint,
                                             NULL /* opts */,
                                             status)) {
        return false;
    }

    if (!_mongocrypt_parse_required_utf8(def, "keyName", &azure->key_name, status)) {
        return false;
    }

    if (!_mongocrypt_parse_optional_utf8(def, "keyVersion", &azure->key_version, status)) {
        return false;
    }

    if (!_mongocrypt_check_allowed_fields(def,
                                          NULL /* root */,
                                          status,
                                          "provider",
                                          "keyVaultEndpoint",
                                          "keyName",
                                          "keyVersion")) {
        return false;
    }
    return true;
}

static bool _mongocrypt_gcp_kek_parse(_mongocrypt_gcp_kek_t *gcp,
                                      const char *kmsid,
                                      const bson_t *def,
                                      mongocrypt_status_t *status) {
    if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &gcp->endpoint, NULL /* opts */, status)) {
        return false;
    }

    if (!_mongocrypt_parse_required_utf8(def, "projectId", &gcp->project_id, status)) {
        return false;
    }

    if (!_mongocrypt_parse_required_utf8(def, "location", &gcp->location, status)) {
        return false;
    }

    if (!_mongocrypt_parse_required_utf8(def, "keyRing", &gcp->key_ring, status)) {
        return false;
    }

    if (!_mongocrypt_parse_required_utf8(def, "keyName", &gcp->key_name, status)) {
        return false;
    }

    if (!_mongocrypt_parse_optional_utf8(def, "keyVersion", &gcp->key_version, status)) {
        return false;
    }
    if (!_mongocrypt_check_allowed_fields(def,
                                          NULL,
                                          status,
                                          "provider",
                                          "endpoint",
                                          "projectId",
                                          "location",
                                          "keyRing",
                                          "keyName",
                                          "keyVersion")) {
        return false;
    }
    return true;
}

static bool _mongocrypt_aws_kek_parse(_mongocrypt_aws_kek_t *aws,
                                      const char *kmsid,
                                      const bson_t *def,
                                      mongocrypt_status_t *status) {
    if (!_mongocrypt_parse_required_utf8(def, "key", &aws->cmk, status)) {
        return false;
    }
    if (!_mongocrypt_parse_required_utf8(def, "region", &aws->region, status)) {
        return false;
    }
    if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &aws->endpoint, NULL /* opts */, status)) {
        return false;
    }
    if (!_mongocrypt_check_allowed_fields(def, NULL, status, "provider", "key", "region", "endpoint")) {
        return false;
    }

    return true;
}

static bool _mongocrypt_kmip_kek_parse(_mongocrypt_kmip_kek_t *kmip,
                                       const char *kmsid,
                                       const bson_t *def,
                                       mongocrypt_status_t *status) {
    _mongocrypt_endpoint_parse_opts_t opts = {0};

    opts.allow_empty_subdomain = true;
    if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &kmip->endpoint, &opts, status)) {
        return false;
    }

    if (!_mongocrypt_parse_optional_utf8(def, "keyId", &kmip->key_id, status)) {
        return false;
    }

    kmip->delegated = false;
    if (!_mongocrypt_parse_optional_bool(def, "delegated", &kmip->delegated, status)) {
        return false;
    }

    if (!_mongocrypt_check_allowed_fields(def, NULL, status, "provider", "endpoint", "keyId", "delegated")) {
        return false;
    }
    return true;
}

/* Possible documents to parse:
 * AWS
 *    provider: "aws"
 *    region: 
 *    key: 
 *    endpoint: 
 * Azure
 *    provider: "azure"
 *    keyVaultEndpoint: 
 *    keyName: 
 *    keyVersion: 
 * GCP
 *    provider: "gcp"
 *    projectId: 
 *    location: 
 *    keyRing: 
 *    keyName: 
 *    keyVersion: 
 *    endpoint: 
 * Local
 *    provider: "local"
 * KMIP
 *    provider: "kmip"
 *    keyId: 
 *    endpoint: 
 */
bool _mongocrypt_kek_parse_owned(const bson_t *bson, _mongocrypt_kek_t *kek, mongocrypt_status_t *status) {
    char *kms_provider = NULL;
    bool ret = false;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(kek);

    if (!_mongocrypt_parse_required_utf8(bson, "provider", &kms_provider, status)) {
        goto done;
    }

    kek->kmsid = bson_strdup(kms_provider);

    _mongocrypt_kms_provider_t type;
    if (!mc_kmsid_parse(kek->kmsid, &type, &kek->kmsid_name, status)) {
        goto done;
    }

    kek->kms_provider = type;
    switch (type) {
    default:
    case MONGOCRYPT_KMS_PROVIDER_NONE: {
        CLIENT_ERR("Unexpected parsing KMS type: none");
        goto done;
    }
    case MONGOCRYPT_KMS_PROVIDER_AWS: {
        if (!_mongocrypt_aws_kek_parse(&kek->provider.aws, kek->kmsid, bson, status)) {
            goto done;
        }
        break;
    }
    case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
        if (!_mongocrypt_check_allowed_fields(bson, NULL, status, "provider")) {
            goto done;
        }
        break;
    }
    case MONGOCRYPT_KMS_PROVIDER_AZURE: {
        if (!_mongocrypt_azure_kek_parse(&kek->provider.azure, kek->kmsid, bson, status)) {
            goto done;
        }
        break;
    }
    case MONGOCRYPT_KMS_PROVIDER_GCP: {
        if (!_mongocrypt_gcp_kek_parse(&kek->provider.gcp, kek->kmsid, bson, status)) {
            goto done;
        }
        break;
    }
    case MONGOCRYPT_KMS_PROVIDER_KMIP: {
        if (!_mongocrypt_kmip_kek_parse(&kek->provider.kmip, kek->kmsid, bson, status)) {
            goto done;
        }
        break;
    }
    }

    ret = true;
done:
    bson_free(kms_provider);
    return ret;
}

bool _mongocrypt_kek_append(const _mongocrypt_kek_t *kek, bson_t *bson, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kek);
    BSON_ASSERT_PARAM(bson);

    BSON_APPEND_UTF8(bson, "provider", kek->kmsid);
    if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        BSON_APPEND_UTF8(bson, "region", kek->provider.aws.region);
        BSON_APPEND_UTF8(bson, "key", kek->provider.aws.cmk);
        if (kek->provider.aws.endpoint) {
            BSON_APPEND_UTF8(bson, "endpoint", kek->provider.aws.endpoint->host_and_port);
        }
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
        // Only `provider` is needed.
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
        BSON_APPEND_UTF8(bson, "keyVaultEndpoint", kek->provider.azure.key_vault_endpoint->host_and_port);
        BSON_APPEND_UTF8(bson, "keyName", kek->provider.azure.key_name);
        if (kek->provider.azure.key_version) {
            BSON_APPEND_UTF8(bson, "keyVersion", kek->provider.azure.key_version);
        }
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
        BSON_APPEND_UTF8(bson, "projectId", kek->provider.gcp.project_id);
        BSON_APPEND_UTF8(bson, "location", kek->provider.gcp.location);
        BSON_APPEND_UTF8(bson, "keyRing", kek->provider.gcp.key_ring);
        BSON_APPEND_UTF8(bson, "keyName", kek->provider.gcp.key_name);
        if (kek->provider.gcp.key_version) {
            BSON_APPEND_UTF8(bson, "keyVersion", kek->provider.gcp.key_version);
        }
        if (kek->provider.gcp.endpoint) {
            BSON_APPEND_UTF8(bson, "endpoint", kek->provider.gcp.endpoint->host_and_port);
        }
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
        if (kek->provider.kmip.endpoint) {
            BSON_APPEND_UTF8(bson, "endpoint", kek->provider.kmip.endpoint->host_and_port);
        }

        if (kek->provider.kmip.delegated) {
            BSON_APPEND_BOOL(bson, "delegated", kek->provider.kmip.delegated);
        }

        /* "keyId" is required in the final data key document for the "kmip" KMS
         * provider. It may be set from the "kmip.keyId" in the BSON document set
         * in mongocrypt_ctx_setopt_key_encryption_key, Otherwise, libmongocrypt
         * is expected to set "keyId". */
        if (kek->provider.kmip.key_id) {
            BSON_APPEND_UTF8(bson, "keyId", kek->provider.kmip.key_id);
        } else {
            CLIENT_ERR("keyId required for KMIP");
            return false;
        }
    } else {
        BSON_ASSERT(kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE);
    }
    return true;
}

void _mongocrypt_kek_copy_to(const _mongocrypt_kek_t *src, _mongocrypt_kek_t *dst) {
    BSON_ASSERT_PARAM(src);
    BSON_ASSERT_PARAM(dst);

    if (src->kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        dst->provider.aws.cmk = bson_strdup(src->provider.aws.cmk);
        dst->provider.aws.region = bson_strdup(src->provider.aws.region);
        dst->provider.aws.endpoint = _mongocrypt_endpoint_copy(src->provider.aws.endpoint);
    } else if (src->kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
        dst->provider.azure.key_vault_endpoint = _mongocrypt_endpoint_copy(src->provider.azure.key_vault_endpoint);
        dst->provider.azure.key_name = bson_strdup(src->provider.azure.key_name);
        dst->provider.azure.key_version = bson_strdup(src->provider.azure.key_version);
    } else if (src->kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
        dst->provider.gcp.project_id = bson_strdup(src->provider.gcp.project_id);
        dst->provider.gcp.location = bson_strdup(src->provider.gcp.location);
        dst->provider.gcp.key_ring = bson_strdup(src->provider.gcp.key_ring);
        dst->provider.gcp.key_name = bson_strdup(src->provider.gcp.key_name);
        dst->provider.gcp.key_version = bson_strdup(src->provider.gcp.key_version);
        dst->provider.gcp.endpoint = _mongocrypt_endpoint_copy(src->provider.gcp.endpoint);
    } else if (src->kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
        dst->provider.kmip.endpoint = _mongocrypt_endpoint_copy(src->provider.kmip.endpoint);
        dst->provider.kmip.key_id = bson_strdup(src->provider.kmip.key_id);
        dst->provider.kmip.delegated = src->provider.kmip.delegated;
    } else {
        BSON_ASSERT(src->kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE
                    || src->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL);
    }
    dst->kms_provider = src->kms_provider;
    dst->kmsid = bson_strdup(src->kmsid);
}

void _mongocrypt_kek_cleanup(_mongocrypt_kek_t *kek) {
    if (!kek) {
        return;
    }

    if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        bson_free(kek->provider.aws.cmk);
        bson_free(kek->provider.aws.region);
        _mongocrypt_endpoint_destroy(kek->provider.aws.endpoint);
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
        _mongocrypt_endpoint_destroy(kek->provider.azure.key_vault_endpoint);
        bson_free(kek->provider.azure.key_name);
        bson_free(kek->provider.azure.key_version);
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
        bson_free(kek->provider.gcp.project_id);
        bson_free(kek->provider.gcp.location);
        bson_free(kek->provider.gcp.key_ring);
        bson_free(kek->provider.gcp.key_name);
        bson_free(kek->provider.gcp.key_version);
        _mongocrypt_endpoint_destroy(kek->provider.gcp.endpoint);
    } else if (kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
        bson_free(kek->provider.kmip.key_id);
        _mongocrypt_endpoint_destroy(kek->provider.kmip.endpoint);
    } else {
        BSON_ASSERT(kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_NONE
                    || kek->kms_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL);
    }
    bson_free(kek->kmsid);
    return;
}
libmongocrypt-1.19.0/src/mongocrypt-key-broker-private.h000066400000000000000000000156701521103432300233560ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_KEY_BROKER_PRIVATE_H
#define MONGOCRYPT_KEY_BROKER_PRIVATE_H

#include 

#include "kms_message/kms_message.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt-cache-key-private.h"
#include "mongocrypt-cache-private.h"
#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt.h"

/* The key broker acts as a middle-man between an encrypt/decrypt request and
 * the key cache.
 * Each encrypt/decrypt request has one key broker. Key brokers are not shared.
 * It is responsible for:
 * - keeping track of requested keys (either by id or keyAltName)
 * - copying keys from the cache to satisfy those requests
 * - generating find cmd filters to fetch keys that aren't cached or are expired
 * - generating KMS decrypt requests on newly fetched keys
 * - adding newly fetched keys back to the cache
 *
 * Notes:
 * - any key request that is satisfied stays satisfied.
 * - keys returned from the driver are validated to not have intersecting key
 * alt names (or duplicate ids).
 * - keys fetched from the cache are not validated, because the cache is shared
 * and locking only occurs on a single fetch (so it is possible to have two keys
 * fetched from the cache that have intersecting keyAltNames but a different
 * _id, and that is not an error)
 */

/* The state of the key broker. */
typedef enum {
    /* Starting state. Accept requests for keys to be added (either by id or
       name) */
    KB_REQUESTING,
    /* Accept key documents fetched from the key vault collection. */
    KB_ADDING_DOCS,
    /* Accept any key document fetched from the key vault collection. */
    KB_ADDING_DOCS_ANY,
    /* Getting oauth token(s) from KMS providers. */
    KB_AUTHENTICATING,
    /* Accept KMS replies to decrypt key material in each key document. */
    KB_DECRYPTING_KEY_MATERIAL,
    KB_DONE,
    KB_ERROR
} key_broker_state_t;

/* Represents a single request for a key, as indicated from a response
 * from mongocryptd. */
typedef struct _key_request_t {
    /* only one of id or alt_name are set. */
    _mongocrypt_buffer_t id;
    _mongocrypt_key_alt_name_t *alt_name;
    bool satisfied; /* true if satisfied by a cache entry or a key returned. */
    struct _key_request_t *next;
} key_request_t;

/* Represents a single key supplied from the driver or cache. */
typedef struct _key_returned_t {
    _mongocrypt_key_doc_t *doc;
    _mongocrypt_buffer_t decrypted_key_material;

    mongocrypt_kms_ctx_t kms;
    bool decrypted;

    bool needs_auth;

    struct _key_returned_t *next;
} key_returned_t;

typedef struct _mc_mapof_kmsid_to_authrequest_t mc_mapof_kmsid_to_authrequest_t;

typedef struct {
    key_broker_state_t state;
    mongocrypt_status_t *status;
    key_request_t *key_requests;
    /* Keep keys returned from driver separate from keys returned from cache.
     * Keys returned from driver MUST not have conflicts (e.g. intersecting key
     * alt names)
     * But keys from cache MAY have conflicts since the cache is only locked for
     * a single get operation.
     */
    key_returned_t *keys_returned;
    key_returned_t *keys_cached;
    _mongocrypt_buffer_t filter;
    mongocrypt_t *crypt;

    key_returned_t *decryptor_iter;
    mc_mapof_kmsid_to_authrequest_t *auth_requests;
} _mongocrypt_key_broker_t;

void _mongocrypt_key_broker_init(_mongocrypt_key_broker_t *kb, mongocrypt_t *crypt);

/* Add a request for a key by UUID. */
bool _mongocrypt_key_broker_request_id(_mongocrypt_key_broker_t *kb,
                                       const _mongocrypt_buffer_t *key_id) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Add keyAltName into the key broker.
   Key is added as KEY_EMPTY. */
bool _mongocrypt_key_broker_request_name(_mongocrypt_key_broker_t *kb,
                                         const bson_value_t *key_alt_name) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Switch mode to permit adding documents without prior requests. */
bool _mongocrypt_key_broker_request_any(_mongocrypt_key_broker_t *kb) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_key_broker_requests_done(_mongocrypt_key_broker_t *kb);

/* Get the find command filter. */
bool _mongocrypt_key_broker_filter(_mongocrypt_key_broker_t *kb,
                                   mongocrypt_binary_t *out) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Add a key document. */
bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
                                    _mongocrypt_opts_kms_providers_t *kms_providers,
                                    const _mongocrypt_buffer_t *doc) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_key_broker_docs_done(_mongocrypt_key_broker_t *kb);

/* Iterate the keys needing KMS decryption. */
mongocrypt_kms_ctx_t *_mongocrypt_key_broker_next_kms(_mongocrypt_key_broker_t *kb) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Indicate that all KMS requests are complete. */
bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_opts_kms_providers_t *kms_providers);

/* Get the final decrypted key material from a key by looking up with a key_id.
 * @out is always initialized, even on error. */
bool _mongocrypt_key_broker_decrypted_key_by_id(_mongocrypt_key_broker_t *kb,
                                                const _mongocrypt_buffer_t *key_id,
                                                _mongocrypt_buffer_t *out) MONGOCRYPT_WARN_UNUSED_RESULT;

/* Get the final decrypted key material from a key, and optionally its key_id.
 * @key_id_out may be NULL. @out and @key_id_out (if not NULL) are always
 * initialized, even on error. */
bool _mongocrypt_key_broker_decrypted_key_by_name(_mongocrypt_key_broker_t *kb,
                                                  const bson_value_t *key_alt_name,
                                                  _mongocrypt_buffer_t *out,
                                                  _mongocrypt_buffer_t *key_id_out) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_key_broker_status(_mongocrypt_key_broker_t *kb, mongocrypt_status_t *out);

void _mongocrypt_key_broker_cleanup(_mongocrypt_key_broker_t *kb);

/* For testing only, add a decrypted key */
void _mongocrypt_key_broker_add_test_key(_mongocrypt_key_broker_t *kb, const _mongocrypt_buffer_t *key_id);

/* _mongocrypt_key_broker_restart is used to request additional keys. It must
 * only be called in the KB_DONE state. */
bool _mongocrypt_key_broker_restart(_mongocrypt_key_broker_t *kb);

#endif /* MONGOCRYPT_KEY_BROKER_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-key-broker.c000066400000000000000000001260671521103432300217040ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mc-array-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-private.h"

typedef struct _auth_request_t {
    mongocrypt_kms_ctx_t kms;
    bool returned;
    char *kmsid;
} auth_request_t;

static auth_request_t *auth_request_new(void) {
    return bson_malloc0(sizeof(auth_request_t));
}

static void auth_request_destroy(auth_request_t *ar) {
    if (!ar) {
        return;
    }
    _mongocrypt_kms_ctx_cleanup(&ar->kms);
    bson_free(ar->kmsid);
    bson_free(ar);
}

struct _mc_mapof_kmsid_to_authrequest_t {
    mc_array_t entries;
};

static mc_mapof_kmsid_to_authrequest_t *mc_mapof_kmsid_to_authrequest_new(void) {
    mc_mapof_kmsid_to_authrequest_t *k2a = bson_malloc0(sizeof(mc_mapof_kmsid_to_authrequest_t));
    _mc_array_init(&k2a->entries, sizeof(auth_request_t *));
    return k2a;
}

static void mc_mapof_kmsid_to_authrequest_destroy(mc_mapof_kmsid_to_authrequest_t *k2a) {
    if (!k2a) {
        return;
    }
    for (size_t i = 0; i < k2a->entries.len; i++) {
        auth_request_t *ar = _mc_array_index(&k2a->entries, auth_request_t *, i);
        auth_request_destroy(ar);
    }
    _mc_array_destroy(&k2a->entries);
    bson_free(k2a);
}

static bool mc_mapof_kmsid_to_authrequest_has(const mc_mapof_kmsid_to_authrequest_t *k2a, const char *kmsid) {
    BSON_ASSERT_PARAM(k2a);
    BSON_ASSERT_PARAM(kmsid);
    for (size_t i = 0; i < k2a->entries.len; i++) {
        auth_request_t *ar = _mc_array_index(&k2a->entries, auth_request_t *, i);
        if (0 == strcmp(ar->kmsid, kmsid)) {
            return true;
        }
    }
    return false;
}

static size_t mc_mapof_kmsid_to_authrequest_len(const mc_mapof_kmsid_to_authrequest_t *k2a) {
    BSON_ASSERT_PARAM(k2a);
    return k2a->entries.len;
}

static bool mc_mapof_kmsid_to_authrequest_empty(const mc_mapof_kmsid_to_authrequest_t *k2a) {
    BSON_ASSERT_PARAM(k2a);
    return k2a->entries.len == 0;
}

// `mc_mapof_kmsid_to_authrequest_put` moves `to_put` into the map and takes ownership of `to_put`.
// No checking is done to prohibit duplicate entries.
static void mc_mapof_kmsid_to_authrequest_put(mc_mapof_kmsid_to_authrequest_t *k2a, auth_request_t *to_put) {
    BSON_ASSERT_PARAM(k2a);

    _mc_array_append_val(&k2a->entries, to_put);
}

static auth_request_t *mc_mapof_kmsid_to_authrequest_at(mc_mapof_kmsid_to_authrequest_t *k2a, size_t i) {
    BSON_ASSERT_PARAM(k2a);

    return _mc_array_index(&k2a->entries, auth_request_t *, i);
}

void _mongocrypt_key_broker_init(_mongocrypt_key_broker_t *kb, mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(crypt);

    memset(kb, 0, sizeof(*kb));
    kb->crypt = crypt;
    kb->state = KB_REQUESTING;
    kb->status = mongocrypt_status_new();
    kb->auth_requests = mc_mapof_kmsid_to_authrequest_new();
}

/*
 * Creates a new key_returned_t and prepends it to a list.
 *
 * Side effects:
 * - updates *list to point to a new head.
 */
static key_returned_t *
_key_returned_prepend(_mongocrypt_key_broker_t *kb, key_returned_t **list, _mongocrypt_key_doc_t *key_doc) {
    key_returned_t *key_returned;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(list);
    BSON_ASSERT_PARAM(key_doc);

    key_returned = bson_malloc0(sizeof(*key_returned));
    BSON_ASSERT(key_returned);

    key_returned->doc = _mongocrypt_key_new();
    _mongocrypt_key_doc_copy_to(key_doc, key_returned->doc);

    /* Prepend and update the head of the list. */
    key_returned->next = *list;
    *list = key_returned;

    /* Update the head of the decrypting iter. */
    kb->decryptor_iter = kb->keys_returned;
    return key_returned;
}

/* Find the first (if any) key_returned_t matching either a key_id or a list of
 * key_alt_names (both are NULLable) */
static key_returned_t *
_key_returned_find_one(key_returned_t *list, _mongocrypt_buffer_t *key_id, _mongocrypt_key_alt_name_t *key_alt_names) {
    key_returned_t *key_returned;

    /* list can be NULL. */
    /* key_id and key_alt_names are not dereferenced in this function and they
     * are checked just before being passed on as parameters. */

    for (key_returned = list; NULL != key_returned; key_returned = key_returned->next) {
        if (key_id) {
            BSON_ASSERT(key_returned->doc);
            if (0 == _mongocrypt_buffer_cmp(key_id, &key_returned->doc->id)) {
                return key_returned;
            }
        }
        if (key_alt_names) {
            BSON_ASSERT(key_returned->doc);
            if (_mongocrypt_key_alt_name_intersects(key_alt_names, key_returned->doc->key_alt_names)) {
                return key_returned;
            }
        }
    }

    return NULL;
}

/* Find the first (if any) key_request_t in the key broker matching either a
 * key_id or a list of key_alt_names (both are NULLable) */
static key_request_t *_key_request_find_one(_mongocrypt_key_broker_t *kb,
                                            const _mongocrypt_buffer_t *key_id,
                                            _mongocrypt_key_alt_name_t *key_alt_names) {
    key_request_t *key_request;

    BSON_ASSERT_PARAM(kb);
    /* key_id and key_alt_names are not dereferenced in this function and they
     * are checked just before being passed on as parameters. */

    for (key_request = kb->key_requests; NULL != key_request; key_request = key_request->next) {
        if (key_id) {
            if (0 == _mongocrypt_buffer_cmp(key_id, &key_request->id)) {
                return key_request;
            }
        }
        if (key_alt_names) {
            if (_mongocrypt_key_alt_name_intersects(key_alt_names, key_request->alt_name)) {
                return key_request;
            }
        }
    }

    return NULL;
}

static bool _all_key_requests_satisfied(_mongocrypt_key_broker_t *kb) {
    key_request_t *key_request;

    BSON_ASSERT_PARAM(kb);

    for (key_request = kb->key_requests; NULL != key_request; key_request = key_request->next) {
        if (!key_request->satisfied) {
            return false;
        }
    }
    return true;
}

static bool _key_broker_fail_w_msg(_mongocrypt_key_broker_t *kb, const char *msg) {
    mongocrypt_status_t *status;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(msg);

    kb->state = KB_ERROR;
    status = kb->status;
    CLIENT_ERR("%s", msg);
    return false;
}

static bool _key_broker_fail(_mongocrypt_key_broker_t *kb) {
    BSON_ASSERT_PARAM(kb);

    if (mongocrypt_status_ok(kb->status)) {
        return _key_broker_fail_w_msg(kb, "unexpected, failing but no error status set");
    }
    kb->state = KB_ERROR;
    return false;
}

static bool _try_satisfying_from_cache(_mongocrypt_key_broker_t *kb, key_request_t *req) {
    _mongocrypt_cache_key_attr_t *attr = NULL;
    _mongocrypt_cache_key_value_t *value = NULL;
    bool ret = false;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(req);

    if (kb->state != KB_REQUESTING && kb->state != KB_ADDING_DOCS_ANY) {
        _key_broker_fail_w_msg(kb, "trying to retrieve key from cache in invalid state");
        goto cleanup;
    }

    attr = _mongocrypt_cache_key_attr_new(&req->id, req->alt_name);
    if (!_mongocrypt_cache_get(&kb->crypt->cache_key, attr, (void **)&value)) {
        _key_broker_fail_w_msg(kb, "failed to retrieve from cache");
        goto cleanup;
    }

    if (value) {
        key_returned_t *key_returned;

        req->satisfied = true;
        if (_mongocrypt_buffer_empty(&value->decrypted_key_material)) {
            _key_broker_fail_w_msg(kb, "cache entry does not have decrypted key material");
            goto cleanup;
        }

        /* Add the cached key to our locally copied list.
         * Note, we deduplicate requests, but *not* keys from the cache,
         * because the state of the cache may change between each call to
         * _mongocrypt_cache_get.
         */
        key_returned = _key_returned_prepend(kb, &kb->keys_cached, value->key_doc);
        _mongocrypt_buffer_init(&key_returned->decrypted_key_material);
        _mongocrypt_buffer_copy_to(&value->decrypted_key_material, &key_returned->decrypted_key_material);
        key_returned->decrypted = true;
    }

    ret = true;
cleanup:
    _mongocrypt_cache_key_value_destroy(value);
    _mongocrypt_cache_key_attr_destroy(attr);
    return ret;
}

static bool _store_to_cache(_mongocrypt_key_broker_t *kb, key_returned_t *key_returned) {
    _mongocrypt_cache_key_value_t *value;
    _mongocrypt_cache_key_attr_t *attr;
    bool ret;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_returned);

    if (!key_returned->decrypted) {
        return _key_broker_fail_w_msg(kb, "cannot cache non-decrypted key");
    }

    attr = _mongocrypt_cache_key_attr_new(&key_returned->doc->id, key_returned->doc->key_alt_names);
    if (!attr) {
        return _key_broker_fail_w_msg(kb, "could not create key cache attribute");
    }
    value = _mongocrypt_cache_key_value_new(key_returned->doc, &key_returned->decrypted_key_material);
    ret = _mongocrypt_cache_add_stolen(&kb->crypt->cache_key, attr, value, kb->status);
    _mongocrypt_cache_key_attr_destroy(attr);
    if (!ret) {
        return _key_broker_fail(kb);
    }
    return true;
}

bool _mongocrypt_key_broker_request_id(_mongocrypt_key_broker_t *kb, const _mongocrypt_buffer_t *key_id) {
    key_request_t *req;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_id);

    if (kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting to request a key id, but in wrong state");
    }

    if (!_mongocrypt_buffer_is_uuid((_mongocrypt_buffer_t *)key_id)) {
        return _key_broker_fail_w_msg(kb, "expected UUID for key id");
    }

    if (_key_request_find_one(kb, key_id, NULL)) {
        return true;
    }

    req = bson_malloc0(sizeof *req);
    BSON_ASSERT(req);

    _mongocrypt_buffer_copy_to(key_id, &req->id);
    req->next = kb->key_requests;
    kb->key_requests = req;
    if (!_try_satisfying_from_cache(kb, req)) {
        return false;
    }
    return true;
}

bool _mongocrypt_key_broker_request_name(_mongocrypt_key_broker_t *kb, const bson_value_t *key_alt_name_value) {
    key_request_t *req;
    _mongocrypt_key_alt_name_t *key_alt_name;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_alt_name_value);

    if (kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting to request a key name, but in wrong state");
    }

    key_alt_name = _mongocrypt_key_alt_name_new(key_alt_name_value);

    /* Check if we already have a request for this key alt name. */
    if (_key_request_find_one(kb, NULL /* key id */, key_alt_name)) {
        _mongocrypt_key_alt_name_destroy_all(key_alt_name);
        return true;
    }

    req = bson_malloc0(sizeof *req);
    BSON_ASSERT(req);

    req->alt_name = key_alt_name /* takes ownership */;
    req->next = kb->key_requests;
    kb->key_requests = req;
    if (!_try_satisfying_from_cache(kb, req)) {
        return false;
    }
    return true;
}

bool _mongocrypt_key_broker_request_any(_mongocrypt_key_broker_t *kb) {
    BSON_ASSERT_PARAM(kb);

    if (kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting to request any keys, but in wrong state");
    }

    if (kb->key_requests) {
        return _key_broker_fail_w_msg(kb, "attempting to request any keys, but requests already made");
    }

    kb->state = KB_ADDING_DOCS_ANY;

    return true;
}

bool _mongocrypt_key_broker_requests_done(_mongocrypt_key_broker_t *kb) {
    BSON_ASSERT_PARAM(kb);

    if (kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting to finish adding requests, but in wrong state");
    }

    if (kb->key_requests) {
        if (_all_key_requests_satisfied(kb)) {
            kb->state = KB_DONE;
        } else {
            kb->state = KB_ADDING_DOCS;
        }
    } else {
        kb->state = KB_DONE;
    }
    return true;
}

bool _mongocrypt_key_broker_filter(_mongocrypt_key_broker_t *kb, mongocrypt_binary_t *out) {
    key_request_t *req;
    _mongocrypt_key_alt_name_t *key_alt_name;
    int name_index = 0;
    int id_index = 0;
    bson_t ids, names;
    bson_t *filter;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(out);

    if (kb->state != KB_ADDING_DOCS) {
        return _key_broker_fail_w_msg(kb, "attempting to retrieve filter, but in wrong state");
    }

    if (!_mongocrypt_buffer_empty(&kb->filter)) {
        _mongocrypt_buffer_to_binary(&kb->filter, out);
        return true;
    }

    bson_init(&names);
    bson_init(&ids);

    for (req = kb->key_requests; NULL != req; req = req->next) {
        if (req->satisfied) {
            continue;
        }

        if (!_mongocrypt_buffer_empty(&req->id)) {
            /* Collect key_ids in "ids" */
            char *key_str;

            key_str = bson_strdup_printf("%d", id_index++);
            if (!key_str || !_mongocrypt_buffer_append(&req->id, &ids, key_str, -1)) {
                bson_destroy(&ids);
                bson_destroy(&names);
                bson_free(key_str);
                return _key_broker_fail_w_msg(kb, "could not construct id list");
            }

            bson_free(key_str);
        }

        /* Collect key alt names in "names" */
        for (key_alt_name = req->alt_name; NULL != key_alt_name; key_alt_name = key_alt_name->next) {
            char *key_str;

            key_str = bson_strdup_printf("%d", name_index++);
            BSON_ASSERT(key_str);
            if (!bson_append_value(&names, key_str, (int)strlen(key_str), &key_alt_name->value)) {
                bson_destroy(&ids);
                bson_destroy(&names);
                bson_free(key_str);
                return _key_broker_fail_w_msg(kb, "could not construct keyAltName list");
            }

            bson_free(key_str);
        }
    }

    /*
     * This is our final query:
     * { $or: [ { _id: { $in : [ids] }},
     *          { keyAltNames : { $in : [names] }} ] }
     */
    filter = BCON_NEW("$or",
                      "[",
                      "{",
                      "_id",
                      "{",
                      "$in",
                      BCON_ARRAY(&ids),
                      "}",
                      "}",
                      "{",
                      "keyAltNames",
                      "{",
                      "$in",
                      BCON_ARRAY(&names),
                      "}",
                      "}",
                      "]");

    _mongocrypt_buffer_steal_from_bson(&kb->filter, filter);
    _mongocrypt_buffer_to_binary(&kb->filter, out);
    bson_destroy(&ids);
    bson_destroy(&names);

    return true;
}

bool _mongocrypt_key_broker_add_doc(_mongocrypt_key_broker_t *kb,
                                    _mongocrypt_opts_kms_providers_t *kms_providers,
                                    const _mongocrypt_buffer_t *doc) {
    bool ret = false;
    bson_t doc_bson;
    _mongocrypt_key_doc_t *key_doc = NULL;
    key_request_t *key_request;
    key_returned_t *key_returned;
    _mongocrypt_kms_provider_t kek_provider;
    char *access_token = NULL;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(kms_providers);

    if (kb->state != KB_ADDING_DOCS && kb->state != KB_ADDING_DOCS_ANY) {
        _key_broker_fail_w_msg(kb, "attempting to add a key doc, but in wrong state");
        goto done;
    }

    if (!doc) {
        _key_broker_fail_w_msg(kb, "invalid key");
        goto done;
    }

    /* First, parse the key document. */
    key_doc = _mongocrypt_key_new();
    if (!_mongocrypt_buffer_to_bson(doc, &doc_bson)) {
        _key_broker_fail_w_msg(kb, "malformed BSON for key document");
        goto done;
    }

    if (!_mongocrypt_key_parse_owned(&doc_bson, key_doc, kb->status)) {
        goto done;
    }

    if (!_key_request_find_one(kb, &key_doc->id, key_doc->key_alt_names)) {
        /* If in normal mode, ensure that this document matches at least one
         * existing request. */
        if (kb->state == KB_ADDING_DOCS) {
            _key_broker_fail_w_msg(kb, "unexpected key returned, does not match any requests");
            goto done;
        }

        /* If in any mode, add request for provided document now. */
        if (kb->state == KB_ADDING_DOCS_ANY) {
            key_request_t *const req = bson_malloc0(sizeof(key_request_t));

            BSON_ASSERT(req);

            _mongocrypt_buffer_copy_to(&key_doc->id, &req->id);
            req->alt_name = _mongocrypt_key_alt_name_copy_all(key_doc->key_alt_names);
            req->next = kb->key_requests;
            kb->key_requests = req;

            if (!_try_satisfying_from_cache(kb, req)) {
                goto done;
            }

            /* Key is already cached; no work to be done. */
            if (req->satisfied) {
                ret = true;
                goto done;
            }
        }
    }

    /* Check if there are other keys_returned with intersecting altnames or
     * equal id. This is an error. Do *not* check cached keys. */
    if (_key_returned_find_one(kb->keys_returned, &key_doc->id, key_doc->key_alt_names)) {
        _key_broker_fail_w_msg(kb, "keys returned have duplicate keyAltNames or _id");
        goto done;
    }

    key_returned = _key_returned_prepend(kb, &kb->keys_returned, key_doc);

    /* Check that the returned key doc's provider matches. */
    kek_provider = key_doc->kek.kms_provider;

    mc_kms_creds_t kc;
    if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key_doc->kek.kmsid, &kc)) {
        mongocrypt_status_t *status = kb->status;
        CLIENT_ERR("KMS provider `%s` is not configured", key_doc->kek.kmsid);
        _key_broker_fail(kb);
        goto done;
    }

    /* If the KMS provider is local, decrypt immediately. Otherwise, create the
     * HTTP KMS request. */
    BSON_ASSERT(kb->crypt);
    if (kek_provider == MONGOCRYPT_KMS_PROVIDER_LOCAL) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_LOCAL);
        if (!_mongocrypt_unwrap_key(kb->crypt->crypto,
                                    &kc.value.local.key,
                                    &key_returned->doc->key_material,
                                    &key_returned->decrypted_key_material,
                                    kb->status)) {
            _key_broker_fail(kb);
            goto done;
        }
        key_returned->decrypted = true;
        if (!_store_to_cache(kb, key_returned)) {
            goto done;
        }
    } else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        if (!_mongocrypt_kms_ctx_init_aws_decrypt(&key_returned->kms,
                                                  kms_providers,
                                                  key_doc,
                                                  kb->crypt->crypto,
                                                  key_doc->kek.kmsid,
                                                  &kb->crypt->log)) {
            mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
            _key_broker_fail(kb);
            goto done;
        }
    } else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
        if (kc.value.azure.access_token) {
            access_token = bson_strdup(kc.value.azure.access_token);
        } else {
            access_token = mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_doc->kek.kmsid);
        }
        if (!access_token) {
            key_returned->needs_auth = true;
            /* Create an oauth request if one does not exist. */
            if (!mc_mapof_kmsid_to_authrequest_has(kb->auth_requests, key_doc->kek.kmsid)) {
                auth_request_t *ar = auth_request_new();
                if (!_mongocrypt_kms_ctx_init_azure_auth(&ar->kms,
                                                         &kc,
                                                         /* The key vault endpoint is used to determine the scope. */
                                                         key_doc->kek.provider.azure.key_vault_endpoint,
                                                         key_doc->kek.kmsid,
                                                         &kb->crypt->log)) {
                    mongocrypt_kms_ctx_status(&ar->kms, kb->status);
                    _key_broker_fail(kb);
                    auth_request_destroy(ar);
                    goto done;
                }
                ar->kmsid = bson_strdup(key_doc->kek.kmsid);
                mc_mapof_kmsid_to_authrequest_put(kb->auth_requests, ar);
            }
        } else {
            if (!_mongocrypt_kms_ctx_init_azure_unwrapkey(&key_returned->kms,
                                                          kms_providers,
                                                          access_token,
                                                          key_doc,
                                                          key_returned->doc->kek.kmsid,
                                                          &kb->crypt->log)) {
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                _key_broker_fail(kb);
                goto done;
            }
        }
    } else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
        if (NULL != kc.value.gcp.access_token) {
            access_token = bson_strdup(kc.value.gcp.access_token);
        } else {
            access_token = mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_doc->kek.kmsid);
        }
        if (!access_token) {
            key_returned->needs_auth = true;
            /* Create an oauth request if one does not exist. */
            if (!mc_mapof_kmsid_to_authrequest_has(kb->auth_requests, key_doc->kek.kmsid)) {
                auth_request_t *ar = auth_request_new();
                if (!_mongocrypt_kms_ctx_init_gcp_auth(&ar->kms,
                                                       &kb->crypt->opts,
                                                       &kc,
                                                       key_doc->kek.provider.gcp.endpoint,
                                                       key_doc->kek.kmsid,
                                                       &kb->crypt->log)) {
                    mongocrypt_kms_ctx_status(&ar->kms, kb->status);
                    _key_broker_fail(kb);
                    auth_request_destroy(ar);
                    goto done;
                }
                ar->kmsid = bson_strdup(key_doc->kek.kmsid);
                mc_mapof_kmsid_to_authrequest_put(kb->auth_requests, ar);
            }
        } else {
            if (!_mongocrypt_kms_ctx_init_gcp_decrypt(&key_returned->kms,
                                                      kms_providers,
                                                      access_token,
                                                      key_doc,
                                                      key_returned->doc->kek.kmsid,
                                                      &kb->crypt->log)) {
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                _key_broker_fail(kb);
                goto done;
            }
        }
    } else if (kek_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
        BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_KMIP);
        char *unique_identifier;
        _mongocrypt_endpoint_t *endpoint;

        if (!key_returned->doc->kek.provider.kmip.key_id) {
            _key_broker_fail_w_msg(kb, "KMIP key malformed, no keyId present");
            goto done;
        }

        unique_identifier = key_returned->doc->kek.provider.kmip.key_id;

        if (key_returned->doc->kek.provider.kmip.endpoint) {
            endpoint = key_returned->doc->kek.provider.kmip.endpoint;
        } else if (kc.value.kmip.endpoint) {
            endpoint = kc.value.kmip.endpoint;
        } else {
            _key_broker_fail_w_msg(kb, "endpoint not set for KMIP request");
            goto done;
        }

        if (key_returned->doc->kek.provider.kmip.delegated) {
            if (!_mongocrypt_kms_ctx_init_kmip_decrypt(&key_returned->kms,
                                                       endpoint,
                                                       key_doc->kek.kmsid,
                                                       key_doc,
                                                       &kb->crypt->log)) {
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                _key_broker_fail(kb);
                goto done;
            }
        } else {
            if (!_mongocrypt_kms_ctx_init_kmip_get(&key_returned->kms,
                                                   endpoint,
                                                   unique_identifier,
                                                   key_doc->kek.kmsid,
                                                   &kb->crypt->log)) {
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                _key_broker_fail(kb);
                goto done;
            }
        }
    } else {
        _key_broker_fail_w_msg(kb, "unrecognized kms provider");
        goto done;
    }

    /* Mark all matching key requests as satisfied. */
    for (key_request = kb->key_requests; NULL != key_request; key_request = key_request->next) {
        if (0 == _mongocrypt_buffer_cmp(&key_doc->id, &key_request->id)) {
            key_request->satisfied = true;
        }
        if (_mongocrypt_key_alt_name_intersects(key_doc->key_alt_names, key_request->alt_name)) {
            key_request->satisfied = true;
            _mongocrypt_buffer_copy_to(&key_doc->id, &key_request->id);
        }
    }

    ret = true;
done:
    bson_free(access_token);
    _mongocrypt_key_destroy(key_doc);
    return ret;
}

bool _mongocrypt_key_broker_docs_done(_mongocrypt_key_broker_t *kb) {
    key_returned_t *key_returned;
    bool needs_decryption;
    bool needs_auth;

    BSON_ASSERT_PARAM(kb);

    if (kb->state != KB_ADDING_DOCS && kb->state != KB_ADDING_DOCS_ANY) {
        return _key_broker_fail_w_msg(kb, "attempting to finish adding docs, but in wrong state");
    }

    /* If there are any requests left unsatisfied, error. */
    if (!_all_key_requests_satisfied(kb)) {
        return _key_broker_fail_w_msg(
            kb,
            "not all keys requested were satisfied. Verify that key vault DB/collection name was correctly specified.");
    }

    /* Transition to the next state.
     *  - If there are any Azure or GCP backed keys, and no oauth token is
     * cached, transition to KB_AUTHENTICATING.
     *  - Otherwise, if there are keys that need to be decrypted, transition to
     * KB_DECRYPTING_KEY_MATERIAL.
     *  - Otherwise, all keys were retrieved from the cache or decrypted locally,
     * skip the decrypting state and go right to KB_DONE.
     */
    needs_decryption = false;
    needs_auth = false;
    for (key_returned = kb->keys_returned; NULL != key_returned; key_returned = key_returned->next) {
        if (key_returned->needs_auth) {
            needs_auth = true;
            break;
        }
        if (!key_returned->decrypted) {
            needs_decryption = true;
        }
    }

    if (needs_auth) {
        kb->state = KB_AUTHENTICATING;
    } else if (needs_decryption) {
        kb->state = KB_DECRYPTING_KEY_MATERIAL;
    } else {
        kb->state = KB_DONE;
    }
    return true;
}

mongocrypt_kms_ctx_t *_mongocrypt_key_broker_next_kms(_mongocrypt_key_broker_t *kb) {
    BSON_ASSERT_PARAM(kb);

    if (kb->state != KB_DECRYPTING_KEY_MATERIAL && kb->state != KB_AUTHENTICATING) {
        _key_broker_fail_w_msg(kb, "attempting to get KMS request, but in wrong state");
        /* TODO (CDRIVER-3327) this breaks other expectations. If the caller only
         * checks the return value they may mistake this NULL as indicating all
         * KMS requests have been iterated. */
        return NULL;
    }

    if (kb->state == KB_AUTHENTICATING) {
        if (mc_mapof_kmsid_to_authrequest_empty(kb->auth_requests)) {
            _key_broker_fail_w_msg(kb,
                                   "unexpected, attempting to authenticate but "
                                   "KMS request not initialized");
            return NULL;
        }

        // Return the first not-yet-returned auth request.
        for (size_t i = 0; i < mc_mapof_kmsid_to_authrequest_len(kb->auth_requests); i++) {
            auth_request_t *ar = mc_mapof_kmsid_to_authrequest_at(kb->auth_requests, i);

            if (ar->kms.should_retry) {
                ar->kms.should_retry = false;
                ar->returned = true;
                return &ar->kms;
            }

            if (ar->returned) {
                continue;
            }
            ar->returned = true;
            return &ar->kms;
        }

        return NULL;
    }

    // Check if any requests need retry
    for (key_returned_t *ptr = kb->keys_returned; ptr != NULL; ptr = ptr->next) {
        if (ptr->kms.should_retry) {
            ptr->kms.should_retry = false;
            return &ptr->kms;
        }
    }
    while (kb->decryptor_iter) {
        if (!kb->decryptor_iter->decrypted) {
            key_returned_t *key_returned;

            key_returned = kb->decryptor_iter;
            /* iterate before returning, so next call starts at next entry */
            kb->decryptor_iter = kb->decryptor_iter->next;
            return &key_returned->kms;
        }
        kb->decryptor_iter = kb->decryptor_iter->next;
    }

    return NULL;
}

bool _mongocrypt_key_broker_kms_done(_mongocrypt_key_broker_t *kb, _mongocrypt_opts_kms_providers_t *kms_providers) {
    key_returned_t *key_returned;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(kms_providers);

    if (kb->state != KB_DECRYPTING_KEY_MATERIAL && kb->state != KB_AUTHENTICATING) {
        return _key_broker_fail_w_msg(kb, "attempting to complete KMS requests, but in wrong state");
    }

    if (kb->state == KB_AUTHENTICATING) {
        bson_t oauth_response;
        _mongocrypt_buffer_t oauth_response_buf;

        // Apply tokens from oauth responses to oauth token cache.
        for (size_t i = 0; i < mc_mapof_kmsid_to_authrequest_len(kb->auth_requests); i++) {
            auth_request_t *ar = mc_mapof_kmsid_to_authrequest_at(kb->auth_requests, i);

            if (!_mongocrypt_kms_ctx_result(&ar->kms, &oauth_response_buf)) {
                mongocrypt_kms_ctx_status(&ar->kms, kb->status);
                return _key_broker_fail(kb);
            }

            /* Cache returned tokens. */
            BSON_ASSERT(_mongocrypt_buffer_to_bson(&oauth_response_buf, &oauth_response));
            if (!mc_mapof_kmsid_to_token_add_response(kb->crypt->cache_oauth, ar->kmsid, &oauth_response, kb->status)) {
                return _key_broker_fail(kb);
            }
        }

        /* Auth should be finished, create any remaining KMS requests. */
        for (key_returned = kb->keys_returned; NULL != key_returned; key_returned = key_returned->next) {
            char *access_token;

            if (!key_returned->needs_auth) {
                continue;
            }

            mc_kms_creds_t kc;
            if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key_returned->doc->kek.kmsid, &kc)) {
                mongocrypt_status_t *status = kb->status;
                CLIENT_ERR("KMS provider `%s` is not configured", key_returned->doc->kek.kmsid);
                return _key_broker_fail(kb);
            }

            if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE) {
                BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AZURE);
                if (kc.value.azure.access_token) {
                    access_token = bson_strdup(kc.value.azure.access_token);
                } else {
                    access_token =
                        mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_returned->doc->kek.kmsid);
                }

                if (!access_token) {
                    return _key_broker_fail_w_msg(kb, "authentication failed, no oauth token");
                }

                if (!_mongocrypt_kms_ctx_init_azure_unwrapkey(&key_returned->kms,
                                                              kms_providers,
                                                              access_token,
                                                              key_returned->doc,
                                                              key_returned->doc->kek.kmsid,
                                                              &kb->crypt->log)) {
                    mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                    bson_free(access_token);
                    return _key_broker_fail(kb);
                }

                key_returned->needs_auth = false;
                bson_free(access_token);
            } else if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
                BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_GCP);
                if (kc.value.gcp.access_token) {
                    access_token = bson_strdup(kc.value.gcp.access_token);
                } else {
                    access_token =
                        mc_mapof_kmsid_to_token_get_token(kb->crypt->cache_oauth, key_returned->doc->kek.kmsid);
                }

                if (!access_token) {
                    return _key_broker_fail_w_msg(kb, "authentication failed, no oauth token");
                }

                if (!_mongocrypt_kms_ctx_init_gcp_decrypt(&key_returned->kms,
                                                          kms_providers,
                                                          access_token,
                                                          key_returned->doc,
                                                          key_returned->doc->kek.kmsid,
                                                          &kb->crypt->log)) {
                    mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                    bson_free(access_token);
                    return _key_broker_fail(kb);
                }

                key_returned->needs_auth = false;
                bson_free(access_token);
            } else {
                return _key_broker_fail_w_msg(kb,
                                              "unexpected, authenticating but "
                                              "no requests require "
                                              "authentication");
            }
        }

        kb->state = KB_DECRYPTING_KEY_MATERIAL;
        return true;
    }

    for (key_returned = kb->keys_returned; NULL != key_returned; key_returned = key_returned->next) {
        /* Local keys were already decrypted. */
        if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AWS
            || key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_AZURE
            || key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_GCP) {
            if (key_returned->decrypted) {
                /* Non-local keys may have been decrypted previously if the key
                 * broker has been restarted. */
                continue;
            }

            if (!key_returned->kms.req) {
                return _key_broker_fail_w_msg(kb, "unexpected, KMS not set on key returned");
            }

            if (!_mongocrypt_kms_ctx_result(&key_returned->kms, &key_returned->decrypted_key_material)) {
                /* Always fatal. Key attempted to decrypt but failed. */
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                return _key_broker_fail(kb);
            }
        } else if (key_returned->doc->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP) {
            _mongocrypt_buffer_t kek;
            if (!_mongocrypt_kms_ctx_result(&key_returned->kms, &kek)) {
                mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                return _key_broker_fail(kb);
            }

            if (key_returned->doc->kek.provider.kmip.delegated) {
                if (!_mongocrypt_kms_ctx_result(&key_returned->kms, &key_returned->decrypted_key_material)) {
                    mongocrypt_kms_ctx_status(&key_returned->kms, kb->status);
                    return _key_broker_fail(kb);
                }
            } else if (!_mongocrypt_unwrap_key(kb->crypt->crypto,
                                               &kek,
                                               &key_returned->doc->key_material,
                                               &key_returned->decrypted_key_material,
                                               kb->status)) {
                _key_broker_fail(kb);
                _mongocrypt_buffer_cleanup(&kek);
                return false;
            }
            _mongocrypt_buffer_cleanup(&kek);
        } else if (key_returned->doc->kek.kms_provider != MONGOCRYPT_KMS_PROVIDER_LOCAL) {
            return _key_broker_fail_w_msg(kb, "unrecognized kms provider");
        }

        if (key_returned->decrypted_key_material.len != MONGOCRYPT_KEY_LEN) {
            return _key_broker_fail_w_msg(kb, "decrypted key is incorrect length");
        }

        key_returned->decrypted = true;
        if (!_store_to_cache(kb, key_returned)) {
            return false;
        }
    }

    kb->state = KB_DONE;
    return true;
}

static bool _get_decrypted_key_material(_mongocrypt_key_broker_t *kb,
                                        _mongocrypt_buffer_t *key_id,
                                        _mongocrypt_key_alt_name_t *key_alt_name,
                                        _mongocrypt_buffer_t *out,
                                        _mongocrypt_buffer_t *key_id_out) {
    key_returned_t *key_returned;

    BSON_ASSERT_PARAM(kb);
    /* key_id can be NULL */
    /* key_alt_name can be NULL */
    BSON_ASSERT_PARAM(out);
    /* key_id_out is checked before each use, so it can be NULL */

    _mongocrypt_buffer_init(out);
    if (key_id_out) {
        _mongocrypt_buffer_init(key_id_out);
    }
    /* Search both keys_returned and keys_cached. */

    key_returned = _key_returned_find_one(kb->keys_returned, key_id, key_alt_name);
    if (!key_returned) {
        /* Try the keys retrieved from the cache. */
        key_returned = _key_returned_find_one(kb->keys_cached, key_id, key_alt_name);
    }

    if (!key_returned) {
        return _key_broker_fail_w_msg(kb, "could not find key");
    }

    if (!key_returned->decrypted) {
        return _key_broker_fail_w_msg(kb, "unexpected, key not decrypted");
    }

    _mongocrypt_buffer_copy_to(&key_returned->decrypted_key_material, out);
    if (key_id_out) {
        _mongocrypt_buffer_copy_to(&key_returned->doc->id, key_id_out);
    }
    return true;
}

bool _mongocrypt_key_broker_decrypted_key_by_id(_mongocrypt_key_broker_t *kb,
                                                const _mongocrypt_buffer_t *key_id,
                                                _mongocrypt_buffer_t *out) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_id);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_buffer_init(out);

    if (kb->state != KB_DONE && kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state");
    }
    return _get_decrypted_key_material(kb,
                                       (_mongocrypt_buffer_t *)key_id,
                                       NULL /* key alt name */,
                                       out,
                                       NULL /* key id out */);
}

bool _mongocrypt_key_broker_decrypted_key_by_name(_mongocrypt_key_broker_t *kb,
                                                  const bson_value_t *key_alt_name_value,
                                                  _mongocrypt_buffer_t *out,
                                                  _mongocrypt_buffer_t *key_id_out) {
    bool ret;
    _mongocrypt_key_alt_name_t *key_alt_name;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_alt_name_value);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT_PARAM(key_id_out);

    _mongocrypt_buffer_init(out);
    _mongocrypt_buffer_init(key_id_out);

    // We may be in KB_REQUESTING and need keys after requesting keys for keyAltName
    if (kb->state != KB_DONE && kb->state != KB_REQUESTING) {
        return _key_broker_fail_w_msg(kb, "attempting retrieve decrypted key material, but in wrong state");
    }

    key_alt_name = _mongocrypt_key_alt_name_new(key_alt_name_value);
    ret = _get_decrypted_key_material(kb, NULL, key_alt_name, out, key_id_out);
    _mongocrypt_key_alt_name_destroy_all(key_alt_name);
    return ret;
}

bool _mongocrypt_key_broker_status(_mongocrypt_key_broker_t *kb, mongocrypt_status_t *out) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(out);

    if (!mongocrypt_status_ok(kb->status)) {
        _mongocrypt_status_copy_to(kb->status, out);
        return false;
    }

    return true;
}

static void _destroy_key_requests(key_request_t *head) {
    key_request_t *tmp;

    while (head) {
        tmp = head->next;

        _mongocrypt_buffer_cleanup(&head->id);
        _mongocrypt_key_alt_name_destroy_all(head->alt_name);

        bson_free(head);
        head = tmp;
    }
}

static void _destroy_keys_returned(key_returned_t *head) {
    key_returned_t *tmp;

    while (head) {
        tmp = head->next;

        _mongocrypt_key_destroy(head->doc);
        _mongocrypt_buffer_cleanup(&head->decrypted_key_material);
        _mongocrypt_kms_ctx_cleanup(&head->kms);

        bson_free(head);
        head = tmp;
    }
}

void _mongocrypt_key_broker_cleanup(_mongocrypt_key_broker_t *kb) {
    if (!kb) {
        return;
    }
    mongocrypt_status_destroy(kb->status);
    _mongocrypt_buffer_cleanup(&kb->filter);
    /* Delete all linked lists */
    _destroy_keys_returned(kb->keys_returned);
    _destroy_keys_returned(kb->keys_cached);
    _destroy_key_requests(kb->key_requests);
    mc_mapof_kmsid_to_authrequest_destroy(kb->auth_requests);
}

void _mongocrypt_key_broker_add_test_key(_mongocrypt_key_broker_t *kb, const _mongocrypt_buffer_t *key_id) {
    key_returned_t *key_returned;
    _mongocrypt_key_doc_t *key_doc;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(key_id);

    key_doc = _mongocrypt_key_new();
    _mongocrypt_buffer_copy_to(key_id, &key_doc->id);

    key_returned = _key_returned_prepend(kb, &kb->keys_returned, key_doc);
    key_returned->decrypted = true;
    _mongocrypt_buffer_init(&key_returned->decrypted_key_material);
    _mongocrypt_buffer_resize(&key_returned->decrypted_key_material, MONGOCRYPT_KEY_LEN);
    // Initialize test key material with all zeros.
    memset(key_returned->decrypted_key_material.data, 0, MONGOCRYPT_KEY_LEN);
    _mongocrypt_key_destroy(key_doc);
    /* Hijack state and move directly to DONE. */
    kb->state = KB_DONE;
}

bool _mongocrypt_key_broker_restart(_mongocrypt_key_broker_t *kb) {
    BSON_ASSERT_PARAM(kb);
    if (kb->state != KB_DONE) {
        return _key_broker_fail_w_msg(kb, "_mongocrypt_key_broker_restart called in wrong state");
    }
    kb->state = KB_REQUESTING;
    _mongocrypt_buffer_cleanup(&kb->filter);
    _mongocrypt_buffer_init(&kb->filter);
    return true;
}
libmongocrypt-1.19.0/src/mongocrypt-key-private.h000066400000000000000000000065451521103432300220750ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_KEY_PRIVATE_H
#define MONGOCRYPT_KEY_PRIVATE_H

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-kek-private.h"
#include "mongocrypt-opts-private.h"

/* A linked list of key alt names */
typedef struct __mongocrypt_key_alt_name_t {
    struct __mongocrypt_key_alt_name_t *next;
    bson_value_t value;
} _mongocrypt_key_alt_name_t;

// `_mongocrypt_key_alt_name_t` inherits extended alignment from libbson. To dynamically allocate, use aligned
// allocation (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_key_alt_name_t,
                    BSON_ALIGNOF(_mongocrypt_key_alt_name_t) >= BSON_ALIGNOF(bson_value_t));

typedef struct {
    bson_t bson; /* original BSON for this key. */
    _mongocrypt_buffer_t id;
    _mongocrypt_key_alt_name_t *key_alt_names;
    _mongocrypt_buffer_t key_material;
    int64_t creation_date;
    int64_t update_date;
    _mongocrypt_kek_t kek;
} _mongocrypt_key_doc_t;

// `_mongocrypt_key_doc_t` inherits extended alignment from libbson. To dynamically allocate, use aligned allocation
// (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_key_doc_t, BSON_ALIGNOF(_mongocrypt_key_doc_t) >= BSON_ALIGNOF(bson_t));

_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_new(const bson_value_t *value);

bool _mongocrypt_key_alt_name_from_iter(const bson_iter_t *iter,
                                        _mongocrypt_key_alt_name_t **out,
                                        mongocrypt_status_t *status);

_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_copy_all(_mongocrypt_key_alt_name_t *list);
void _mongocrypt_key_alt_name_destroy_all(_mongocrypt_key_alt_name_t *list);
bool _mongocrypt_key_alt_name_intersects(_mongocrypt_key_alt_name_t *list_a, _mongocrypt_key_alt_name_t *list_b);
bool _mongocrypt_key_parse_owned(const bson_t *bson,
                                 _mongocrypt_key_doc_t *out,
                                 mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

_mongocrypt_key_doc_t *_mongocrypt_key_new(void);

void _mongocrypt_key_doc_copy_to(_mongocrypt_key_doc_t *src, _mongocrypt_key_doc_t *dst);

void _mongocrypt_key_destroy(_mongocrypt_key_doc_t *key);

const char *_mongocrypt_key_alt_name_get_string(_mongocrypt_key_alt_name_t *key_alt_name);

/* Begin: Functions for tests. */
/* Are the two lists equal without ordering.  */
bool _mongocrypt_key_alt_name_unique_list_equal(_mongocrypt_key_alt_name_t *list_a, _mongocrypt_key_alt_name_t *list_b);

/* For testing, construct a list of key alt names from variadic args */
_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_create(const char *name, ...);
#define _MONGOCRYPT_KEY_ALT_NAME_CREATE(...) _mongocrypt_key_alt_name_create(__VA_ARGS__, NULL)
/* End: Functions for tests. */

#endif /* MONGOCRYPT_KEY_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-key.c000066400000000000000000000303671521103432300204170ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-key-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_iter_document_as_bson

/* Check if two single entries are equal (i.e. ignore the 'next' pointer). */
static bool _one_key_alt_name_equal(_mongocrypt_key_alt_name_t *ptr_a, _mongocrypt_key_alt_name_t *ptr_b) {
    BSON_ASSERT_PARAM(ptr_a);
    BSON_ASSERT_PARAM(ptr_b);
    BSON_ASSERT(ptr_a->value.value_type == BSON_TYPE_UTF8);
    BSON_ASSERT(ptr_b->value.value_type == BSON_TYPE_UTF8);
    return 0 == strcmp(_mongocrypt_key_alt_name_get_string(ptr_a), _mongocrypt_key_alt_name_get_string(ptr_b));
}

static bool _find(_mongocrypt_key_alt_name_t *list, _mongocrypt_key_alt_name_t *entry) {
    BSON_ASSERT_PARAM(entry);

    for (; NULL != list; list = list->next) {
        if (_one_key_alt_name_equal(list, entry)) {
            return true;
        }
    }
    return false;
}

static uint32_t _list_len(_mongocrypt_key_alt_name_t *list) {
    uint32_t count = 0;

    while (NULL != list && count < UINT32_MAX) {
        count++;
        list = list->next;
    }
    return count;
}

static bool _check_unique(_mongocrypt_key_alt_name_t *list) {
    for (; NULL != list; list = list->next) {
        /* Check if we can find the current entry in the remaining. */
        if (_find(list->next, list)) {
            return false;
        }
    }
    return true;
}

static bool _parse_masterkey(bson_iter_t *iter, _mongocrypt_key_doc_t *out, mongocrypt_status_t *status) {
    bson_t kek_doc;

    BSON_ASSERT_PARAM(iter);
    BSON_ASSERT_PARAM(out);

    if (!BSON_ITER_HOLDS_DOCUMENT(iter)) {
        CLIENT_ERR("invalid 'masterKey', expected document");
        return false;
    }

    if (!mc_iter_document_as_bson(iter, &kek_doc, status)) {
        return false;
    }

    if (!_mongocrypt_kek_parse_owned(&kek_doc, &out->kek, status)) {
        return false;
    }
    return true;
}

bool _mongocrypt_key_alt_name_from_iter(const bson_iter_t *iter_in,
                                        _mongocrypt_key_alt_name_t **out,
                                        mongocrypt_status_t *status) {
    _mongocrypt_key_alt_name_t *key_alt_names = NULL, *tmp;
    bson_iter_t iter;

    BSON_ASSERT_PARAM(iter_in);
    BSON_ASSERT_PARAM(out);

    memcpy(&iter, iter_in, sizeof(iter));
    *out = NULL;

    /* A key parsed with no keyAltNames will have a zero'ed out bson value. Not
     * an error. */
    if (!BSON_ITER_HOLDS_ARRAY(&iter)) {
        CLIENT_ERR("malformed keyAltNames, expected array");
        return false;
    }

    if (!bson_iter_recurse(&iter, &iter)) {
        CLIENT_ERR("malformed keyAltNames, could not recurse into array");
        return false;
    }

    while (bson_iter_next(&iter)) {
        if (!BSON_ITER_HOLDS_UTF8(&iter)) {
            _mongocrypt_key_alt_name_destroy_all(key_alt_names);
            CLIENT_ERR("unexpected non-UTF8 keyAltName");
            return false;
        }

        tmp = _mongocrypt_key_alt_name_new(bson_iter_value(&iter));
        tmp->next = key_alt_names;
        key_alt_names = tmp;
    }

    if (!_check_unique(key_alt_names)) {
        _mongocrypt_key_alt_name_destroy_all(key_alt_names);
        CLIENT_ERR("unexpected duplicate keyAltNames");
        return false;
    }

    *out = key_alt_names;
    return true;
}

/* Takes ownership of all fields. */
bool _mongocrypt_key_parse_owned(const bson_t *bson, _mongocrypt_key_doc_t *out, mongocrypt_status_t *status) {
    bson_iter_t iter = {0};
    bool has_id = false, has_key_material = false, has_status = false, has_creation_date = false,
         has_update_date = false, has_master_key = false;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(out);

    if (!bson_validate(bson, BSON_VALIDATE_NONE, NULL) || !bson_iter_init(&iter, bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }

    bson_destroy(&out->bson);
    bson_copy_to(bson, &out->bson);

    while (bson_iter_next(&iter)) {
        const char *field;

        field = bson_iter_key(&iter);
        if (!field) {
            CLIENT_ERR("invalid BSON, could not retrieve field name");
            return false;
        }
        if (0 == strcmp("_id", field)) {
            has_id = true;
            if (!_mongocrypt_buffer_copy_from_uuid_iter(&out->id, &iter)) {
                CLIENT_ERR("invalid key, '_id' is not a UUID");
                return false;
            }
            continue;
        }

        /* keyAltNames (optional) */
        if (0 == strcmp("keyAltNames", field)) {
            if (!_mongocrypt_key_alt_name_from_iter(&iter, &out->key_alt_names, status)) {
                return false;
            }
            continue;
        }

        if (0 == strcmp("keyMaterial", field)) {
            has_key_material = true;
            if (!_mongocrypt_buffer_copy_from_binary_iter(&out->key_material, &iter)) {
                CLIENT_ERR("invalid 'keyMaterial', expected binary");
                return false;
            }
            if (out->key_material.subtype != BSON_SUBTYPE_BINARY) {
                CLIENT_ERR("invalid 'keyMaterial', expected subtype 0");
                return false;
            }
            continue;
        }

        if (0 == strcmp("masterKey", field)) {
            has_master_key = true;
            if (!_parse_masterkey(&iter, out, status)) {
                return false;
            }
            continue;
        }

        if (0 == strcmp("version", field)) {
            if (!BSON_ITER_HOLDS_INT(&iter)) {
                CLIENT_ERR("invalid 'version', expect int");
                return false;
            }
            if (bson_iter_as_int64(&iter) != 0) {
                CLIENT_ERR("unsupported key document version, only supports version=0");
                return false;
            }
            continue;
        }

        if (0 == strcmp("status", field)) {
            /* Don't need status. Check that it's present and ignore it. */
            has_status = true;
            continue;
        }

        if (0 == strcmp("creationDate", field)) {
            has_creation_date = true;

            if (!BSON_ITER_HOLDS_DATE_TIME(&iter)) {
                CLIENT_ERR("invalid 'creationDate', expect datetime");
                return false;
            }

            out->creation_date = bson_iter_date_time(&iter);
            continue;
        }

        if (0 == strcmp("updateDate", field)) {
            has_update_date = true;

            if (!BSON_ITER_HOLDS_DATE_TIME(&iter)) {
                CLIENT_ERR("invalid 'updateDate', expect datetime");
                return false;
            }

            out->update_date = bson_iter_date_time(&iter);
            continue;
        }

        CLIENT_ERR("unrecognized field '%s'", field);
        return false;
    }

    /* Check that required fields were set. */
    if (!has_id) {
        CLIENT_ERR("invalid key, no '_id'");
        return false;
    }

    if (!has_master_key) {
        CLIENT_ERR("invalid key, no 'masterKey'");
        return false;
    }

    if (!has_key_material) {
        CLIENT_ERR("invalid key, no 'keyMaterial'");
        return false;
    }

    if (!has_status) {
        CLIENT_ERR("invalid key, no 'status'");
        return false;
    }

    if (!has_creation_date) {
        CLIENT_ERR("invalid key, no 'creationDate'");
        return false;
    }

    if (!has_update_date) {
        CLIENT_ERR("invalid key, no 'updateDate'");
        return false;
    }

    return true;
}

_mongocrypt_key_doc_t *_mongocrypt_key_new(void) {
    _mongocrypt_key_doc_t *key_doc;

    key_doc = BSON_ALIGNED_ALLOC(_mongocrypt_key_doc_t);
    // Use two sets of braces to avoid erroneous missing-braces warning in GCC. Refer:
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
    *key_doc = (_mongocrypt_key_doc_t){{0}};
    bson_init(&key_doc->bson);

    return key_doc;
}

void _mongocrypt_key_destroy(_mongocrypt_key_doc_t *key) {
    if (!key) {
        return;
    }

    _mongocrypt_buffer_cleanup(&key->id);
    _mongocrypt_key_alt_name_destroy_all(key->key_alt_names);
    _mongocrypt_buffer_cleanup(&key->key_material);
    _mongocrypt_kek_cleanup(&key->kek);

    bson_destroy(&key->bson);
    bson_free(key);
}

void _mongocrypt_key_doc_copy_to(_mongocrypt_key_doc_t *src, _mongocrypt_key_doc_t *dst) {
    BSON_ASSERT_PARAM(src);
    BSON_ASSERT_PARAM(dst);

    _mongocrypt_buffer_copy_to(&src->id, &dst->id);
    _mongocrypt_buffer_copy_to(&src->key_material, &dst->key_material);
    dst->key_alt_names = _mongocrypt_key_alt_name_copy_all(src->key_alt_names);
    bson_destroy(&dst->bson);
    bson_copy_to(&src->bson, &dst->bson);
    _mongocrypt_kek_copy_to(&src->kek, &dst->kek);
    dst->creation_date = src->creation_date;
    dst->update_date = src->update_date;
}

_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_copy_all(_mongocrypt_key_alt_name_t *ptr) {
    _mongocrypt_key_alt_name_t *ptr_copy = NULL, *head = NULL;

    while (ptr) {
        _mongocrypt_key_alt_name_t *copied;
        copied = bson_malloc0(sizeof(*copied));
        BSON_ASSERT(copied);

        bson_value_copy(&ptr->value, &copied->value);

        if (!ptr_copy) {
            ptr_copy = copied;
            head = ptr_copy;
        } else {
            ptr_copy->next = copied;
            ptr_copy = ptr_copy->next;
        }
        ptr = ptr->next;
    }
    return head;
}

void _mongocrypt_key_alt_name_destroy_all(_mongocrypt_key_alt_name_t *ptr) {
    _mongocrypt_key_alt_name_t *next;
    while (ptr) {
        next = ptr->next;
        bson_value_destroy(&ptr->value);
        bson_free(ptr);
        ptr = next;
    }
}

bool _mongocrypt_key_alt_name_intersects(_mongocrypt_key_alt_name_t *ptr_a, _mongocrypt_key_alt_name_t *ptr_b) {
    _mongocrypt_key_alt_name_t *orig_ptr_b = ptr_b;

    if (!ptr_a || !ptr_b) {
        return false;
    }

    for (; ptr_a; ptr_a = ptr_a->next) {
        for (ptr_b = orig_ptr_b; ptr_b; ptr_b = ptr_b->next) {
            if (_one_key_alt_name_equal(ptr_a, ptr_b)) {
                return true;
            }
        }
    }
    return false;
}

_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_create(const char *name, ...) {
    va_list args;
    const char *arg_ptr;
    _mongocrypt_key_alt_name_t *head, *prev;

    head = NULL;
    prev = NULL;
    va_start(args, name);
    arg_ptr = name;
    while (arg_ptr) {
        _mongocrypt_key_alt_name_t *curr;

        curr = bson_malloc0(sizeof(*curr));
        BSON_ASSERT(curr);

        curr->value.value_type = BSON_TYPE_UTF8;
        curr->value.value.v_utf8.str = bson_strdup(arg_ptr);
        curr->value.value.v_utf8.len = (uint32_t)strlen(arg_ptr);
        if (!prev) {
            head = curr;
        } else {
            prev->next = curr;
        }

        arg_ptr = va_arg(args, const char *);
        prev = curr;
    }
    va_end(args);

    return head;
}

_mongocrypt_key_alt_name_t *_mongocrypt_key_alt_name_new(const bson_value_t *value) {
    BSON_ASSERT_PARAM(value);

    _mongocrypt_key_alt_name_t *name = BSON_ALIGNED_ALLOC(_mongocrypt_key_alt_name_t);
    *name = (_mongocrypt_key_alt_name_t){0};
    BSON_ASSERT(name);

    bson_value_copy(value, &name->value);
    return name;
}

bool _mongocrypt_key_alt_name_unique_list_equal(_mongocrypt_key_alt_name_t *list_a,
                                                _mongocrypt_key_alt_name_t *list_b) {
    _mongocrypt_key_alt_name_t *ptr;

    BSON_ASSERT(_check_unique(list_a));
    BSON_ASSERT(_check_unique(list_b));
    if (_list_len(list_a) != _list_len(list_b)) {
        return false;
    }
    for (ptr = list_a; NULL != ptr; ptr = ptr->next) {
        if (!_find(list_b, ptr)) {
            return false;
        }
    }
    return true;
}

const char *_mongocrypt_key_alt_name_get_string(_mongocrypt_key_alt_name_t *key_alt_name) {
    BSON_ASSERT_PARAM(key_alt_name);

    return key_alt_name->value.value.v_utf8.str;
}
libmongocrypt-1.19.0/src/mongocrypt-kms-ctx-private.h000066400000000000000000000202551521103432300226650ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_KMX_CTX_PRIVATE_H
#define MONGOCRYPT_KMX_CTX_PRIVATE_H

#include "kms_message/kms_message.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-cache-key-private.h"
#include "mongocrypt-compat.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-endpoint-private.h"
#include "mongocrypt-key-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt.h"

struct __mongocrypt_ctx_opts_t;

typedef enum {
    MONGOCRYPT_KMS_AWS_ENCRYPT,
    MONGOCRYPT_KMS_AWS_DECRYPT,
    MONGOCRYPT_KMS_AZURE_OAUTH,
    MONGOCRYPT_KMS_AZURE_WRAPKEY,
    MONGOCRYPT_KMS_AZURE_UNWRAPKEY,
    MONGOCRYPT_KMS_GCP_OAUTH,
    MONGOCRYPT_KMS_GCP_ENCRYPT,
    MONGOCRYPT_KMS_GCP_DECRYPT,
    MONGOCRYPT_KMS_KMIP_REGISTER,
    MONGOCRYPT_KMS_KMIP_ACTIVATE,
    MONGOCRYPT_KMS_KMIP_GET,
    MONGOCRYPT_KMS_KMIP_CREATE,
    MONGOCRYPT_KMS_KMIP_ENCRYPT,
    MONGOCRYPT_KMS_KMIP_DECRYPT,
} _kms_request_type_t;

struct _mongocrypt_kms_ctx_t {
    kms_request_t *req;
    _kms_request_type_t req_type;
    kms_response_parser_t *parser;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t msg;
    _mongocrypt_buffer_t result;
    char *endpoint;
    _mongocrypt_log_t *log;
    char *kmsid;
    int64_t sleep_usec;
    int attempts;
    bool retry_enabled;
    bool should_retry;
};

static const int kms_max_attempts = 3;

bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          _mongocrypt_key_doc_t *key,
                                          _mongocrypt_crypto_t *crypto,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          struct __mongocrypt_ctx_opts_t *ctx_opts,
                                          _mongocrypt_buffer_t *decrypted_key_material,
                                          _mongocrypt_crypto_t *crypto,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_result(mongocrypt_kms_ctx_t *kms, _mongocrypt_buffer_t *out) MONGOCRYPT_WARN_UNUSED_RESULT;

void _mongocrypt_kms_ctx_cleanup(mongocrypt_kms_ctx_t *kms);

bool _mongocrypt_kms_ctx_init_azure_auth(mongocrypt_kms_ctx_t *kms,
                                         const mc_kms_creds_t *kc,
                                         _mongocrypt_endpoint_t *key_vault_endpoint,
                                         const char *kmsid,
                                         _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_azure_wrapkey(mongocrypt_kms_ctx_t *kms,
                                            _mongocrypt_opts_kms_providers_t *kms_providers,
                                            struct __mongocrypt_ctx_opts_t *ctx_opts,
                                            const char *access_token,
                                            _mongocrypt_buffer_t *plaintext_key_material,
                                            const char *kmsid,
                                            _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_azure_unwrapkey(mongocrypt_kms_ctx_t *kms,
                                              _mongocrypt_opts_kms_providers_t *kms_providers,
                                              const char *access_token,
                                              _mongocrypt_key_doc_t *key,
                                              const char *kmsid,
                                              _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
                                       _mongocrypt_opts_t *crypt_opts,
                                       const mc_kms_creds_t *kc,
                                       _mongocrypt_endpoint_t *kms_endpoint,
                                       const char *kmsid,
                                       _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_gcp_encrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          struct __mongocrypt_ctx_opts_t *ctx_opts,
                                          const char *access_token,
                                          _mongocrypt_buffer_t *plaintext_key_material,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_gcp_decrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          const char *access_token,
                                          _mongocrypt_key_doc_t *key,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_kmip_register(mongocrypt_kms_ctx_t *kms,
                                            const _mongocrypt_endpoint_t *endpoint,
                                            const uint8_t *secretdata,
                                            uint32_t secretdata_len,

                                            const char *kmsid,
                                            _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_kmip_activate(mongocrypt_kms_ctx_t *kms,
                                            const _mongocrypt_endpoint_t *endpoint,
                                            const char *unique_identifier,
                                            const char *kmsid,
                                            _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_kmip_get(mongocrypt_kms_ctx_t *kms,
                                       const _mongocrypt_endpoint_t *endpoint,
                                       const char *unique_identifier,
                                       const char *kmsid,
                                       _mongocrypt_log_t *log) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_kms_ctx_init_kmip_create(mongocrypt_kms_ctx_t *kms,
                                          const _mongocrypt_endpoint_t *endpoint,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log);

bool _mongocrypt_kms_ctx_init_kmip_encrypt(mongocrypt_kms_ctx_t *kms,
                                           const _mongocrypt_endpoint_t *endpoint,
                                           const char *unique_identifier,
                                           const char *kmsid,
                                           _mongocrypt_buffer_t *plaintext,
                                           _mongocrypt_log_t *log);

bool _mongocrypt_kms_ctx_init_kmip_decrypt(mongocrypt_kms_ctx_t *kms,
                                           const _mongocrypt_endpoint_t *endpoint,
                                           const char *kmsid,
                                           _mongocrypt_key_doc_t *key,
                                           _mongocrypt_log_t *log);

#endif /* MONGOCRYPT_KMX_CTX_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-kms-ctx.c000066400000000000000000002153371521103432300212170ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "kms_message/kms_kmip_request.h"
#include "kms_message/kms_response_parser.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-endpoint-private.h"
#include "mongocrypt-kek-private.h"
#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-status-private.h"
#include "mongocrypt-util-private.h"
#include "mongocrypt.h"
#include 
#include 
#include 
#include 

/* Sadly, Windows does not define SSIZE_MAX. It is defined in bson-compat.h,
 * but only since 1.22.x, so copy this from bson-compat.h for now. */
#ifndef SSIZE_MAX
#define SSIZE_MAX (ssize_t)((((size_t)0x01u) << (sizeof(ssize_t) * (size_t)CHAR_BIT - 1u)) - 1u)
#endif

typedef struct {
    mongocrypt_status_t *status;
    void *ctx;
} ctx_with_status_t;

/* Before we've read the Content-Length header in an HTTP response,
 * we don't know how many bytes we'll need. So return this value
 * in kms_ctx_bytes_needed until we are fed the Content-Length.
 */
#define DEFAULT_MAX_KMS_BYTE_REQUEST 1024
#define SHA256_LEN 32
#define DEFAULT_HTTPS_PORT "443"
#define DEFAULT_KMIP_PORT "5696"

static bool _sha256(void *ctx, const char *input, size_t len, unsigned char *hash_out) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(input);
    BSON_ASSERT_PARAM(hash_out);

    bool ret;
    ctx_with_status_t *ctx_with_status = (ctx_with_status_t *)ctx;
    _mongocrypt_crypto_t *crypto;
    mongocrypt_binary_t *plaintext, *out;

    crypto = (_mongocrypt_crypto_t *)ctx_with_status->ctx;
    BSON_ASSERT(crypto);
    BSON_ASSERT(len <= UINT32_MAX);
    plaintext = mongocrypt_binary_new_from_data((uint8_t *)input, (uint32_t)len);
    out = mongocrypt_binary_new();

    out->data = hash_out;
    out->len = SHA256_LEN;

    ret = crypto->sha_256(crypto->ctx, plaintext, out, ctx_with_status->status);

    mongocrypt_binary_destroy(plaintext);
    mongocrypt_binary_destroy(out);
    return ret;
}

static bool
_sha256_hmac(void *ctx, const char *key_input, size_t key_len, const char *input, size_t len, unsigned char *hash_out) {
    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(key_input);
    BSON_ASSERT_PARAM(input);
    BSON_ASSERT_PARAM(hash_out);

    ctx_with_status_t *ctx_with_status = (ctx_with_status_t *)ctx;
    _mongocrypt_crypto_t *crypto;
    mongocrypt_binary_t *key, *plaintext, *out;
    bool ret;

    crypto = (_mongocrypt_crypto_t *)ctx_with_status->ctx;
    BSON_ASSERT(crypto);

    BSON_ASSERT(key_len <= UINT32_MAX);
    key = mongocrypt_binary_new_from_data((uint8_t *)key_input, (uint32_t)key_len);
    BSON_ASSERT(len <= UINT32_MAX);
    plaintext = mongocrypt_binary_new_from_data((uint8_t *)input, (uint32_t)len);
    out = mongocrypt_binary_new();

    out->data = hash_out;
    out->len = SHA256_LEN;

    ret = crypto->hmac_sha_256(crypto->ctx, key, plaintext, out, ctx_with_status->status);

    mongocrypt_binary_destroy(key);
    mongocrypt_binary_destroy(plaintext);
    mongocrypt_binary_destroy(out);
    return ret;
}

static void
_set_kms_crypto_hooks(_mongocrypt_crypto_t *crypto, ctx_with_status_t *ctx_with_status, kms_request_opt_t *opts) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(ctx_with_status);
    BSON_ASSERT_PARAM(opts);

    if (crypto->hooks_enabled) {
        kms_request_opt_set_crypto_hooks(opts, _sha256, _sha256_hmac, ctx_with_status);
    }
}

static bool is_kms(_kms_request_type_t kms_type) {
    return kms_type == MONGOCRYPT_KMS_KMIP_REGISTER || kms_type == MONGOCRYPT_KMS_KMIP_ACTIVATE
        || kms_type == MONGOCRYPT_KMS_KMIP_GET || kms_type == MONGOCRYPT_KMS_KMIP_ENCRYPT
        || kms_type == MONGOCRYPT_KMS_KMIP_DECRYPT || kms_type == MONGOCRYPT_KMS_KMIP_CREATE;
}

static void
_init_common(mongocrypt_kms_ctx_t *kms, _mongocrypt_log_t *log, _kms_request_type_t kms_type, const char *kmsid) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(kmsid);

    kms->kmsid = bson_strdup(kmsid);

    if (is_kms(kms_type)) {
        kms->parser = kms_kmip_response_parser_new(NULL /* reserved */);
    } else {
        kms->parser = kms_response_parser_new();
    }
    kms->log = log;
    kms->status = mongocrypt_status_new();
    kms->req_type = kms_type;
    _mongocrypt_buffer_init(&kms->result);
    kms->sleep_usec = 0;
    kms->attempts = 0;
    kms->should_retry = false;
}

bool _mongocrypt_kms_ctx_init_aws_decrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          _mongocrypt_key_doc_t *key,
                                          _mongocrypt_crypto_t *crypto,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(kms_providers);
    BSON_ASSERT_PARAM(crypto);

    kms_request_opt_t *opt;
    mongocrypt_status_t *status;
    ctx_with_status_t ctx_with_status;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_AWS_DECRYPT, kmsid);
    status = kms->status;
    ctx_with_status.ctx = crypto;
    ctx_with_status.status = mongocrypt_status_new();

    if (!key->kek.kms_provider) {
        CLIENT_ERR("no kms provider specified on key");
        goto done;
    }

    if (MONGOCRYPT_KMS_PROVIDER_AWS != key->kek.kms_provider) {
        CLIENT_ERR("expected aws kms provider");
        goto done;
    }

    if (!key->kek.provider.aws.region) {
        CLIENT_ERR("no key region provided");
        goto done;
    }

    mc_kms_creds_t kc;
    if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, key->kek.kmsid, &kc)) {
        CLIENT_ERR("KMS provider `%s` is not configured", key->kek.kmsid);
        goto done;
    }
    BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AWS);

    if (!kc.value.aws.access_key_id) {
        CLIENT_ERR("aws access key id not provided");
        goto done;
    }

    if (!kc.value.aws.secret_access_key) {
        CLIENT_ERR("aws secret access key not provided");
        goto done;
    }

    /* create the KMS request. */
    opt = kms_request_opt_new();
    BSON_ASSERT(opt);

    _set_kms_crypto_hooks(crypto, &ctx_with_status, opt);
    kms_request_opt_set_connection_close(opt, true);

    kms->req = kms_decrypt_request_new(key->key_material.data, key->key_material.len, opt);

    kms_request_opt_destroy(opt);
    if (!kms_request_set_service(kms->req, "kms")) {
        CLIENT_ERR("failed to set service: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    if (kc.value.aws.session_token) {
        if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kc.value.aws.session_token)) {
            CLIENT_ERR("failed to set session token: %s", kms_request_get_error(kms->req));
            _mongocrypt_status_append(status, ctx_with_status.status);
            goto done;
        }
    }

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    /* If an endpoint was set, override the default Host header. */
    if (key->kek.provider.aws.endpoint) {
        if (!kms_request_add_header_field(kms->req, "Host", key->kek.provider.aws.endpoint->host_and_port)) {
            CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
            _mongocrypt_status_append(status, ctx_with_status.status);
            goto done;
        }
    }

    if (!kms_request_set_region(kms->req, key->kek.provider.aws.region)) {
        CLIENT_ERR("failed to set region: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    if (!kms_request_set_access_key_id(kms->req, kc.value.aws.access_key_id)) {
        CLIENT_ERR("failed to set aws access key id: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }
    if (!kms_request_set_secret_key(kms->req, kc.value.aws.secret_access_key)) {
        CLIENT_ERR("failed to set aws secret access key: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)kms_request_get_signed(kms->req);
    if (!kms->msg.data) {
        CLIENT_ERR("failed to create KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }
    kms->msg.len = (uint32_t)strlen((char *)kms->msg.data);
    kms->msg.owned = true;

    if (key->kek.provider.aws.endpoint) {
        kms->endpoint = bson_strdup(key->kek.provider.aws.endpoint->host_and_port);
    } else {
        /* construct the endpoint from AWS region. */
        kms->endpoint = bson_strdup_printf("kms.%s.amazonaws.com", key->kek.provider.aws.region);
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    ret = true;
done:
    mongocrypt_status_destroy(ctx_with_status.status);

    return ret;
}

bool _mongocrypt_kms_ctx_init_aws_encrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          _mongocrypt_ctx_opts_t *ctx_opts,
                                          _mongocrypt_buffer_t *plaintext_key_material,
                                          _mongocrypt_crypto_t *crypto,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(ctx_opts);
    BSON_ASSERT_PARAM(kms_providers);
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(plaintext_key_material);

    kms_request_opt_t *opt;
    mongocrypt_status_t *status;
    ctx_with_status_t ctx_with_status;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_AWS_ENCRYPT, kmsid);
    status = kms->status;
    ctx_with_status.ctx = crypto;
    ctx_with_status.status = mongocrypt_status_new();

    if (MONGOCRYPT_KMS_PROVIDER_AWS != ctx_opts->kek.kms_provider) {
        CLIENT_ERR("expected aws kms provider");
        goto done;
    }

    if (!ctx_opts->kek.provider.aws.region) {
        CLIENT_ERR("no key region provided");
        goto done;
    }

    if (!ctx_opts->kek.provider.aws.cmk) {
        CLIENT_ERR("no aws cmk provided");
        goto done;
    }

    mc_kms_creds_t kc;
    if (!_mongocrypt_opts_kms_providers_lookup(kms_providers, ctx_opts->kek.kmsid, &kc)) {
        CLIENT_ERR("KMS provider `%s` is not configured", ctx_opts->kek.kmsid);
        goto done;
    }
    BSON_ASSERT(kc.type == MONGOCRYPT_KMS_PROVIDER_AWS);

    if (!kc.value.aws.access_key_id) {
        CLIENT_ERR("aws access key id not provided");
        goto done;
    }

    if (!kc.value.aws.secret_access_key) {
        CLIENT_ERR("aws secret access key not provided");
        goto done;
    }

    /* create the KMS request. */
    opt = kms_request_opt_new();
    BSON_ASSERT(opt);

    _set_kms_crypto_hooks(crypto, &ctx_with_status, opt);
    kms_request_opt_set_connection_close(opt, true);

    kms->req = kms_encrypt_request_new(plaintext_key_material->data,
                                       plaintext_key_material->len,
                                       ctx_opts->kek.provider.aws.cmk,
                                       opt);

    kms_request_opt_destroy(opt);
    if (!kms_request_set_service(kms->req, "kms")) {
        CLIENT_ERR("failed to set service: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    if (kc.value.aws.session_token) {
        if (!kms_request_add_header_field(kms->req, "X-Amz-Security-Token", kc.value.aws.session_token)) {
            CLIENT_ERR("failed to set session token: %s", kms_request_get_error(kms->req));
            _mongocrypt_status_append(status, ctx_with_status.status);
            goto done;
        }
    }

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    /* If an endpoint was set, override the default Host header. */
    if (ctx_opts->kek.provider.aws.endpoint) {
        if (!kms_request_add_header_field(kms->req, "Host", ctx_opts->kek.provider.aws.endpoint->host)) {
            CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
            _mongocrypt_status_append(status, ctx_with_status.status);
            goto done;
        }
    }

    if (!kms_request_set_region(kms->req, ctx_opts->kek.provider.aws.region)) {
        CLIENT_ERR("failed to set region: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    if (!kms_request_set_access_key_id(kms->req, kc.value.aws.access_key_id)) {
        CLIENT_ERR("failed to set aws access key id: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }
    if (!kms_request_set_secret_key(kms->req, kc.value.aws.secret_access_key)) {
        CLIENT_ERR("failed to set aws secret access key: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }

    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)kms_request_get_signed(kms->req);
    if (!kms->msg.data) {
        CLIENT_ERR("failed to create KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto done;
    }
    kms->msg.len = (uint32_t)strlen((char *)kms->msg.data);
    kms->msg.owned = true;

    /* construct the endpoint */
    if (ctx_opts->kek.provider.aws.endpoint) {
        kms->endpoint = bson_strdup(ctx_opts->kek.provider.aws.endpoint->host_and_port);
    } else {
        kms->endpoint = bson_strdup_printf("kms.%s.amazonaws.com", ctx_opts->kek.provider.aws.region);
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    ret = true;
done:
    mongocrypt_status_destroy(ctx_with_status.status);
    return ret;
}

uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms) {
    int want_bytes;

    if (!kms) {
        return 0;
    }
    /* TODO: an oddity of kms-message. After retrieving the result, it
     * resets the parser. */
    if (!mongocrypt_status_ok(kms->status) || !_mongocrypt_buffer_empty(&kms->result)) {
        return 0;
    }
    if (kms->should_retry) {
        return 0;
    }
    want_bytes = kms_response_parser_wants_bytes(kms->parser, DEFAULT_MAX_KMS_BYTE_REQUEST);
    BSON_ASSERT(want_bytes >= 0);
    return (uint32_t)want_bytes;
}

int64_t mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t *kms) {
    if (!kms) {
        return 0;
    }
    return kms->sleep_usec;
}

static void
_handle_non200_http_status(int http_status, const char *body, size_t body_len, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(body);

    /* 1xx, 2xx, and 3xx HTTP status codes are not errors, but we only
     * support handling 200 response. */
    if (http_status < 400) {
        CLIENT_ERR("Unsupported HTTP code in KMS response. HTTP status=%d. "
                   "Response body=\n%s",
                   http_status,
                   body);
        return;
    }

    /* Either empty body or body containing JSON with error message. */
    if (body_len == 0) {
        CLIENT_ERR("Error in KMS response. HTTP status=%d. Empty body.", http_status);
        return;
    }

    CLIENT_ERR("Error in KMS response. HTTP status=%d. Response body=\n%s", http_status, body);
}

static int64_t backoff_time_usec(int64_t attempts) {
    static bool seeded = false;
    if (!seeded) {
        srand((uint32_t)time(NULL));
        seeded = true;
    }

    /* Exponential backoff with jitter. */
    const int64_t base = 200000;  /* 0.2 seconds */
    const int64_t max = 20000000; /* 20 seconds */
    BSON_ASSERT(attempts > 0);
    int64_t backoff = base * ((int64_t)1 << (attempts - 1));
    if (backoff > max) {
        backoff = max;
    }

    /* Full jitter: between 1 and current max */
    return (int64_t)((double)rand() / (double)RAND_MAX * (double)backoff) + 1;
}

static bool should_retry_http(int http_status, _kms_request_type_t t) {
    static const int retryable_aws[] = {408, 429, 500, 502, 503, 509};
    static const int retryable_azure[] = {408, 429, 500, 502, 503, 504};
    if (t == MONGOCRYPT_KMS_AWS_ENCRYPT || t == MONGOCRYPT_KMS_AWS_DECRYPT) {
        for (size_t i = 0; i < sizeof(retryable_aws) / sizeof(retryable_aws[0]); i++) {
            if (http_status == retryable_aws[i]) {
                return true;
            }
        }
    } else if (t == MONGOCRYPT_KMS_AZURE_WRAPKEY || t == MONGOCRYPT_KMS_AZURE_UNWRAPKEY
               || t == MONGOCRYPT_KMS_AZURE_OAUTH) {
        for (size_t i = 0; i < sizeof(retryable_azure) / sizeof(retryable_azure[0]); i++) {
            if (http_status == retryable_azure[i]) {
                return true;
            }
        }
    } else if (t == MONGOCRYPT_KMS_GCP_ENCRYPT || t == MONGOCRYPT_KMS_GCP_DECRYPT || t == MONGOCRYPT_KMS_GCP_OAUTH) {
        if (http_status == 408 || http_status == 429 || http_status / 500 == 1) {
            return true;
        }
    }
    return false;
}

static void set_retry(mongocrypt_kms_ctx_t *kms) {
    kms->should_retry = true;
    kms->attempts++;
    kms->sleep_usec = backoff_time_usec(kms->attempts);
    if (kms->parser) {
        kms_response_parser_reset(kms->parser);
    }
}

/* An AWS KMS context has received full response. Parse out the result or error.
 */
static bool _ctx_done_aws(mongocrypt_kms_ctx_t *kms, const char *json_field) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(json_field);

    kms_response_t *response = NULL;
    const char *body;
    bson_t body_bson = BSON_INITIALIZER;
    bool ret;
    bson_error_t bson_error;
    bson_iter_t iter;
    uint32_t b64_strlen;
    char *b64_str;
    int http_status;
    size_t body_len;
    int result_len;
    mongocrypt_status_t *status;

    status = kms->status;
    ret = false;
    /* Parse out the {en|de}crypted result. */
    http_status = kms_response_parser_status(kms->parser);
    response = kms_response_parser_get_response(kms->parser);
    if (!response) {
        CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
        goto fail;
    }
    body = kms_response_get_body(response, &body_len);

    if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
        if (kms->attempts >= kms_max_attempts) {
            // Wrap error to indicate maximum retries occurred.
            _handle_non200_http_status(http_status, body, body_len, status);
            CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
                       kms_max_attempts,
                       mongocrypt_status_message(status, NULL));
            goto fail;
        } else {
            ret = true;
            set_retry(kms);
            goto fail;
        }
    }

    if (http_status != 200) {
        _handle_non200_http_status(http_status, body, body_len, status);
        goto fail;
    }

    /* If HTTP response succeeded (status 200) then body should contain JSON.
     */
    bson_destroy(&body_bson);
    if (body_len > (size_t)SSIZE_MAX) {
        CLIENT_ERR("Error parsing JSON in KMS response. Response body exceeds maximum supported length");
        bson_init(&body_bson);
        goto fail;
    }
    if (!bson_init_from_json(&body_bson, body, (ssize_t)body_len, &bson_error)) {
        CLIENT_ERR("Error parsing JSON in KMS response '%s'. "
                   "HTTP status=%d. Response body=\n%s",
                   bson_error.message,
                   http_status,
                   body);
        bson_init(&body_bson);
        goto fail;
    }

    if (!bson_iter_init_find(&iter, &body_bson, json_field) || !BSON_ITER_HOLDS_UTF8(&iter)) {
        CLIENT_ERR("KMS JSON response does not include field '%s'. HTTP status=%d. "
                   "Response body=\n%s",
                   json_field,
                   http_status,
                   body);
        goto fail;
    }

    b64_str = (char *)bson_iter_utf8(&iter, &b64_strlen);
    BSON_ASSERT(b64_str);
    uint8_t *result_data = bson_malloc((size_t)b64_strlen + 1u);
    BSON_ASSERT(result_data);

    result_len = kms_message_b64_pton(b64_str, result_data, b64_strlen);
    if (result_len < 0) {
        CLIENT_ERR("Failed to base64 decode response. HTTP status=%d. Response body=\n%s", http_status, body);
        bson_free(result_data);
        goto fail;
    }
    kms->result.data = result_data;
    kms->result.len = (uint32_t)result_len;
    kms->result.owned = true;
    ret = true;
fail:
    bson_destroy(&body_bson);
    kms_response_destroy(response);
    return ret;
}

/* A Azure/GCP oauth KMS context has received full response. Parse out the
 * bearer token or error. */
static bool _ctx_done_oauth(mongocrypt_kms_ctx_t *kms) {
    BSON_ASSERT_PARAM(kms);

    kms_response_t *response = NULL;
    const char *body;
    bson_t *bson_body = NULL;
    bool ret;
    bson_error_t bson_error;
    bson_iter_t iter;
    int http_status;
    size_t body_len;
    mongocrypt_status_t *status;

    status = kms->status;
    ret = false;
    /* Parse out the oauth token result (or error). */
    http_status = kms_response_parser_status(kms->parser);
    response = kms_response_parser_get_response(kms->parser);
    if (!response) {
        CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
        goto fail;
    }
    body = kms_response_get_body(response, &body_len);

    if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
        if (kms->attempts >= kms_max_attempts) {
            // Wrap error to indicate maximum retries occurred.
            _handle_non200_http_status(http_status, body, body_len, status);
            CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
                       kms_max_attempts,
                       mongocrypt_status_message(status, NULL));
            goto fail;
        } else {
            ret = true;
            set_retry(kms);
            goto fail;
        }
    }

    if (body_len == 0) {
        CLIENT_ERR("Empty KMS response. HTTP status=%d", http_status);
        goto fail;
    }

    if (body_len > (size_t)SSIZE_MAX) {
        CLIENT_ERR("Error parsing JSON in KMS response. Response body exceeds maximum supported length");
        goto fail;
    }
    bson_body = bson_new_from_json((const uint8_t *)body, (ssize_t)body_len, &bson_error);
    if (!bson_body) {
        CLIENT_ERR("Error parsing JSON in KMS response '%s'. "
                   "HTTP status=%d. Response body=\n%s",
                   bson_error.message,
                   http_status,
                   body);
        goto fail;
    }

    if (http_status != 200) {
        _handle_non200_http_status(http_status, body, body_len, status);
        goto fail;
    }

    if (!bson_iter_init_find(&iter, bson_body, "access_token") || !BSON_ITER_HOLDS_UTF8(&iter)) {
        CLIENT_ERR("Invalid KMS response. KMS JSON response does not include "
                   "field 'access_token'. "
                   "HTTP status=%d. Response body=\n%s",
                   http_status,
                   body);
        goto fail;
    }

    /* Store the full response, to include the expiration time. */
    _mongocrypt_buffer_steal_from_bson(&kms->result, bson_body);
    bson_body = NULL;

    ret = true;
fail:
    bson_destroy(bson_body);
    kms_response_destroy(response);
    return ret;
}

/* An Azure oauth KMS context has received full response. Parse out the bearer
 * token or error. */
static bool _ctx_done_azure_wrapkey_unwrapkey(mongocrypt_kms_ctx_t *kms) {
    BSON_ASSERT_PARAM(kms);

    kms_response_t *response = NULL;
    const char *body;
    bson_t *bson_body = NULL;
    bool ret;
    bson_error_t bson_error;
    bson_iter_t iter;
    int http_status;
    size_t body_len;
    mongocrypt_status_t *status;
    const char *b64url_data = NULL;
    uint32_t b64url_len;
    char *b64_data = NULL;
    uint32_t b64_len;
    int result_len;

    status = kms->status;
    ret = false;
    /* Parse out the oauth token result (or error). */
    http_status = kms_response_parser_status(kms->parser);
    response = kms_response_parser_get_response(kms->parser);
    if (!response) {
        CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
        goto fail;
    }
    body = kms_response_get_body(response, &body_len);

    if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
        if (kms->attempts >= kms_max_attempts) {
            // Wrap error to indicate maximum retries occurred.
            _handle_non200_http_status(http_status, body, body_len, status);
            CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
                       kms_max_attempts,
                       mongocrypt_status_message(status, NULL));
            goto fail;
        } else {
            ret = true;
            set_retry(kms);
            goto fail;
        }
    }

    if (body_len == 0) {
        CLIENT_ERR("Empty KMS response. HTTP status=%d", http_status);
        goto fail;
    }

    if (body_len > (size_t)SSIZE_MAX) {
        CLIENT_ERR("Error parsing JSON in KMS response. Response body exceeds maximum supported length");
        goto fail;
    }
    bson_body = bson_new_from_json((const uint8_t *)body, (ssize_t)body_len, &bson_error);
    if (!bson_body) {
        CLIENT_ERR("Error parsing JSON in KMS response '%s'. "
                   "HTTP status=%d. Response body=\n%s",
                   bson_error.message,
                   http_status,
                   body);
        goto fail;
    }

    if (http_status != 200) {
        _handle_non200_http_status(http_status, body, body_len, status);
        goto fail;
    }

    if (!bson_iter_init_find(&iter, bson_body, "value") || !BSON_ITER_HOLDS_UTF8(&iter)) {
        CLIENT_ERR("KMS JSON response does not include field 'value'. HTTP status=%d. "
                   "Response body=\n%s",
                   http_status,
                   body);
        goto fail;
    }

    b64url_data = bson_iter_utf8(&iter, &b64url_len);
    BSON_ASSERT(b64url_len <= UINT32_MAX - 4u);
    /* add four for padding. */
    b64_len = b64url_len + 4;
    b64_data = bson_malloc0(b64_len);
    if (kms_message_b64url_to_b64(b64url_data, b64url_len, b64_data, b64_len) == -1) {
        CLIENT_ERR("Error converting base64url to base64");
        goto fail;
    }

    uint8_t *result_data = bson_malloc(b64_len);
    BSON_ASSERT(result_data);
    result_len = kms_message_b64_pton(b64_data, result_data, b64_len);
    if (result_len < 0) {
        CLIENT_ERR("Failed to base64 decode response. HTTP status=%d. Response body=\n%s", http_status, body);
        bson_free(result_data);
        goto fail;
    }

    kms->result.data = result_data;
    kms->result.len = (uint32_t)result_len;
    kms->result.owned = true;

    ret = true;
fail:
    bson_destroy(bson_body);
    kms_response_destroy(response);
    bson_free(b64_data);
    return ret;
}

/* A GCP KMS context has received full response. Parse out the result or error.
 */
static bool _ctx_done_gcp(mongocrypt_kms_ctx_t *kms, const char *json_field) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(json_field);

    kms_response_t *response = NULL;
    const char *body;
    bson_t body_bson = BSON_INITIALIZER;
    bool ret;
    bson_error_t bson_error;
    bson_iter_t iter;
    size_t outlen;
    char *b64_str;
    int http_status;
    size_t body_len;
    mongocrypt_status_t *status;

    status = kms->status;
    ret = false;
    /* Parse out the {en|de}crypted result. */
    http_status = kms_response_parser_status(kms->parser);
    response = kms_response_parser_get_response(kms->parser);
    if (!response) {
        CLIENT_ERR("Failed to get response from parser: %s", kms_response_parser_error(kms->parser));
        goto fail;
    }
    body = kms_response_get_body(response, &body_len);

    if (kms->retry_enabled && should_retry_http(http_status, kms->req_type)) {
        if (kms->attempts >= kms_max_attempts) {
            // Wrap error to indicate maximum retries occurred.
            _handle_non200_http_status(http_status, body, body_len, status);
            CLIENT_ERR("KMS request failed after maximum of %d retries: %s",
                       kms_max_attempts,
                       mongocrypt_status_message(status, NULL));
            goto fail;
        } else {
            ret = true;
            set_retry(kms);
            goto fail;
        }
    }

    if (http_status != 200) {
        _handle_non200_http_status(http_status, body, body_len, status);
        goto fail;
    }

    /* If HTTP response succeeded (status 200) then body should contain JSON.
     */
    bson_destroy(&body_bson);
    if (body_len > (size_t)SSIZE_MAX) {
        CLIENT_ERR("Error parsing JSON in KMS response. Response body exceeds maximum supported length");
        bson_init(&body_bson);
        goto fail;
    }
    if (!bson_init_from_json(&body_bson, body, (ssize_t)body_len, &bson_error)) {
        CLIENT_ERR("Error parsing JSON in KMS response '%s'. "
                   "HTTP status=%d. Response body=\n%s",
                   bson_error.message,
                   http_status,
                   body);
        bson_init(&body_bson);
        goto fail;
    }

    if (!bson_iter_init_find(&iter, &body_bson, json_field) || !BSON_ITER_HOLDS_UTF8(&iter)) {
        CLIENT_ERR("KMS JSON response does not include field '%s'. HTTP status=%d. "
                   "Response body=\n%s",
                   json_field,
                   http_status,
                   body);
        goto fail;
    }

    b64_str = (char *)bson_iter_utf8(&iter, NULL);
    BSON_ASSERT(b64_str);
    kms->result.data = kms_message_b64_to_raw(b64_str, &outlen);
    BSON_ASSERT(outlen <= UINT32_MAX);
    kms->result.len = (uint32_t)outlen;
    kms->result.owned = true;
    ret = true;
fail:
    bson_destroy(&body_bson);
    kms_response_destroy(response);
    return ret;
}

static bool _ctx_done_kmip_register(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);

    kms_response_t *res = NULL;

    mongocrypt_status_t *status = kms_ctx->status;
    bool ret = false;
    char *uid;

    res = kms_response_parser_get_response(kms_ctx->parser);
    if (!res) {
        CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
        goto done;
    }

    uid = kms_kmip_response_get_unique_identifier(res);
    if (!uid) {
        CLIENT_ERR("Error getting UniqueIdentifer from KMIP Register response: %s", kms_response_get_error(res));
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_string(&kms_ctx->result, uid)) {
        CLIENT_ERR("Error storing KMS UniqueIdentifer result");
        bson_free(uid);
        goto done;
    }
    ret = true;

done:
    kms_response_destroy(res);
    return ret;
}

static bool _ctx_done_kmip_activate(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);
    return _ctx_done_kmip_register(kms_ctx);
}

static bool _ctx_done_kmip_get(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);

    kms_response_t *res = NULL;

    mongocrypt_status_t *status = kms_ctx->status;
    bool ret = false;
    uint8_t *secretdata;
    size_t secretdata_len;

    res = kms_response_parser_get_response(kms_ctx->parser);
    if (!res) {
        CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
        goto done;
    }

    secretdata = kms_kmip_response_get_secretdata(res, &secretdata_len);
    if (!secretdata) {
        CLIENT_ERR("Error getting SecretData from KMIP Get response: %s", kms_response_get_error(res));
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_data_and_size(&kms_ctx->result, secretdata, secretdata_len)) {
        CLIENT_ERR("Error storing KMS SecretData result");
        bson_free(secretdata);
        goto done;
    }

    ret = true;

done:
    kms_response_destroy(res);
    return ret;
}

static bool _ctx_done_kmip_create(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);

    kms_response_t *res = NULL;

    mongocrypt_status_t *status = kms_ctx->status;
    bool ret = false;
    char *uid;

    res = kms_response_parser_get_response(kms_ctx->parser);
    if (!res) {
        CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
        goto done;
    }

    uid = kms_kmip_response_get_unique_identifier(res);
    if (!uid) {
        CLIENT_ERR("Error getting UniqueIdentifer from KMIP Create response: %s", kms_response_get_error(res));
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_string(&kms_ctx->result, uid)) {
        CLIENT_ERR("Error storing KMS UniqueIdentifer result");
        bson_free(uid);
        goto done;
    }
    ret = true;

done:
    kms_response_destroy(res);
    return ret;
}

static bool _ctx_done_kmip_encrypt(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);

    kms_response_t *res = NULL;

    mongocrypt_status_t *status = kms_ctx->status;
    bool ret = false;
    uint8_t *ciphertext;
    size_t ciphertext_len;
    uint8_t *iv;
    size_t iv_len;
    _mongocrypt_buffer_t data_buf, iv_buf;
    _mongocrypt_buffer_init(&data_buf);
    _mongocrypt_buffer_init(&iv_buf);

    res = kms_response_parser_get_response(kms_ctx->parser);
    if (!res) {
        CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
        goto done;
    }

    ciphertext = kms_kmip_response_get_data(res, &ciphertext_len);
    if (!ciphertext) {
        CLIENT_ERR("Error getting data from KMIP Encrypt response: %s", kms_response_get_error(res));
        goto done;
    }

    iv = kms_kmip_response_get_iv(res, &iv_len);
    if (!iv) {
        CLIENT_ERR("Error getting IV from KMIP Encrypt response: %s", kms_response_get_error(res));
        bson_free(ciphertext);
        goto done;
    }

    if (iv_len != MONGOCRYPT_IV_LEN) {
        CLIENT_ERR("KMIP IV response has unexpected length: %zu", iv_len);
        bson_free(ciphertext);
        bson_free(iv);
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_data_and_size(&data_buf, ciphertext, ciphertext_len)) {
        CLIENT_ERR("Error storing KMS Encrypt result");
        bson_free(ciphertext);
        bson_free(iv);
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_data_and_size(&iv_buf, iv, iv_len)) {
        CLIENT_ERR("Error storing KMS Encrypt IV");
        bson_free(ciphertext);
        bson_free(iv);
        goto done;
    }

    const _mongocrypt_buffer_t results_buf[2] = {iv_buf, data_buf};
    if (!_mongocrypt_buffer_concat(&kms_ctx->result, results_buf, 2)) {
        CLIENT_ERR("Error concatenating IV and ciphertext");
        goto done;
    }

    ret = true;

done:
    kms_response_destroy(res);
    _mongocrypt_buffer_cleanup(&iv_buf);
    _mongocrypt_buffer_cleanup(&data_buf);
    return ret;
}

static bool _ctx_done_kmip_decrypt(mongocrypt_kms_ctx_t *kms_ctx) {
    BSON_ASSERT_PARAM(kms_ctx);

    kms_response_t *res = NULL;

    mongocrypt_status_t *status = kms_ctx->status;
    bool ret = false;
    uint8_t *ciphertext;
    size_t ciphertext_len;

    res = kms_response_parser_get_response(kms_ctx->parser);
    if (!res) {
        CLIENT_ERR("Error getting KMIP response: %s", kms_response_parser_error(kms_ctx->parser));
        goto done;
    }

    ciphertext = kms_kmip_response_get_data(res, &ciphertext_len);
    if (!ciphertext) {
        CLIENT_ERR("Error getting data from KMIP Decrypt response: %s", kms_response_get_error(res));
        goto done;
    }

    if (!_mongocrypt_buffer_steal_from_data_and_size(&kms_ctx->result, ciphertext, ciphertext_len)) {
        CLIENT_ERR("Error storing KMS Decrypt result");
        bson_free(ciphertext);
        goto done;
    }

    ret = true;

done:
    kms_response_destroy(res);
    return ret;
}

static bool _is_retryable_req(_kms_request_type_t req_type) {
    // Check if request type is retryable. Some requests are non-idempotent and cannot be safely retried.
    _kms_request_type_t retryable_types[] = {MONGOCRYPT_KMS_AZURE_OAUTH,
                                             MONGOCRYPT_KMS_GCP_OAUTH,
                                             MONGOCRYPT_KMS_AWS_ENCRYPT,
                                             MONGOCRYPT_KMS_AWS_DECRYPT,
                                             MONGOCRYPT_KMS_AZURE_WRAPKEY,
                                             MONGOCRYPT_KMS_AZURE_UNWRAPKEY,
                                             MONGOCRYPT_KMS_GCP_ENCRYPT,
                                             MONGOCRYPT_KMS_GCP_DECRYPT};
    for (size_t i = 0; i < sizeof(retryable_types) / sizeof(retryable_types[0]); i++) {
        if (retryable_types[i] == req_type) {
            return true;
        }
    }
    return false;
}

bool mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t *kms) {
    if (!kms) {
        return false;
    }

    kms->should_retry = false;
    mongocrypt_status_t *status = kms->status;

    if (!kms->retry_enabled) {
        CLIENT_ERR("KMS request failed due to network error");
        return false;
    }

    if (kms->attempts >= kms_max_attempts) {
        CLIENT_ERR("KMS request failed after %d retries due to a network error", kms_max_attempts);
        return false;
    }

    if (!_is_retryable_req(kms->req_type)) {
        CLIENT_ERR("KMS request failed due to network error");
        return false;
    }

    // Mark KMS context as retryable. Return again in `mongocrypt_ctx_next_kms_ctx`.
    set_retry(kms);
    return true;
}

bool mongocrypt_kms_ctx_feed_with_retry(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes, bool *should_retry) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(bytes);
    BSON_ASSERT_PARAM(should_retry);
    kms->should_retry = false;
    *should_retry = false;
    const bool res = mongocrypt_kms_ctx_feed(kms, bytes);
    *should_retry = kms->should_retry && kms->retry_enabled;
    return res;
}

bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes) {
    if (!kms) {
        return false;
    }

    mongocrypt_status_t *status = kms->status;
    if (!mongocrypt_status_ok(status)) {
        return false;
    }
    if (kms->should_retry) {
        CLIENT_ERR("KMS context needs retry. Call mongocrypt_kms_ctx_feed_with_retry instead");
        return false;
    }

    if (!bytes) {
        CLIENT_ERR("argument 'bytes' is required");
        return false;
    }

    if (0 == bytes->len) {
        CLIENT_ERR("argument 'bytes' cannot be empty");
        return false;
    }

    if (bytes->len > mongocrypt_kms_ctx_bytes_needed(kms)) {
        CLIENT_ERR("KMS response fed too much data");
        return false;
    }

    if (!kms_response_parser_feed(kms->parser, bytes->data, bytes->len)) {
        if (is_kms(kms->req_type)) {
            /* The KMIP response parser does not suport kms_response_parser_status.
             * Only report the error string. */
            CLIENT_ERR("KMS response parser error with error: '%s'", kms_response_parser_error(kms->parser));
        } else {
            CLIENT_ERR("KMS response parser error with status %d, error: '%s'",
                       kms_response_parser_status(kms->parser),
                       kms_response_parser_error(kms->parser));
        }

        return false;
    }

    if (0 == mongocrypt_kms_ctx_bytes_needed(kms)) {
        switch (kms->req_type) {
        default: CLIENT_ERR("Unknown request type"); return false;
        case MONGOCRYPT_KMS_AWS_ENCRYPT: return _ctx_done_aws(kms, "CiphertextBlob");
        case MONGOCRYPT_KMS_AWS_DECRYPT: return _ctx_done_aws(kms, "Plaintext");
        case MONGOCRYPT_KMS_AZURE_OAUTH: return _ctx_done_oauth(kms);
        case MONGOCRYPT_KMS_AZURE_WRAPKEY: return _ctx_done_azure_wrapkey_unwrapkey(kms);
        case MONGOCRYPT_KMS_AZURE_UNWRAPKEY: return _ctx_done_azure_wrapkey_unwrapkey(kms);
        case MONGOCRYPT_KMS_GCP_OAUTH: return _ctx_done_oauth(kms);
        case MONGOCRYPT_KMS_GCP_ENCRYPT: return _ctx_done_gcp(kms, "ciphertext");
        case MONGOCRYPT_KMS_GCP_DECRYPT: return _ctx_done_gcp(kms, "plaintext");
        case MONGOCRYPT_KMS_KMIP_REGISTER: return _ctx_done_kmip_register(kms);
        case MONGOCRYPT_KMS_KMIP_ACTIVATE: return _ctx_done_kmip_activate(kms);
        case MONGOCRYPT_KMS_KMIP_GET: return _ctx_done_kmip_get(kms);
        case MONGOCRYPT_KMS_KMIP_ENCRYPT: return _ctx_done_kmip_encrypt(kms);
        case MONGOCRYPT_KMS_KMIP_DECRYPT: return _ctx_done_kmip_decrypt(kms);
        case MONGOCRYPT_KMS_KMIP_CREATE: return _ctx_done_kmip_create(kms);
        }
    }
    return true;
}

bool _mongocrypt_kms_ctx_result(mongocrypt_kms_ctx_t *kms, _mongocrypt_buffer_t *out) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(out);

    mongocrypt_status_t *status = kms->status;

    /* If we have no status, we were never initialized */
    if (!status) {
        return false;
    }

    if (!mongocrypt_status_ok(status)) {
        return false;
    }

    if (mongocrypt_kms_ctx_bytes_needed(kms) > 0) {
        CLIENT_ERR("KMS response unfinished");
        return false;
    }

    _mongocrypt_buffer_init(out);
    out->data = kms->result.data;
    out->len = kms->result.len;
    return true;
}

bool mongocrypt_kms_ctx_status(mongocrypt_kms_ctx_t *kms, mongocrypt_status_t *status_out) {
    if (!kms) {
        return false;
    }

    if (!status_out) {
        mongocrypt_status_t *status = kms->status;
        CLIENT_ERR("argument 'status' is required");
        return false;
    }
    _mongocrypt_status_copy_to(kms->status, status_out);
    return mongocrypt_status_ok(status_out);
}

void _mongocrypt_kms_ctx_cleanup(mongocrypt_kms_ctx_t *kms) {
    if (!kms) {
        return;
    }
    if (kms->req) {
        kms_request_destroy(kms->req);
    }
    if (kms->parser) {
        kms_response_parser_destroy(kms->parser);
    }
    mongocrypt_status_destroy(kms->status);
    _mongocrypt_buffer_cleanup(&kms->msg);
    _mongocrypt_buffer_cleanup(&kms->result);
    bson_free(kms->endpoint);
    bson_free(kms->kmsid);
}

bool mongocrypt_kms_ctx_message(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *msg) {
    if (!kms) {
        return false;
    }

    if (!msg) {
        mongocrypt_status_t *status = kms->status;
        CLIENT_ERR("argument 'msg' is required");
        return false;
    }
    msg->data = kms->msg.data;
    msg->len = kms->msg.len;
    return true;
}

bool mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t *kms, const char **endpoint) {
    if (!kms) {
        return false;
    }
    if (!endpoint) {
        mongocrypt_status_t *status = kms->status;
        CLIENT_ERR("argument 'endpoint' is required");
        return false;
    }
    *endpoint = kms->endpoint;
    return true;
}

bool _mongocrypt_kms_ctx_init_azure_auth(mongocrypt_kms_ctx_t *kms,
                                         const mc_kms_creds_t *kc,
                                         _mongocrypt_endpoint_t *key_vault_endpoint,
                                         const char *kmsid,
                                         _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(kc);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    const _mongocrypt_endpoint_t *identity_platform_endpoint;
    char *scope = NULL;
    const char *hostname;
    char *request_string;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_AZURE_OAUTH, kmsid);
    status = kms->status;

    BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_AZURE);

    identity_platform_endpoint = kc->value.azure.identity_platform_endpoint;

    if (identity_platform_endpoint) {
        kms->endpoint = bson_strdup(identity_platform_endpoint->host_and_port);
        hostname = identity_platform_endpoint->host;
    } else {
        kms->endpoint = bson_strdup("login.microsoftonline.com");
        hostname = "login.microsoftonline.com";
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    if (key_vault_endpoint) {
        /* Request a custom scope. It is URL encoded, like
         * https%3A%2F%2Fvault.azure.net%2F.default */
        scope = bson_strdup_printf("%s%s%s", "https%3A%2F%2F", key_vault_endpoint->domain, "%2F.default");
    } else {
        /* Default to commercial Azure endpoint. */
        scope = bson_strdup("https%3A%2F%2Fvault.azure.net%2F.default");
    }

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_AZURE);
    kms->req = kms_azure_request_oauth_new(hostname,
                                           scope,
                                           kc->value.azure.tenant_id,
                                           kc->value.azure.client_id,
                                           kc->value.azure.client_secret,
                                           opt);
    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting Azure OAuth KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    bson_free(scope);
    kms_request_opt_destroy(opt);
    return ret;
}

bool _mongocrypt_kms_ctx_init_azure_wrapkey(mongocrypt_kms_ctx_t *kms,
                                            _mongocrypt_opts_kms_providers_t *kms_providers,
                                            struct __mongocrypt_ctx_opts_t *ctx_opts,
                                            const char *access_token,
                                            _mongocrypt_buffer_t *plaintext_key_material,
                                            const char *kmsid,
                                            _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(ctx_opts);
    BSON_ASSERT_PARAM(plaintext_key_material);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    char *path_and_query = NULL;
    char *payload = NULL;
    const char *host;
    char *request_string;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_AZURE_WRAPKEY, kmsid);
    status = kms->status;

    BSON_ASSERT(ctx_opts->kek.provider.azure.key_vault_endpoint);

    kms->endpoint = bson_strdup(ctx_opts->kek.provider.azure.key_vault_endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);
    host = ctx_opts->kek.provider.azure.key_vault_endpoint->host;

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_AZURE);
    kms->req = kms_azure_request_wrapkey_new(host,
                                             access_token,
                                             ctx_opts->kek.provider.azure.key_name,
                                             ctx_opts->kek.provider.azure.key_version,
                                             plaintext_key_material->data,
                                             plaintext_key_material->len,
                                             opt);

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS wrapkey message: %s", kms_request_get_error(kms->req));
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting Azure wrapkey KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    kms_request_opt_destroy(opt);
    bson_free(path_and_query);
    bson_free(payload);
    return ret;
}

bool _mongocrypt_kms_ctx_init_azure_unwrapkey(mongocrypt_kms_ctx_t *kms,
                                              _mongocrypt_opts_kms_providers_t *kms_providers,
                                              const char *access_token,
                                              _mongocrypt_key_doc_t *key,
                                              const char *kmsid,
                                              _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(key);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    char *path_and_query = NULL;
    char *payload = NULL;
    const char *host;
    char *request_string;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_AZURE_UNWRAPKEY, kmsid);
    status = kms->status;

    BSON_ASSERT(key->kek.provider.azure.key_vault_endpoint);

    kms->endpoint = bson_strdup(key->kek.provider.azure.key_vault_endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);
    host = key->kek.provider.azure.key_vault_endpoint->host;

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_AZURE);
    kms->req = kms_azure_request_unwrapkey_new(host,
                                               access_token,
                                               key->kek.provider.azure.key_name,
                                               key->kek.provider.azure.key_version,
                                               key->key_material.data,
                                               key->key_material.len,
                                               opt);

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS unwrapkey message: %s", kms_request_get_error(kms->req));
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting Azure unwrapkey KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    kms_request_opt_destroy(opt);
    bson_free(path_and_query);
    bson_free(payload);
    return ret;
}

#define RSAES_PKCS1_V1_5_SIGNATURE_LEN 256

/* This is the form of the callback that KMS message calls. */
static bool _sign_rsaes_pkcs1_v1_5_trampoline(void *ctx,
                                              const char *private_key,
                                              size_t private_key_len,
                                              const char *input,
                                              size_t input_len,
                                              unsigned char *signature_out) {
    ctx_with_status_t *ctx_with_status;
    _mongocrypt_opts_t *crypt_opts;
    mongocrypt_binary_t private_key_bin;
    mongocrypt_binary_t input_bin;
    mongocrypt_binary_t output_bin;
    bool ret;

    BSON_ASSERT_PARAM(ctx);
    BSON_ASSERT_PARAM(input);
    BSON_ASSERT_PARAM(private_key);
    BSON_ASSERT_PARAM(signature_out);

    ctx_with_status = (ctx_with_status_t *)ctx;
    crypt_opts = (_mongocrypt_opts_t *)ctx_with_status->ctx;
    BSON_ASSERT(crypt_opts);
    private_key_bin.data = (uint8_t *)private_key;
    BSON_ASSERT(private_key_len <= UINT32_MAX);
    private_key_bin.len = (uint32_t)private_key_len;
    input_bin.data = (uint8_t *)input;
    BSON_ASSERT(input_len <= UINT32_MAX);
    input_bin.len = (uint32_t)input_len;
    output_bin.data = (uint8_t *)signature_out;
    output_bin.len = RSAES_PKCS1_V1_5_SIGNATURE_LEN;

    ret = crypt_opts->sign_rsaes_pkcs1_v1_5(crypt_opts->sign_ctx,
                                            &private_key_bin,
                                            &input_bin,
                                            &output_bin,
                                            ctx_with_status->status);
    return ret;
}

bool _mongocrypt_kms_ctx_init_gcp_auth(mongocrypt_kms_ctx_t *kms,
                                       _mongocrypt_opts_t *crypt_opts,
                                       const mc_kms_creds_t *kc,
                                       _mongocrypt_endpoint_t *kms_endpoint,
                                       const char *kmsid,
                                       _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(kc);
    BSON_ASSERT_PARAM(crypt_opts);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    const _mongocrypt_endpoint_t *auth_endpoint;
    char *scope = NULL;
    char *audience = NULL;
    const char *hostname;
    char *request_string;
    bool ret = false;
    ctx_with_status_t ctx_with_status;

    _init_common(kms, log, MONGOCRYPT_KMS_GCP_OAUTH, kmsid);
    status = kms->status;
    ctx_with_status.ctx = crypt_opts;
    ctx_with_status.status = mongocrypt_status_new();

    BSON_ASSERT(kc->type == MONGOCRYPT_KMS_PROVIDER_GCP);

    auth_endpoint = kc->value.gcp.endpoint;
    if (auth_endpoint) {
        kms->endpoint = bson_strdup(auth_endpoint->host_and_port);
        hostname = auth_endpoint->host;
        audience = bson_strdup_printf("https://%s/token", auth_endpoint->host);
    } else {
        kms->endpoint = bson_strdup("oauth2.googleapis.com");
        hostname = "oauth2.googleapis.com";
        audience = bson_strdup_printf("https://oauth2.googleapis.com/token");
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    if (kms_endpoint) {
        /* Request a custom scope. */
        scope = bson_strdup_printf("https://www.%s/auth/cloudkms", kms_endpoint->domain);
    } else {
        scope = bson_strdup("https://www.googleapis.com/auth/cloudkms");
    }

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_GCP);
    if (crypt_opts->sign_rsaes_pkcs1_v1_5) {
        kms_request_opt_set_crypto_hook_sign_rsaes_pkcs1_v1_5(opt, _sign_rsaes_pkcs1_v1_5_trampoline, &ctx_with_status);
    }
    kms->req = kms_gcp_request_oauth_new(hostname,
                                         kc->value.gcp.email,
                                         audience,
                                         scope,
                                         (const char *)kc->value.gcp.private_key.data,
                                         kc->value.gcp.private_key.len,
                                         opt);
    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting GCP OAuth KMS message: %s", kms_request_get_error(kms->req));
        _mongocrypt_status_append(status, ctx_with_status.status);
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    bson_free(scope);
    bson_free(audience);
    kms_request_opt_destroy(opt);
    mongocrypt_status_destroy(ctx_with_status.status);
    return ret;
}

bool _mongocrypt_kms_ctx_init_gcp_encrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          struct __mongocrypt_ctx_opts_t *ctx_opts,
                                          const char *access_token,
                                          _mongocrypt_buffer_t *plaintext_key_material,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(ctx_opts);
    BSON_ASSERT_PARAM(kms_providers);
    BSON_ASSERT_PARAM(access_token);
    BSON_ASSERT_PARAM(plaintext_key_material);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    char *path_and_query = NULL;
    char *payload = NULL;
    const char *hostname;
    char *request_string;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_GCP_ENCRYPT, kmsid);
    status = kms->status;

    if (ctx_opts->kek.provider.gcp.endpoint) {
        kms->endpoint = bson_strdup(ctx_opts->kek.provider.gcp.endpoint->host_and_port);
        hostname = ctx_opts->kek.provider.gcp.endpoint->host;
    } else {
        kms->endpoint = bson_strdup("cloudkms.googleapis.com");
        hostname = "cloudkms.googleapis.com";
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_GCP);
    kms->req = kms_gcp_request_encrypt_new(hostname,
                                           access_token,
                                           ctx_opts->kek.provider.gcp.project_id,
                                           ctx_opts->kek.provider.gcp.location,
                                           ctx_opts->kek.provider.gcp.key_ring,
                                           ctx_opts->kek.provider.gcp.key_name,
                                           ctx_opts->kek.provider.gcp.key_version,
                                           plaintext_key_material->data,
                                           plaintext_key_material->len,
                                           opt);

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing GCP KMS encrypt message: %s", kms_request_get_error(kms->req));
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting GCP KMS encrypt KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    kms_request_opt_destroy(opt);
    bson_free(path_and_query);
    bson_free(payload);
    return ret;
}

bool _mongocrypt_kms_ctx_init_gcp_decrypt(mongocrypt_kms_ctx_t *kms,
                                          _mongocrypt_opts_kms_providers_t *kms_providers,
                                          const char *access_token,
                                          _mongocrypt_key_doc_t *key,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms);
    BSON_ASSERT_PARAM(kms_providers);
    BSON_ASSERT_PARAM(access_token);
    BSON_ASSERT_PARAM(key);

    kms_request_opt_t *opt = NULL;
    mongocrypt_status_t *status;
    char *path_and_query = NULL;
    char *payload = NULL;
    const char *hostname;
    char *request_string;
    bool ret = false;

    _init_common(kms, log, MONGOCRYPT_KMS_GCP_DECRYPT, kmsid);
    status = kms->status;

    if (key->kek.provider.gcp.endpoint) {
        kms->endpoint = bson_strdup(key->kek.provider.gcp.endpoint->host_and_port);
        hostname = key->kek.provider.gcp.endpoint->host;
    } else {
        kms->endpoint = bson_strdup("cloudkms.googleapis.com");
        hostname = "cloudkms.googleapis.com";
    }
    _mongocrypt_apply_default_port(&kms->endpoint, DEFAULT_HTTPS_PORT);

    opt = kms_request_opt_new();
    BSON_ASSERT(opt);
    kms_request_opt_set_connection_close(opt, true);
    kms_request_opt_set_provider(opt, KMS_REQUEST_PROVIDER_GCP);
    kms->req = kms_gcp_request_decrypt_new(hostname,
                                           access_token,
                                           key->kek.provider.gcp.project_id,
                                           key->kek.provider.gcp.location,
                                           key->kek.provider.gcp.key_ring,
                                           key->kek.provider.gcp.key_name,
                                           key->key_material.data,
                                           key->key_material.len,
                                           opt);

    if (kms_request_get_error(kms->req)) {
        CLIENT_ERR("error constructing GCP KMS decrypt message: %s", kms_request_get_error(kms->req));
        goto fail;
    }

    request_string = kms_request_to_string(kms->req);
    if (!request_string) {
        CLIENT_ERR("error getting GCP KMS decrypt KMS message: %s", kms_request_get_error(kms->req));
        goto fail;
    }
    _mongocrypt_buffer_init(&kms->msg);
    kms->msg.data = (uint8_t *)request_string;
    kms->msg.len = (uint32_t)strlen(request_string);
    kms->msg.owned = true;

    ret = true;
fail:
    kms_request_opt_destroy(opt);
    bson_free(path_and_query);
    bson_free(payload);
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_register(mongocrypt_kms_ctx_t *kms_ctx,
                                            const _mongocrypt_endpoint_t *endpoint,
                                            const uint8_t *secretdata,
                                            uint32_t secretdata_len,
                                            const char *kmsid,
                                            _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    BSON_ASSERT_PARAM(secretdata);

    mongocrypt_status_t *status;
    bool ret = false;
    const uint8_t *reqdata;
    size_t reqlen;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_REGISTER, kmsid);
    status = kms_ctx->status;

    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
    kms_ctx->req = kms_kmip_request_register_secretdata_new(NULL /* reserved */, secretdata, secretdata_len);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP register request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_activate(mongocrypt_kms_ctx_t *kms_ctx,
                                            const _mongocrypt_endpoint_t *endpoint,
                                            const char *unique_identifier,
                                            const char *kmsid,
                                            _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    BSON_ASSERT_PARAM(unique_identifier);

    mongocrypt_status_t *status;
    bool ret = false;
    size_t reqlen;
    const uint8_t *reqdata;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_ACTIVATE, kmsid);
    status = kms_ctx->status;

    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
    kms_ctx->req = kms_kmip_request_activate_new(NULL /* reserved */, unique_identifier);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP activate request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_get(mongocrypt_kms_ctx_t *kms_ctx,
                                       const _mongocrypt_endpoint_t *endpoint,
                                       const char *unique_identifier,
                                       const char *kmsid,
                                       _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    BSON_ASSERT_PARAM(unique_identifier);

    mongocrypt_status_t *status;
    bool ret = false;
    size_t reqlen;
    const uint8_t *reqdata;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_GET, kmsid);
    status = kms_ctx->status;

    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);
    kms_ctx->req = kms_kmip_request_get_new(NULL /* reserved */, unique_identifier);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP get request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_create(mongocrypt_kms_ctx_t *kms_ctx,
                                          const _mongocrypt_endpoint_t *endpoint,
                                          const char *kmsid,
                                          _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    bool ret = false;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_CREATE, kmsid);
    mongocrypt_status_t *status = kms_ctx->status;
    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);

    kms_ctx->req = kms_kmip_request_create_new(NULL /* reserved */);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP create request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    size_t reqlen;
    const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_encrypt(mongocrypt_kms_ctx_t *kms_ctx,
                                           const _mongocrypt_endpoint_t *endpoint,
                                           const char *unique_identifier,
                                           const char *kmsid,
                                           _mongocrypt_buffer_t *plaintext,
                                           _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    BSON_ASSERT_PARAM(plaintext);
    bool ret = false;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_ENCRYPT, kmsid);
    mongocrypt_status_t *status = kms_ctx->status;
    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);

    kms_ctx->req =
        kms_kmip_request_encrypt_new(NULL /* reserved */, unique_identifier, plaintext->data, plaintext->len);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP encrypt request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    size_t reqlen;
    const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

bool _mongocrypt_kms_ctx_init_kmip_decrypt(mongocrypt_kms_ctx_t *kms_ctx,
                                           const _mongocrypt_endpoint_t *endpoint,
                                           const char *kmsid,
                                           _mongocrypt_key_doc_t *key,
                                           _mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(kms_ctx);
    BSON_ASSERT_PARAM(endpoint);
    BSON_ASSERT_PARAM(key);
    bool ret = false;

    _init_common(kms_ctx, log, MONGOCRYPT_KMS_KMIP_DECRYPT, kmsid);
    mongocrypt_status_t *status = kms_ctx->status;
    kms_ctx->endpoint = bson_strdup(endpoint->host_and_port);
    _mongocrypt_apply_default_port(&kms_ctx->endpoint, DEFAULT_KMIP_PORT);

    _mongocrypt_buffer_t iv;
    if (!_mongocrypt_buffer_from_subrange(&iv, &key->key_material, 0, MONGOCRYPT_IV_LEN)) {
        CLIENT_ERR("Error getting IV from key material");
        goto done;
    }
    _mongocrypt_buffer_t ciphertext;
    if (!_mongocrypt_buffer_from_subrange(&ciphertext,
                                          &key->key_material,
                                          MONGOCRYPT_IV_LEN,
                                          key->key_material.len - MONGOCRYPT_IV_LEN)) {
        CLIENT_ERR("Error getting ciphertext from key material");
        goto done;
    }

    BSON_ASSERT(key->kek.kms_provider == MONGOCRYPT_KMS_PROVIDER_KMIP);
    BSON_ASSERT(key->kek.provider.kmip.delegated);
    kms_ctx->req = kms_kmip_request_decrypt_new(NULL /* reserved */,
                                                key->kek.provider.kmip.key_id,
                                                ciphertext.data,
                                                ciphertext.len,
                                                iv.data,
                                                iv.len);

    if (kms_request_get_error(kms_ctx->req)) {
        CLIENT_ERR("Error creating KMIP decrypt request: %s", kms_request_get_error(kms_ctx->req));
        goto done;
    }

    size_t reqlen;
    const uint8_t *reqdata = kms_request_to_bytes(kms_ctx->req, &reqlen);
    if (!_mongocrypt_buffer_copy_from_data_and_size(&kms_ctx->msg, reqdata, reqlen)) {
        CLIENT_ERR("Error storing KMS request payload");
        goto done;
    }

    ret = true;
done:
    return ret;
}

static const char *set_and_ret(const char *what, uint32_t *len) {
    BSON_ASSERT_PARAM(what);

    if (len) {
        BSON_ASSERT(size_to_uint32(strlen(what), len));
    }
    return what;
}

const char *mongocrypt_kms_ctx_get_kms_provider(mongocrypt_kms_ctx_t *kms, uint32_t *len) {
    BSON_ASSERT_PARAM(kms);
    /* len is checked in set_and_ret () before it is used */

    return set_and_ret(kms->kmsid, len);
}
libmongocrypt-1.19.0/src/mongocrypt-log-private.h000066400000000000000000000030121521103432300220500ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_LOG_PRIVATE_H
#define MONGOCRYPT_LOG_PRIVATE_H

#include "mongocrypt-mutex-private.h"
#include "mongocrypt.h"

typedef struct {
    mongocrypt_mutex_t mutex; /* protects fn and ctx. */
    mongocrypt_log_fn_t fn;
    void *ctx;
} _mongocrypt_log_t;

void _mongocrypt_stdout_log_fn(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx);

void _mongocrypt_log(_mongocrypt_log_t *log, mongocrypt_log_level_t level, const char *message, ...)
    BSON_GNUC_PRINTF(3, 4);

void _mongocrypt_log_init(_mongocrypt_log_t *log);

void _mongocrypt_log_cleanup(_mongocrypt_log_t *log);

void _mongocrypt_log_set_fn(_mongocrypt_log_t *log, mongocrypt_log_fn_t fn, void *ctx);

#define CRYPT_TRACEF(log, fmt, ...)
#define CRYPT_TRACE(log, msg)
#define CRYPT_ENTRY(log)
#define CRYPT_EXIT(log)
#define CRYPT_RETURN(log, x) return (x);
#define CRYPT_GOTO(log, x) goto x;

#endif /* MONGOCRYPT_LOG_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-log.c000066400000000000000000000045751521103432300204120ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-config.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"

#include 

void _mongocrypt_log_init(_mongocrypt_log_t *log) {
    BSON_ASSERT_PARAM(log);

    _mongocrypt_mutex_init(&log->mutex);
    /* Initially, no log function is set. */
    _mongocrypt_log_set_fn(log, NULL, NULL);
}

void _mongocrypt_log_cleanup(_mongocrypt_log_t *log) {
    if (!log) {
        return;
    }

    _mongocrypt_mutex_cleanup(&log->mutex);
}

void _mongocrypt_stdout_log_fn(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) {
    BSON_ASSERT_PARAM(message);

    switch (level) {
    case MONGOCRYPT_LOG_LEVEL_FATAL: printf("FATAL"); break;
    case MONGOCRYPT_LOG_LEVEL_ERROR: printf("ERROR"); break;
    case MONGOCRYPT_LOG_LEVEL_WARNING: printf("WARNING"); break;
    case MONGOCRYPT_LOG_LEVEL_INFO: printf("INFO"); break;
    case MONGOCRYPT_LOG_LEVEL_TRACE: printf("TRACE"); break; /* UNUSED */
    default: printf("UNKNOWN"); break;
    }
    printf(" %s\n", message);
}

void _mongocrypt_log_set_fn(_mongocrypt_log_t *log, mongocrypt_log_fn_t fn, void *ctx) {
    BSON_ASSERT_PARAM(log);

    _mongocrypt_mutex_lock(&log->mutex);
    log->fn = fn;
    log->ctx = ctx;
    _mongocrypt_mutex_unlock(&log->mutex);
}

void _mongocrypt_log(_mongocrypt_log_t *log, mongocrypt_log_level_t level, const char *format, ...) {
    va_list args;
    char *message;

    BSON_ASSERT_PARAM(log);
    BSON_ASSERT_PARAM(format);

    va_start(args, format);
    message = bson_strdupv_printf(format, args);
    va_end(args);

    BSON_ASSERT(message);

    _mongocrypt_mutex_lock(&log->mutex);
    if (log->fn) {
        log->fn(level, message, (uint32_t)strlen(message), log->ctx);
    }
    _mongocrypt_mutex_unlock(&log->mutex);
    bson_free(message);
}
libmongocrypt-1.19.0/src/mongocrypt-marking-private.h000066400000000000000000000053731521103432300227330ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_MARKING_PRIVATE_H
#define MONGOCRYPT_MARKING_PRIVATE_H

#include "mc-fle2-encryption-placeholder-private.h"
#include "mc-range-mincover-private.h"
#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-private.h"

typedef enum {
    MONGOCRYPT_MARKING_FLE1_BY_ID,
    MONGOCRYPT_MARKING_FLE1_BY_ALTNAME,
    MONGOCRYPT_MARKING_FLE2_ENCRYPTION,
} mongocrypt_marking_type_t;

typedef struct {
    mongocrypt_marking_type_t type;

    union {
        struct {
            // Markings used by FLE1
            mongocrypt_encryption_algorithm_t algorithm;
            bson_iter_t v_iter;

            _mongocrypt_buffer_t key_id;
            bson_value_t key_alt_name;
        } fle1;

        mc_FLE2EncryptionPlaceholder_t fle2;
    } u;
} _mongocrypt_marking_t;

// `_mongocrypt_marking_t` inherits extended alignment from libbson. To dynamically allocate, use aligned allocation
// (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_marking_t, BSON_ALIGNOF(_mongocrypt_marking_t) >= BSON_ALIGNOF(bson_iter_t));

void _mongocrypt_marking_init(_mongocrypt_marking_t *marking);

void _mongocrypt_marking_cleanup(_mongocrypt_marking_t *marking);

bool _mongocrypt_marking_parse_unowned(const _mongocrypt_buffer_t *in,
                                       _mongocrypt_marking_t *out,
                                       mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

// Callers are expected to initialize `ciphertext` with
// `_mongocrypt_ciphertext_init before calling,
// and eventually free it using `_mongocrypt_ciphertext_cleanup`.
bool _mongocrypt_marking_to_ciphertext(void *ctx,
                                       _mongocrypt_marking_t *marking,
                                       _mongocrypt_ciphertext_t *ciphertext,
                                       mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

mc_mincover_t *mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec,
                                                      size_t sparsity,
                                                      mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

#endif /* MONGOCRYPT_MARKING_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-marking.c000066400000000000000000003447541521103432300212670ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "bson/bson.h"
#include "mc-fle-blob-subtype-private.h"
#include "mc-fle2-encryption-placeholder-private.h"
#include "mc-fle2-find-equality-payload-private-v2.h"
#include "mc-fle2-find-equality-payload-private.h"
#include "mc-fle2-find-range-payload-private-v2.h"
#include "mc-fle2-find-range-payload-private.h"
#include "mc-fle2-find-text-payload-private.h"
#include "mc-fle2-insert-update-payload-private-v2.h"
#include "mc-fle2-insert-update-payload-private.h"
#include "mc-fle2-payload-uev-private.h"
#include "mc-fle2-payload-uev-v2-private.h"
#include "mc-optional-private.h"
#include "mc-range-edge-generation-private.h"
#include "mc-range-encoding-private.h"
#include "mc-range-mincover-private.h"
#include "mc-str-encode-string-sets-private.h"
#include "mc-text-search-str-encode-private.h"
#include "mc-tokens-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-marking-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-util-private.h" // mc_bson_type_to_string
#include "mongocrypt.h"

#include  // isinf

static bool
_mongocrypt_marking_parse_fle1_placeholder(const bson_t *in, _mongocrypt_marking_t *out, mongocrypt_status_t *status) {
    bson_iter_t iter = {0};
    bool has_ki = false, has_ka = false, has_a = false, has_v = false;

    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    out->type = MONGOCRYPT_MARKING_FLE1_BY_ID;

    if (!bson_iter_init(&iter, in)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }

    while (bson_iter_next(&iter)) {
        const char *field;

        field = bson_iter_key(&iter);
        BSON_ASSERT(field);
        if (0 == strcmp("ki", field)) {
            has_ki = true;
            if (!_mongocrypt_buffer_from_uuid_iter(&out->u.fle1.key_id, &iter)) {
                CLIENT_ERR("key id must be a UUID");
                return false;
            }
            continue;
        }

        if (0 == strcmp("ka", field)) {
            has_ka = true;
            /* Some bson_value types are not allowed to be key alt names */
            const bson_value_t *value;

            value = bson_iter_value(&iter);

            if (!BSON_ITER_HOLDS_UTF8(&iter)) {
                CLIENT_ERR("key alt name must be a UTF8");
                return false;
            }
            /* CDRIVER-3100 We must make a copy of this value; the result of
             * bson_iter_value is ephemeral. */
            bson_value_copy(value, &out->u.fle1.key_alt_name);
            out->type = MONGOCRYPT_MARKING_FLE1_BY_ALTNAME;
            continue;
        }

        if (0 == strcmp("v", field)) {
            has_v = true;
            memcpy(&out->u.fle1.v_iter, &iter, sizeof(bson_iter_t));
            continue;
        }

        if (0 == strcmp("a", field)) {
            int32_t algorithm;

            has_a = true;
            if (!BSON_ITER_HOLDS_INT32(&iter)) {
                CLIENT_ERR("invalid marking, 'a' must be an int32");
                return false;
            }
            algorithm = bson_iter_int32(&iter);
            if (algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC
                && algorithm != MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM) {
                CLIENT_ERR("invalid algorithm value: %d", algorithm);
                return false;
            }
            out->u.fle1.algorithm = (mongocrypt_encryption_algorithm_t)algorithm;
            continue;
        }

        CLIENT_ERR("unrecognized field '%s'", field);
        return false;
    }

    if (!has_v) {
        CLIENT_ERR("no 'v' specified");
        return false;
    }

    if (!has_ki && !has_ka) {
        CLIENT_ERR("neither 'ki' nor 'ka' specified");
        return false;
    }

    if (has_ki && has_ka) {
        CLIENT_ERR("both 'ki' and 'ka' specified");
        return false;
    }

    if (!has_a) {
        CLIENT_ERR("no 'a' specified");
        return false;
    }

    return true;
}

static bool
_mongocrypt_marking_parse_fle2_placeholder(const bson_t *in, _mongocrypt_marking_t *out, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    out->type = MONGOCRYPT_MARKING_FLE2_ENCRYPTION;
    return mc_FLE2EncryptionPlaceholder_parse(&out->u.fle2, in, status);
}

bool _mongocrypt_marking_parse_unowned(const _mongocrypt_buffer_t *in,
                                       _mongocrypt_marking_t *out,
                                       mongocrypt_status_t *status) {
    bson_t bson;

    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_marking_init(out);
    /* 5 for minimal BSON object, plus one for blob subtype */
    if (in->len < 6) {
        CLIENT_ERR("invalid marking, length < 6");
        return false;
    }

    if (!bson_init_static(&bson, in->data + 1, in->len - 1) || !bson_validate(&bson, BSON_VALIDATE_NONE, NULL)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }

    if (in->data[0] == MC_SUBTYPE_FLE1EncryptionPlaceholder) {
        return _mongocrypt_marking_parse_fle1_placeholder(&bson, out, status);
    } else if (in->data[0] == MC_SUBTYPE_FLE2EncryptionPlaceholder) {
        return _mongocrypt_marking_parse_fle2_placeholder(&bson, out, status);
    } else {
        CLIENT_ERR("invalid marking, first byte must be 0 or 3");
        return false;
    }
}

void _mongocrypt_marking_init(_mongocrypt_marking_t *marking) {
    BSON_ASSERT_PARAM(marking);

    memset(marking, 0, sizeof(*marking));
}

void _mongocrypt_marking_cleanup(_mongocrypt_marking_t *marking) {
    if (!marking) {
        return;
    }
    if (marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION) {
        mc_FLE2EncryptionPlaceholder_cleanup(&marking->u.fle2);
        return;
    }

    // else FLE1
    _mongocrypt_buffer_cleanup(&marking->u.fle1.key_id);
    bson_value_destroy(&marking->u.fle1.key_alt_name);
}

/**
 * Calculates:
 * E?CToken = HMAC(collectionLevel1Token, n)
 * E?CDerivedFromDataToken = HMAC(E?CToken, value)
 * E?CDerivedFromDataTokenAndContentionFactor = HMAC(E?CDerivedFromDataToken, cf)
 *
 * E?C = EDC|ESC|ECC
 * n = 1 for EDC, 2 for ESC, 3 for ECC
 * cf = contentionFactor
 *
 * If {useContentionFactor} is False, E?CDerivedFromDataToken is saved to out, and {contentionFactor} is ignored.
 * Otherwise, E?CDerivedFromDataTokenAndContentionFactor is saved to out using {contentionFactor}.
 *
 * Note that {out} is initialized even on failure.
 */
#define DERIVE_TOKEN_IMPL(Name)                                                                                        \
    static bool _fle2_derive_##Name##_token(_mongocrypt_crypto_t *crypto,                                              \
                                            _mongocrypt_buffer_t *out,                                                 \
                                            const mc_CollectionsLevel1Token_t *level1Token,                            \
                                            const _mongocrypt_buffer_t *value,                                         \
                                            bool useContentionFactor,                                                  \
                                            int64_t contentionFactor,                                                  \
                                            mongocrypt_status_t *status) {                                             \
        BSON_ASSERT_PARAM(crypto);                                                                                     \
        BSON_ASSERT_PARAM(out);                                                                                        \
        BSON_ASSERT_PARAM(level1Token);                                                                                \
        BSON_ASSERT_PARAM(value);                                                                                      \
                                                                                                                       \
        _mongocrypt_buffer_init(out);                                                                                  \
                                                                                                                       \
        mc_##Name##Token_t *token = mc_##Name##Token_new(crypto, level1Token, status);                                 \
        if (!token) {                                                                                                  \
            return false;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        mc_##Name##DerivedFromDataToken_t *fromDataToken =                                                             \
            mc_##Name##DerivedFromDataToken_new(crypto, token, value, status);                                         \
        mc_##Name##Token_destroy(token);                                                                               \
        if (!fromDataToken) {                                                                                          \
            return false;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        if (!useContentionFactor) {                                                                                    \
            /* FindEqualityPayload uses *fromDataToken */                                                              \
            _mongocrypt_buffer_copy_to(mc_##Name##DerivedFromDataToken_get(fromDataToken), out);                       \
            mc_##Name##DerivedFromDataToken_destroy(fromDataToken);                                                    \
            return true;                                                                                               \
        }                                                                                                              \
                                                                                                                       \
        BSON_ASSERT(contentionFactor >= 0);                                                                            \
        /* InsertUpdatePayload continues through *fromDataTokenAndContentionFactor */                                  \
        mc_##Name##DerivedFromDataTokenAndContentionFactor_t *fromTokenAndContentionFactor =                           \
            mc_##Name##DerivedFromDataTokenAndContentionFactor_new(crypto,                                             \
                                                                   fromDataToken,                                      \
                                                                   (uint64_t)contentionFactor,                         \
                                                                   status);                                            \
        mc_##Name##DerivedFromDataToken_destroy(fromDataToken);                                                        \
        if (!fromTokenAndContentionFactor) {                                                                           \
            return false;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        _mongocrypt_buffer_copy_to(                                                                                    \
            mc_##Name##DerivedFromDataTokenAndContentionFactor_get(fromTokenAndContentionFactor),                      \
            out);                                                                                                      \
        mc_##Name##DerivedFromDataTokenAndContentionFactor_destroy(fromTokenAndContentionFactor);                      \
                                                                                                                       \
        return true;                                                                                                   \
    }

DERIVE_TOKEN_IMPL(EDC)
DERIVE_TOKEN_IMPL(ESC)

#undef DERIVE_TOKEN_IMPL

/**
 * Calculates:
 * E?CToken = HMAC(collectionLevel1Token, n)
 * E?CTextToken = HMAC(E?CToken, t)
 * E?CTextDerivedFromDataToken = HMAC(E?CTextToken, v)
 * E?CTextDerivedFromDataTokenAndContentionFactorToken = HMAC(E?CTextDerivedFromDataToken, cf)
 *
 * E?C = EDC|ESC
 * n = 1 for EDC, 2 for ESC
 *  = Exact|Substring|Suffix|Prefix
 * t = 1 for Exact, 2 for Substring, 3 for Suffix, 4 for Prefix
 * cf = contentionFactor
 *
 * If {useContentionFactor} is False, E?CTextDerivedFromDataToken is saved to out, and
 * {contentionFactor} is ignored.
 * Otherwise, E?CTextDerivedFromDataTokenAndContentionFactorToken is saved to out.
 * Note that {out} is initialized even on failure.
 */
#define DERIVE_TEXT_SEARCH_TOKEN_IMPL(Name, Type)                                                                      \
    static bool _fle2_derive_##Name##Text##Type##_token(_mongocrypt_crypto_t *crypto,                                  \
                                                        _mongocrypt_buffer_t *out,                                     \
                                                        const mc_CollectionsLevel1Token_t *level1Token,                \
                                                        const _mongocrypt_buffer_t *value,                             \
                                                        bool useContentionFactor,                                      \
                                                        int64_t contentionFactor,                                      \
                                                        mongocrypt_status_t *status) {                                 \
        BSON_ASSERT_PARAM(crypto);                                                                                     \
        BSON_ASSERT_PARAM(out);                                                                                        \
        BSON_ASSERT_PARAM(level1Token);                                                                                \
        BSON_ASSERT_PARAM(value);                                                                                      \
                                                                                                                       \
        _mongocrypt_buffer_init(out);                                                                                  \
                                                                                                                       \
        mc_##Name##Token_t *token = mc_##Name##Token_new(crypto, level1Token, status);                                 \
        if (!token) {                                                                                                  \
            return false;                                                                                              \
        }                                                                                                              \
        mc_##Name##Text##Type##Token_t *textToken = mc_##Name##Text##Type##Token_new(crypto, token, status);           \
        mc_##Name##Token_destroy(token);                                                                               \
        if (!textToken) {                                                                                              \
            return false;                                                                                              \
        }                                                                                                              \
        mc_##Name##Text##Type##DerivedFromDataToken_t *fromDataToken =                                                 \
            mc_##Name##Text##Type##DerivedFromDataToken_new(crypto, textToken, value, status);                         \
        mc_##Name##Text##Type##Token_destroy(textToken);                                                               \
        if (!fromDataToken) {                                                                                          \
            return false;                                                                                              \
        }                                                                                                              \
                                                                                                                       \
        if (!useContentionFactor) {                                                                                    \
            /* FindTextPayload uses *fromDataToken */                                                                  \
            _mongocrypt_buffer_copy_to(mc_##Name##Text##Type##DerivedFromDataToken_get(fromDataToken), out);           \
            mc_##Name##Text##Type##DerivedFromDataToken_destroy(fromDataToken);                                        \
            return true;                                                                                               \
        }                                                                                                              \
                                                                                                                       \
        BSON_ASSERT(contentionFactor >= 0);                                                                            \
        /* InsertUpdatePayload continues through *fromDataTokenAndContentionFactor */                                  \
        mc_##Name##Text##Type##DerivedFromDataTokenAndContentionFactorToken_t *fromDataAndContentionFactor =           \
            mc_##Name##Text##Type##DerivedFromDataTokenAndContentionFactorToken_new(crypto,                            \
                                                                                    fromDataToken,                     \
                                                                                    (uint64_t)contentionFactor,        \
                                                                                    status);                           \
        mc_##Name##Text##Type##DerivedFromDataToken_destroy(fromDataToken);                                            \
        if (!fromDataAndContentionFactor) {                                                                            \
            return false;                                                                                              \
        }                                                                                                              \
        _mongocrypt_buffer_copy_to(                                                                                    \
            mc_##Name##Text##Type##DerivedFromDataTokenAndContentionFactorToken_get(fromDataAndContentionFactor),      \
            out);                                                                                                      \
        mc_##Name##Text##Type##DerivedFromDataTokenAndContentionFactorToken_destroy(fromDataAndContentionFactor);      \
        return true;                                                                                                   \
    }

DERIVE_TEXT_SEARCH_TOKEN_IMPL(EDC, Exact)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(ESC, Exact)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(EDC, Substring)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(ESC, Substring)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(EDC, Suffix)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(ESC, Suffix)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(EDC, Prefix)
DERIVE_TEXT_SEARCH_TOKEN_IMPL(ESC, Prefix)
#undef DERIVE_TEXT_SEARCH_TOKEN_IMPL

/**
 * Calculates:
 * ServerTextToken = HMAC(collectionLevel1Token, t)
 * ServerTextDerivedFromDataToken = HMAC(ServerTextToken, v)
 *
 *  = Exact|Substring|Suffix|Prefix
 * t = 1 for Exact, 2 for Substring, 3 for Suffix, 4 for Prefix
 *
 * ServerTextDerivedFromDataToken is saved to out.
 * Note that {out} is initialized even on failure.
 */
#define DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL(Type)                                                   \
    static bool _fle2_derive_serverText##Type##DerivedFromDataToken(                                                   \
        _mongocrypt_crypto_t *crypto,                                                                                  \
        _mongocrypt_buffer_t *out,                                                                                     \
        const mc_ServerTokenDerivationLevel1Token_t *level1Token,                                                      \
        const _mongocrypt_buffer_t *value,                                                                             \
        mongocrypt_status_t *status) {                                                                                 \
        BSON_ASSERT_PARAM(crypto);                                                                                     \
        BSON_ASSERT_PARAM(out);                                                                                        \
        BSON_ASSERT_PARAM(level1Token);                                                                                \
        BSON_ASSERT_PARAM(value);                                                                                      \
        BSON_ASSERT_PARAM(status);                                                                                     \
                                                                                                                       \
        _mongocrypt_buffer_init(out);                                                                                  \
        mc_ServerText##Type##Token_t *token = mc_ServerText##Type##Token_new(crypto, level1Token, status);             \
        if (!token) {                                                                                                  \
            return false;                                                                                              \
        }                                                                                                              \
        mc_ServerText##Type##DerivedFromDataToken_t *dataToken =                                                       \
            mc_ServerText##Type##DerivedFromDataToken_new(crypto, token, value, status);                               \
        mc_ServerText##Type##Token_destroy(token);                                                                     \
        if (!dataToken) {                                                                                              \
            return false;                                                                                              \
        }                                                                                                              \
        _mongocrypt_buffer_copy_to(mc_ServerText##Type##DerivedFromDataToken_get(dataToken), out);                     \
        mc_ServerText##Type##DerivedFromDataToken_destroy(dataToken);                                                  \
        return true;                                                                                                   \
    }

DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL(Exact)
DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL(Substring)
DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL(Suffix)
DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL(Prefix)
#undef DERIVE_TEXT_SEARCH_SERVER_DERIVED_FROM_DATA_TOKEN_IMPL

static bool _fle2_derive_serverDerivedFromDataToken(_mongocrypt_crypto_t *crypto,
                                                    _mongocrypt_buffer_t *out,
                                                    const mc_ServerTokenDerivationLevel1Token_t *level1Token,
                                                    const _mongocrypt_buffer_t *value,
                                                    mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT_PARAM(level1Token);
    BSON_ASSERT_PARAM(value);
    BSON_ASSERT_PARAM(status);

    _mongocrypt_buffer_init(out);

    mc_ServerDerivedFromDataToken_t *token = mc_ServerDerivedFromDataToken_new(crypto, level1Token, value, status);
    if (!token) {
        return false;
    }

    _mongocrypt_buffer_copy_to(mc_ServerDerivedFromDataToken_get(token), out);
    mc_ServerDerivedFromDataToken_destroy(token);
    return true;
}

static bool _fle2_placeholder_aes_ctr_encrypt(_mongocrypt_crypto_t *crypto,
                                              const _mongocrypt_buffer_t *key,
                                              const _mongocrypt_buffer_t *in,
                                              _mongocrypt_buffer_t *out,
                                              mongocrypt_status_t *status) {
    const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
    BSON_ASSERT_PARAM(crypto);
    BSON_ASSERT_PARAM(key);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_buffer_t iv;
    const uint32_t cipherlen = fle2alg->get_ciphertext_len(in->len, status);
    if (cipherlen == 0) {
        return false;
    }
    uint32_t written = 0;

    _mongocrypt_buffer_init_size(out, cipherlen);

    BSON_ASSERT(_mongocrypt_buffer_from_subrange(&iv, out, 0, MONGOCRYPT_IV_LEN));
    if (!_mongocrypt_random(crypto, &iv, MONGOCRYPT_IV_LEN, status)) {
        return false;
    }

    if (!fle2alg->do_encrypt(crypto, &iv, NULL /* aad */, key, in, out, &written, status)) {
        _mongocrypt_buffer_cleanup(out);
        _mongocrypt_buffer_init(out);
        return false;
    }

    return true;
}

static bool _fle2_placeholder_aes_aead_encrypt(_mongocrypt_key_broker_t *kb,
                                               const _mongocrypt_value_encryption_algorithm_t *algorithm,
                                               _mongocrypt_buffer_t *out,
                                               const _mongocrypt_buffer_t *keyId,
                                               const _mongocrypt_buffer_t *in,
                                               mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(keyId);
    BSON_ASSERT_PARAM(in);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT(kb->crypt);

    _mongocrypt_crypto_t *crypto = kb->crypt->crypto;
    _mongocrypt_buffer_t iv, key;
    const uint32_t cipherlen = algorithm->get_ciphertext_len(in->len, status);
    if (cipherlen == 0) {
        return false;
    }
    uint32_t written = 0;
    bool res;

    if (!_mongocrypt_key_broker_decrypted_key_by_id(kb, keyId, &key)) {
        CLIENT_ERR("unable to retrieve key");
        return false;
    }

    _mongocrypt_buffer_init_size(&iv, MONGOCRYPT_IV_LEN);
    if (!_mongocrypt_random(crypto, &iv, iv.len, status)) {
        _mongocrypt_buffer_cleanup(&key);
        _mongocrypt_buffer_cleanup(&iv);
        return false;
    }

    _mongocrypt_buffer_init_size(out, cipherlen);
    res = algorithm->do_encrypt(crypto, &iv, keyId, &key, in, out, &written, status);
    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&iv);

    if (!res) {
        _mongocrypt_buffer_cleanup(out);
        _mongocrypt_buffer_init(out);
        return false;
    }

    return true;
}

// FLE V1: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
//                            ECCDerivedFromDataTokenAndContentionFactor)
// FLE V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
// Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
// Text search: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || msize)
struct encrypted_token_metadata {
    mc_optional_bool_t is_leaf; // isLeaf for Range V2, none for all other cases
    mc_optional_uint32_t msize; // msize for text search, none for all other cases
};

static bool _fle2_derive_encrypted_token(_mongocrypt_crypto_t *crypto,
                                         _mongocrypt_buffer_t *out,
                                         const mc_CollectionsLevel1Token_t *collectionsLevel1Token,
                                         const _mongocrypt_buffer_t *escDerivedToken,
                                         const _mongocrypt_buffer_t *eccDerivedToken,
                                         struct encrypted_token_metadata token_metadata,
                                         mongocrypt_status_t *status) {
    // isLeaf and msize should never both be set.
    BSON_ASSERT(!token_metadata.is_leaf.set || !token_metadata.msize.set);
    mc_ECOCToken_t *ecocToken = mc_ECOCToken_new(crypto, collectionsLevel1Token, status);
    if (!ecocToken) {
        return false;
    }
    bool ok = false;

    _mongocrypt_buffer_t tmp;
    _mongocrypt_buffer_init(&tmp);
    const _mongocrypt_buffer_t *p = &tmp;
    if (!eccDerivedToken) {
        // FLE2v2
        if (token_metadata.is_leaf.set) {
            // Range V2; concat isLeaf
            _mongocrypt_buffer_t isLeafBuf;
            if (!_mongocrypt_buffer_copy_from_data_and_size(&isLeafBuf, (uint8_t[]){token_metadata.is_leaf.value}, 1)) {
                CLIENT_ERR("failed to create is_leaf buffer");
                goto fail;
            }
            if (!_mongocrypt_buffer_concat(&tmp, (_mongocrypt_buffer_t[]){*escDerivedToken, isLeafBuf}, 2)) {
                CLIENT_ERR("failed to allocate buffer");
                _mongocrypt_buffer_cleanup(&isLeafBuf);
                goto fail;
            }
            _mongocrypt_buffer_cleanup(&isLeafBuf);
        } else if (token_metadata.msize.set) {
            // Text search; concat msize
            _mongocrypt_buffer_t msizeBuf;
            // msize is a 3-byte value, so copy the 3 least significant bytes into the buffer in little-endian order.
            uint32_t le_msize = BSON_UINT32_TO_LE(token_metadata.msize.value);
            if (!_mongocrypt_buffer_copy_from_data_and_size(&msizeBuf, (uint8_t *)&le_msize, 3)) {
                CLIENT_ERR("failed to create msize buffer");
                goto fail;
            }
            if (!_mongocrypt_buffer_concat(&tmp, (_mongocrypt_buffer_t[]){*escDerivedToken, msizeBuf}, 2)) {
                CLIENT_ERR("failed to allocate buffer");
                _mongocrypt_buffer_cleanup(&msizeBuf);
                goto fail;
            }
            _mongocrypt_buffer_cleanup(&msizeBuf);
        } else {
            p = escDerivedToken;
        }

    } else {
        // FLE2v1
        const _mongocrypt_buffer_t tokens[] = {*escDerivedToken, *eccDerivedToken};
        if (!_mongocrypt_buffer_concat(&tmp, tokens, 2)) {
            CLIENT_ERR("failed to allocate buffer");
            goto fail;
        }
    }

    if (!_fle2_placeholder_aes_ctr_encrypt(crypto, mc_ECOCToken_get(ecocToken), p, out, status)) {
        goto fail;
    }

    ok = true;
fail:
    _mongocrypt_buffer_cleanup(&tmp);
    mc_ECOCToken_destroy(ecocToken);
    return ok;
}

// Field derivations shared by both INSERT and FIND payloads.
typedef struct {
    _mongocrypt_buffer_t tokenKey;
    mc_CollectionsLevel1Token_t *collectionsLevel1Token;
    mc_ServerDataEncryptionLevel1Token_t *serverDataEncryptionLevel1Token;
    mc_ServerTokenDerivationLevel1Token_t *serverTokenDerivationLevel1Token; // v2
    _mongocrypt_buffer_t edcDerivedToken;
    _mongocrypt_buffer_t escDerivedToken;
    _mongocrypt_buffer_t eccDerivedToken;            // v1
    _mongocrypt_buffer_t serverDerivedFromDataToken; // v2
} _FLE2EncryptedPayloadCommon_t;

static void _FLE2EncryptedPayloadCommon_cleanup(_FLE2EncryptedPayloadCommon_t *common) {
    if (!common) {
        return;
    }

    _mongocrypt_buffer_cleanup(&common->tokenKey);
    mc_CollectionsLevel1Token_destroy(common->collectionsLevel1Token);
    mc_ServerDataEncryptionLevel1Token_destroy(common->serverDataEncryptionLevel1Token);
    mc_ServerTokenDerivationLevel1Token_destroy(common->serverTokenDerivationLevel1Token);
    _mongocrypt_buffer_cleanup(&common->edcDerivedToken);
    _mongocrypt_buffer_cleanup(&common->escDerivedToken);
    _mongocrypt_buffer_cleanup(&common->eccDerivedToken);
    _mongocrypt_buffer_cleanup(&common->serverDerivedFromDataToken);
    // Zero out memory so `_FLE2EncryptedPayloadCommon_cleanup` is safe to call twice.
    *common = (_FLE2EncryptedPayloadCommon_t){{0}};
}

// _get_tokenKey returns the tokenKey identified by indexKeyId.
// Returns false on error.
static bool _get_tokenKey(_mongocrypt_key_broker_t *kb,
                          const _mongocrypt_buffer_t *indexKeyId,
                          _mongocrypt_buffer_t *tokenKey,
                          mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(indexKeyId);
    BSON_ASSERT_PARAM(tokenKey);

    _mongocrypt_buffer_t indexKey = {0};
    _mongocrypt_buffer_init(tokenKey);

    if (!_mongocrypt_key_broker_decrypted_key_by_id(kb, indexKeyId, &indexKey)) {
        CLIENT_ERR("unable to retrieve key");
        return false;
    }

    if (indexKey.len != MONGOCRYPT_KEY_LEN) {
        CLIENT_ERR("invalid indexKey, expected len=%d, got len=%" PRIu32, MONGOCRYPT_KEY_LEN, indexKey.len);
        _mongocrypt_buffer_cleanup(&indexKey);
        return false;
    }

    // indexKey is 3 equal sized keys: [Ke][Km][TokenKey]
    BSON_ASSERT(MONGOCRYPT_KEY_LEN == (3 * MONGOCRYPT_TOKEN_KEY_LEN));
    if (!_mongocrypt_buffer_copy_from_data_and_size(tokenKey,
                                                    indexKey.data + (2 * MONGOCRYPT_TOKEN_KEY_LEN),
                                                    MONGOCRYPT_TOKEN_KEY_LEN)) {
        CLIENT_ERR("failed allocating memory for token key");
        _mongocrypt_buffer_cleanup(&indexKey);
        return false;
    }
    _mongocrypt_buffer_cleanup(&indexKey);
    return true;
}

static bool _mongocrypt_fle2_placeholder_common(_mongocrypt_key_broker_t *kb,
                                                _FLE2EncryptedPayloadCommon_t *ret,
                                                const _mongocrypt_buffer_t *indexKeyId,
                                                const _mongocrypt_buffer_t *value,
                                                bool useContentionFactor,
                                                int64_t contentionFactor,
                                                mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(ret);
    BSON_ASSERT_PARAM(indexKeyId);
    BSON_ASSERT_PARAM(value);

    _mongocrypt_crypto_t *crypto = kb->crypt->crypto;
    *ret = (_FLE2EncryptedPayloadCommon_t){{0}};

    if (!_get_tokenKey(kb, indexKeyId, &ret->tokenKey, status)) {
        goto fail;
    }

    ret->collectionsLevel1Token = mc_CollectionsLevel1Token_new(crypto, &ret->tokenKey, status);
    if (!ret->collectionsLevel1Token) {
        CLIENT_ERR("unable to derive collectionLevel1Token");
        goto fail;
    }

    ret->serverDataEncryptionLevel1Token = mc_ServerDataEncryptionLevel1Token_new(crypto, &ret->tokenKey, status);
    if (!ret->serverDataEncryptionLevel1Token) {
        CLIENT_ERR("unable to derive serverDataEncryptionLevel1Token");
        goto fail;
    }

    if (!_fle2_derive_EDC_token(crypto,
                                &ret->edcDerivedToken,
                                ret->collectionsLevel1Token,
                                value,
                                useContentionFactor,
                                contentionFactor,
                                status)) {
        goto fail;
    }

    if (!_fle2_derive_ESC_token(crypto,
                                &ret->escDerivedToken,
                                ret->collectionsLevel1Token,
                                value,
                                useContentionFactor,
                                contentionFactor,
                                status)) {
        goto fail;
    }

    ret->serverTokenDerivationLevel1Token = mc_ServerTokenDerivationLevel1Token_new(crypto, &ret->tokenKey, status);
    if (!ret->serverTokenDerivationLevel1Token) {
        CLIENT_ERR("unable to derive serverTokenDerivationLevel1Token");
        goto fail;
    }

    if (!_fle2_derive_serverDerivedFromDataToken(crypto,
                                                 &ret->serverDerivedFromDataToken,
                                                 ret->serverTokenDerivationLevel1Token,
                                                 value,
                                                 status)) {
        goto fail;
    }

    return true;

fail:
    _FLE2EncryptedPayloadCommon_cleanup(ret);
    return false;
}

static bool _fle2_choose_contention_factor(mongocrypt_t *crypt,
                                           int64_t maxContentionFactor,
                                           int64_t *out,
                                           mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(out);
    if (crypt->opts.contention_factor_fn) {
        if (!crypt->opts.contention_factor_fn(maxContentionFactor + 1, out)) {
            CLIENT_ERR("contention_factor_fn failed");
            return false;
        }
        if (*out < 0 || *out > maxContentionFactor) {
            CLIENT_ERR("chosen contentionFactor out of range");
            return false;
        }
        return true;
    }

    return _mongocrypt_random_int64(crypt->crypto, maxContentionFactor + 1, out, status);
}

// Shared implementation for insert/update and insert/update ForRange (v2)
static bool _mongocrypt_fle2_placeholder_to_insert_update_common(_mongocrypt_key_broker_t *kb,
                                                                 mc_FLE2InsertUpdatePayloadV2_t *out,
                                                                 _FLE2EncryptedPayloadCommon_t *common,
                                                                 const mc_FLE2EncryptionPlaceholder_t *placeholder,
                                                                 bson_iter_t *value_iter,
                                                                 mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT_PARAM(common);
    BSON_ASSERT_PARAM(placeholder);
    BSON_ASSERT_PARAM(value_iter);
    BSON_ASSERT(kb->crypt);
    BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);

    _mongocrypt_crypto_t *crypto = kb->crypt->crypto;
    _mongocrypt_buffer_t value = {0};
    bool res = false;

    out->contentionFactor = 0; // k
    if (placeholder->maxContentionFactor > 0) {
        /* Choose a contentionFactor in the inclusive range [0,
         * placeholder->maxContentionFactor] */
        if (!_fle2_choose_contention_factor(kb->crypt,
                                            placeholder->maxContentionFactor,
                                            &out->contentionFactor,
                                            status)) {
            goto fail;
        }
    }

    _mongocrypt_buffer_from_iter(&value, value_iter);
    if (!_mongocrypt_fle2_placeholder_common(kb,
                                             common,
                                             &placeholder->index_key_id,
                                             &value,
                                             true, /* derive tokens using contentionFactor */
                                             out->contentionFactor,
                                             status)) {
        goto fail;
    }

    // d := EDCDerivedToken
    _mongocrypt_buffer_steal(&out->edcDerivedToken, &common->edcDerivedToken);
    // s := ESCDerivedToken
    _mongocrypt_buffer_steal(&out->escDerivedToken, &common->escDerivedToken);
    BSON_ASSERT(common->eccDerivedToken.data == NULL);

    // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
    // Or in Range V2, when using range: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || 0x00)
    // Or in Text Search, when using msize: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor ||
    // 0x000000)
    struct encrypted_token_metadata et_meta = {{0}};
    if (placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE) {
        // For range, we append isLeaf to the encryptedTokens.
        et_meta.is_leaf = OPT_BOOL(false);
    } else if (placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH) {
        // For text search, we append msize to the encryptedTokens.
        et_meta.msize = OPT_U32(0);
    }
    if (!_fle2_derive_encrypted_token(crypto,
                                      &out->encryptedTokens,
                                      common->collectionsLevel1Token,
                                      &out->escDerivedToken,
                                      NULL, // unused in v2
                                      et_meta,
                                      status)) {
        goto fail;
    }

    _mongocrypt_buffer_copy_to(&placeholder->index_key_id,
                               &out->indexKeyId); // u
    out->valueType = bson_iter_type(value_iter);  // t

    // v := UserKeyId + EncryptCBCAEAD(UserKey, value)
    {
        _mongocrypt_buffer_t ciphertext = {0};
        if (!_fle2_placeholder_aes_aead_encrypt(kb,
                                                _mcFLE2v2AEADAlgorithm(),
                                                &ciphertext,
                                                &placeholder->user_key_id,
                                                &value,
                                                status)) {
            goto fail;
        }
        const _mongocrypt_buffer_t v[2] = {placeholder->user_key_id, ciphertext};
        const bool ok = _mongocrypt_buffer_concat(&out->value, v, 2);
        _mongocrypt_buffer_cleanup(&ciphertext);
        if (!ok) {
            goto fail;
        }
    }

    // e := ServerDataEncryptionLevel1Token
    _mongocrypt_buffer_copy_to(mc_ServerDataEncryptionLevel1Token_get(common->serverDataEncryptionLevel1Token),
                               &out->serverEncryptionToken);

    // l := ServerDerivedFromDataToken
    _mongocrypt_buffer_steal(&out->serverDerivedFromDataToken, &common->serverDerivedFromDataToken);

    res = true;
fail:
    _mongocrypt_buffer_cleanup(&value);
    return res;
}

/**
 * Payload subtype 11: FLE2InsertUpdatePayloadV2
 *
 * {d: EDC, s: ESC, p: encToken,
 *  u: indexKeyId, t: valueType, v: value,
 *  e: serverToken, l: serverDerivedFromDataToken,
 *  k: contentionFactor}
 */
static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertext(_mongocrypt_key_broker_t *kb,
                                                                     _mongocrypt_marking_t *marking,
                                                                     _mongocrypt_ciphertext_t *ciphertext,
                                                                     mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT(kb->crypt);
    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);

    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    _FLE2EncryptedPayloadCommon_t common = {{0}};
    mc_FLE2InsertUpdatePayloadV2_t payload;
    mc_FLE2InsertUpdatePayloadV2_init(&payload);
    bool res = false;

    if (!_mongocrypt_fle2_placeholder_to_insert_update_common(kb,
                                                              &payload,
                                                              &common,
                                                              placeholder,
                                                              &placeholder->v_iter,
                                                              status)) {
        goto fail;
    }

    {
        bson_t out;
        bson_init(&out);
        mc_FLE2InsertUpdatePayloadV2_serialize(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }

    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2InsertUpdatePayloadV2.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2InsertUpdatePayloadV2;

    res = true;
fail:
    mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
    _FLE2EncryptedPayloadCommon_cleanup(&common);

    return res;
}

// get_edges creates and returns edges from an FLE2RangeInsertSpec. Returns NULL
// on error.
static mc_edges_t *get_edges(mc_FLE2RangeInsertSpec_t *insertSpec, size_t sparsity, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(insertSpec);

    bson_type_t value_type = bson_iter_type(&insertSpec->v);

    if (value_type == BSON_TYPE_INT32) {
        return mc_getEdgesInt32((mc_getEdgesInt32_args_t){.value = bson_iter_int32(&insertSpec->v),
                                                          .min = OPT_I32(bson_iter_int32(&insertSpec->min)),
                                                          .max = OPT_I32(bson_iter_int32(&insertSpec->max)),
                                                          .sparsity = sparsity,
                                                          .trimFactor = insertSpec->trimFactor},
                                status);
    }

    else if (value_type == BSON_TYPE_INT64) {
        return mc_getEdgesInt64((mc_getEdgesInt64_args_t){.value = bson_iter_int64(&insertSpec->v),
                                                          .min = OPT_I64(bson_iter_int64(&insertSpec->min)),
                                                          .max = OPT_I64(bson_iter_int64(&insertSpec->max)),
                                                          .sparsity = sparsity,
                                                          .trimFactor = insertSpec->trimFactor},
                                status);
    }

    else if (value_type == BSON_TYPE_DATE_TIME) {
        return mc_getEdgesInt64((mc_getEdgesInt64_args_t){.value = bson_iter_date_time(&insertSpec->v),
                                                          .min = OPT_I64(bson_iter_date_time(&insertSpec->min)),
                                                          .max = OPT_I64(bson_iter_date_time(&insertSpec->max)),
                                                          .sparsity = sparsity,
                                                          .trimFactor = insertSpec->trimFactor},
                                status);
    }

    else if (value_type == BSON_TYPE_DOUBLE) {
        mc_getEdgesDouble_args_t args = {.value = bson_iter_double(&insertSpec->v),
                                         .sparsity = sparsity,
                                         .trimFactor = insertSpec->trimFactor};
        if (insertSpec->precision.set) {
            // If precision is set, pass min/max/precision to mc_getEdgesDouble.
            // Do not pass min/max if precision is not set. All three must be set
            // or all three must be unset in mc_getTypeInfoDouble.
            args.min = OPT_DOUBLE(bson_iter_double(&insertSpec->min));
            args.max = OPT_DOUBLE(bson_iter_double(&insertSpec->max));
            args.precision = insertSpec->precision;
        }

        return mc_getEdgesDouble(args, status);
    }

    else if (value_type == BSON_TYPE_DECIMAL128) {
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
        const mc_dec128 value = mc_dec128_from_bson_iter(&insertSpec->v);
        mc_getEdgesDecimal128_args_t args = {
            .value = value,
            .sparsity = sparsity,
            .trimFactor = insertSpec->trimFactor,
        };
        if (insertSpec->precision.set) {
            const mc_dec128 min = mc_dec128_from_bson_iter(&insertSpec->min);
            const mc_dec128 max = mc_dec128_from_bson_iter(&insertSpec->max);
            args.min = OPT_MC_DEC128(min);
            args.max = OPT_MC_DEC128(max);
            args.precision = insertSpec->precision;
        }
        return mc_getEdgesDecimal128(args, status);
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
        CLIENT_ERR("unsupported BSON type (Decimal128) for range: libmongocrypt "
                   "was built without extended Decimal128 support");
        return NULL;
#endif
    }

    CLIENT_ERR("unsupported BSON type: %s for range", mc_bson_type_to_string(value_type));
    return NULL;
}

/**
 * Payload subtype 11: FLE2InsertUpdatePayloadV2 for range updates
 *
 * {d: EDC, s: ESC, p: encToken,
 *  u: indexKeyId, t: valueType, v: value,
 *  e: serverToken, l: serverDerivedFromDataToken,
 *  k: contentionFactor,
 *  g: [{d: EDC, s: ESC, l: serverDerivedFromDataToken, p: encToken},
 *      {d: EDC, s: ESC, l: serverDerivedFromDataToken, p: encToken},
 *      ...]}
 */
static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(_mongocrypt_key_broker_t *kb,
                                                                             _mongocrypt_marking_t *marking,
                                                                             _mongocrypt_ciphertext_t *ciphertext,
                                                                             mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT(kb->crypt);
    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);

    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    _FLE2EncryptedPayloadCommon_t common = {{0}};
    mc_FLE2InsertUpdatePayloadV2_t payload;
    mc_FLE2InsertUpdatePayloadV2_init(&payload);
    bool res = false;
    mc_edges_t *edges = NULL;

    // Parse the value ("v"), min ("min"), and max ("max") from
    // FLE2EncryptionPlaceholder for range insert.
    mc_FLE2RangeInsertSpec_t insertSpec;
    if (!mc_FLE2RangeInsertSpec_parse(&insertSpec, &placeholder->v_iter, status)) {
        goto fail;
    }

    if (!_mongocrypt_fle2_placeholder_to_insert_update_common(kb,
                                                              &payload,
                                                              &common,
                                                              &marking->u.fle2,
                                                              &insertSpec.v,
                                                              status)) {
        goto fail;
    }

    // g:= array
    {
        BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
        edges = get_edges(&insertSpec, (size_t)placeholder->sparsity, status);
        if (!edges) {
            goto fail;
        }

        for (size_t i = 0; i < mc_edges_len(edges); ++i) {
            // Create an EdgeTokenSet from each edge.
            bool loop_ok = false;
            const char *edge = mc_edges_get(edges, i);
            bool is_leaf = mc_edges_is_leaf(edges, edge);
            _mongocrypt_buffer_t edge_buf = {0};
            _FLE2EncryptedPayloadCommon_t edge_tokens = {{0}};
            _mongocrypt_buffer_t encryptedTokens = {0};
            mc_EdgeTokenSetV2_t etc = {{0}};

            if (!_mongocrypt_buffer_from_string(&edge_buf, edge)) {
                CLIENT_ERR("failed to copy edge to buffer");
                goto fail_loop;
            }

            if (!_mongocrypt_fle2_placeholder_common(kb,
                                                     &edge_tokens,
                                                     &placeholder->index_key_id,
                                                     &edge_buf,
                                                     true, /* derive tokens using contentionFactor */
                                                     payload.contentionFactor,
                                                     status)) {
                goto fail_loop;
            }
            BSON_ASSERT(edge_tokens.eccDerivedToken.data == NULL);

            // d := EDCDerivedToken
            _mongocrypt_buffer_steal(&etc.edcDerivedToken, &edge_tokens.edcDerivedToken);
            // s := ESCDerivedToken
            _mongocrypt_buffer_steal(&etc.escDerivedToken, &edge_tokens.escDerivedToken);

            // l := serverDerivedFromDataToken
            _mongocrypt_buffer_steal(&etc.serverDerivedFromDataToken, &edge_tokens.serverDerivedFromDataToken);

            // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor)
            // Or in Range V2: p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor || isLeaf)
            if (!_fle2_derive_encrypted_token(kb->crypt->crypto,
                                              &etc.encryptedTokens,
                                              edge_tokens.collectionsLevel1Token,
                                              &etc.escDerivedToken,
                                              NULL, // ecc unsed in FLE2v2
                                              (struct encrypted_token_metadata){.is_leaf = OPT_BOOL(is_leaf)},
                                              status)) {
                goto fail_loop;
            }

            _mc_array_append_val(&payload.edgeTokenSetArray, etc);

            loop_ok = true;
        fail_loop:
            _mongocrypt_buffer_cleanup(&encryptedTokens);
            _FLE2EncryptedPayloadCommon_cleanup(&edge_tokens);
            _mongocrypt_buffer_cleanup(&edge_buf);
            if (!loop_ok) {
                goto fail;
            }
        }
    }

    // Include "range" payload fields introduced in SERVER-91889.
    payload.sparsity = OPT_I64(placeholder->sparsity);
    payload.precision = insertSpec.precision;
    payload.trimFactor = OPT_I32(mc_edges_get_used_trimFactor(edges));
    bson_value_copy(bson_iter_value(&insertSpec.min), &payload.indexMin);
    bson_value_copy(bson_iter_value(&insertSpec.max), &payload.indexMax);

    {
        bson_t out;
        bson_init(&out);
        mc_FLE2InsertUpdatePayloadV2_serializeForRange(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }
    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2InsertUpdatePayloadV2.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2InsertUpdatePayloadV2;

    res = true;
fail:
    mc_edges_destroy(edges);
    mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
    _FLE2EncryptedPayloadCommon_cleanup(&common);

    return res;
}

/**
 * Sets up a mc_TextTokenSet_t type by generating its member tokens:
 * - edcDerivedToken = HMAC(HMAC(HMAC(EDCToken, t), v), cf)
 * - escDerivedToken = HMAC(HMAC(HMAC(ESCToken, t), v), cf)
 * - serverDerivedFromDataToken = HMAC(HMAC(ServerLevel1Token, t), v)
 * and the encrypted token:
 * - encryptedTokens = EncryptCTR(ECOCToken, escDerivedToken)
 *
 *  = Exact|Substring|Suffix|Prefix
 * t = 1 for Exact, 2 for Substring, 3 for Suffix, 4 for Prefix
 * cf = contentionFactor
 * EDC/ESC/ECOCToken are derived from {collLevel1Token}
 */
#define GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Type)                                                             \
    static bool _fle2_generate_Text##Type##TokenSet(_mongocrypt_key_broker_t *kb,                                      \
                                                    mc_Text##Type##TokenSet_t *out,                                    \
                                                    const _mongocrypt_buffer_t *value,                                 \
                                                    int64_t contentionFactor,                                          \
                                                    uint32_t msize,                                                    \
                                                    const mc_CollectionsLevel1Token_t *collLevel1Token,                \
                                                    const mc_ServerTokenDerivationLevel1Token_t *serverLevel1Token,    \
                                                    mongocrypt_status_t *status) {                                     \
        BSON_ASSERT_PARAM(kb);                                                                                         \
        BSON_ASSERT_PARAM(kb->crypt);                                                                                  \
        BSON_ASSERT_PARAM(out);                                                                                        \
        BSON_ASSERT_PARAM(value);                                                                                      \
        BSON_ASSERT_PARAM(collLevel1Token);                                                                            \
        BSON_ASSERT_PARAM(serverLevel1Token);                                                                          \
                                                                                                                       \
        if (!_fle2_derive_EDCText##Type##_token(kb->crypt->crypto,                                                     \
                                                &out->edcDerivedToken,                                                 \
                                                collLevel1Token,                                                       \
                                                value,                                                                 \
                                                true,                                                                  \
                                                contentionFactor,                                                      \
                                                status)) {                                                             \
            return false;                                                                                              \
        }                                                                                                              \
        if (!_fle2_derive_ESCText##Type##_token(kb->crypt->crypto,                                                     \
                                                &out->escDerivedToken,                                                 \
                                                collLevel1Token,                                                       \
                                                value,                                                                 \
                                                true,                                                                  \
                                                contentionFactor,                                                      \
                                                status)) {                                                             \
            return false;                                                                                              \
        }                                                                                                              \
        if (!_fle2_derive_serverText##Type##DerivedFromDataToken(kb->crypt->crypto,                                    \
                                                                 &out->serverDerivedFromDataToken,                     \
                                                                 serverLevel1Token,                                    \
                                                                 value,                                                \
                                                                 status)) {                                            \
            return false;                                                                                              \
        }                                                                                                              \
        if (!_fle2_derive_encrypted_token(kb->crypt->crypto,                                                           \
                                          &out->encryptedTokens,                                                       \
                                          collLevel1Token,                                                             \
                                          &out->escDerivedToken,                                                       \
                                          NULL,                                                                        \
                                          (struct encrypted_token_metadata){.msize = OPT_U32(msize)},                  \
                                          status)) {                                                                   \
            return false;                                                                                              \
        }                                                                                                              \
        return true;                                                                                                   \
    }                                                                                                                  \
    static bool _fle2_generate_Text##Type##FindTokenSet(                                                               \
        _mongocrypt_key_broker_t *kb,                                                                                  \
        mc_Text##Type##FindTokenSet_t *out,                                                                            \
        const _mongocrypt_buffer_t *value,                                                                             \
        const mc_CollectionsLevel1Token_t *collLevel1Token,                                                            \
        const mc_ServerTokenDerivationLevel1Token_t *serverLevel1Token,                                                \
        mongocrypt_status_t *status) {                                                                                 \
        BSON_ASSERT_PARAM(kb);                                                                                         \
        BSON_ASSERT_PARAM(kb->crypt);                                                                                  \
        BSON_ASSERT_PARAM(out);                                                                                        \
        BSON_ASSERT_PARAM(value);                                                                                      \
        BSON_ASSERT_PARAM(collLevel1Token);                                                                            \
        BSON_ASSERT_PARAM(serverLevel1Token);                                                                          \
        if (!_fle2_derive_EDCText##Type##_token(kb->crypt->crypto,                                                     \
                                                &out->edcDerivedToken,                                                 \
                                                collLevel1Token,                                                       \
                                                value,                                                                 \
                                                false,                                                                 \
                                                0,                                                                     \
                                                status)) {                                                             \
            return false;                                                                                              \
        }                                                                                                              \
        if (!_fle2_derive_ESCText##Type##_token(kb->crypt->crypto,                                                     \
                                                &out->escDerivedToken,                                                 \
                                                collLevel1Token,                                                       \
                                                value,                                                                 \
                                                false,                                                                 \
                                                0,                                                                     \
                                                status)) {                                                             \
            return false;                                                                                              \
        }                                                                                                              \
        if (!_fle2_derive_serverText##Type##DerivedFromDataToken(kb->crypt->crypto,                                    \
                                                                 &out->serverDerivedFromDataToken,                     \
                                                                 serverLevel1Token,                                    \
                                                                 value,                                                \
                                                                 status)) {                                            \
            return false;                                                                                              \
        }                                                                                                              \
        return true;                                                                                                   \
    }
GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Exact)
GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Substring)
GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Suffix)
GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL(Prefix)
#undef GENERATE_TEXT_SEARCH_TOKEN_SET_FOR_TYPE_IMPL

static bool _fle2_generate_TextSearchTokenSets(_mongocrypt_key_broker_t *kb,
                                               mc_FLE2InsertUpdatePayloadV2_t *payload,
                                               const _mongocrypt_buffer_t *indexKeyId,
                                               const mc_FLE2TextSearchInsertSpec_t *spec,
                                               int64_t contentionFactor,
                                               mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(payload);
    BSON_ASSERT_PARAM(indexKeyId);
    BSON_ASSERT_PARAM(spec);

    _mongocrypt_crypto_t *crypto = kb->crypt->crypto;
    mc_TextSearchTokenSets_t *tsts = &payload->textSearchTokenSets.tsts;
    _FLE2EncryptedPayloadCommon_t common = {{0}};
    bool res = false;

    mc_str_encode_sets_t *encodeSets = mc_text_search_str_encode(spec, status);
    if (!encodeSets) {
        goto fail;
    }

    // Start the token derivations
    if (!_get_tokenKey(kb, indexKeyId, &common.tokenKey, status)) {
        goto fail;
    }

    common.collectionsLevel1Token = mc_CollectionsLevel1Token_new(crypto, &common.tokenKey, status);
    if (!common.collectionsLevel1Token) {
        CLIENT_ERR("unable to derive collectionLevel1Token");
        goto fail;
    }

    common.serverTokenDerivationLevel1Token = mc_ServerTokenDerivationLevel1Token_new(crypto, &common.tokenKey, status);
    if (!common.serverTokenDerivationLevel1Token) {
        CLIENT_ERR("unable to derive serverTokenDerivationLevel1Token");
        goto fail;
    }

    // Generate exact token set singleton
    {
        _mongocrypt_buffer_t asBsonValue;
        _mongocrypt_buffer_init(&asBsonValue);
        BSON_ASSERT(encodeSets->exact.len < INT_MAX);
        _mongocrypt_buffer_copy_from_string_as_bson_value(&asBsonValue,
                                                          (const char *)encodeSets->exact.data,
                                                          (int)encodeSets->exact.len);
        if (!_fle2_generate_TextExactTokenSet(kb,
                                              &tsts->exact,
                                              &asBsonValue,
                                              contentionFactor,
                                              // For the exact token, report total msize of the token set.
                                              encodeSets->msize,
                                              common.collectionsLevel1Token,
                                              common.serverTokenDerivationLevel1Token,
                                              status)) {
            _mongocrypt_buffer_cleanup(&asBsonValue);
            goto fail;
        }
        _mongocrypt_buffer_cleanup(&asBsonValue);
    }

    const char *substring;
    uint32_t bytelen;
    uint32_t appendCount;

    // Generate array of substring token sets
    if (encodeSets->substring_set) {
        mc_substring_set_iter_t set_itr;
        mc_substring_set_iter_init(&set_itr, encodeSets->substring_set);

        while (mc_substring_set_iter_next(&set_itr, &substring, &bytelen, &appendCount)) {
            BSON_ASSERT(appendCount > 0);
            BSON_ASSERT(bytelen < INT_MAX);

            mc_TextSubstringTokenSet_t tset = {{0}};

            _mongocrypt_buffer_t asBsonValue;
            _mongocrypt_buffer_init(&asBsonValue);
            _mongocrypt_buffer_copy_from_string_as_bson_value(&asBsonValue, substring, (int)bytelen);

            // For substring, prefix, and suffix tokens, report 0 as the msize.
            if (!_fle2_generate_TextSubstringTokenSet(kb,
                                                      &tset,
                                                      &asBsonValue,
                                                      contentionFactor,
                                                      0 /* msize */,
                                                      common.collectionsLevel1Token,
                                                      common.serverTokenDerivationLevel1Token,
                                                      status)) {
                _mongocrypt_buffer_cleanup(&asBsonValue);
                mc_TextSubstringTokenSet_cleanup(&tset);
                goto fail;
            }
            _mongocrypt_buffer_cleanup(&asBsonValue);

            if (appendCount > 1) {
                mc_TextSubstringTokenSet_t tset_copy;
                mc_TextSubstringTokenSet_shallow_copy(&tset, &tset_copy);
                for (; appendCount > 1; appendCount--) {
                    _mc_array_append_val(&tsts->substringArray, tset_copy);
                }
            }
            _mc_array_append_val(&tsts->substringArray, tset); // array now owns tset
        }
    }

    // Generate array of suffix token sets
    if (encodeSets->suffix_set) {
        mc_affix_set_iter_t set_itr;
        mc_affix_set_iter_init(&set_itr, encodeSets->suffix_set);

        while (mc_affix_set_iter_next(&set_itr, &substring, &bytelen, &appendCount)) {
            BSON_ASSERT(appendCount > 0);
            BSON_ASSERT(bytelen < INT_MAX);

            mc_TextSuffixTokenSet_t tset = {{0}};
            mc_TextSuffixTokenSet_init(&tset);

            _mongocrypt_buffer_t asBsonValue;
            _mongocrypt_buffer_init(&asBsonValue);
            _mongocrypt_buffer_copy_from_string_as_bson_value(&asBsonValue, substring, (int)bytelen);

            if (!_fle2_generate_TextSuffixTokenSet(kb,
                                                   &tset,
                                                   &asBsonValue,
                                                   contentionFactor,
                                                   0 /* msize */,
                                                   common.collectionsLevel1Token,
                                                   common.serverTokenDerivationLevel1Token,
                                                   status)) {
                _mongocrypt_buffer_cleanup(&asBsonValue);
                mc_TextSuffixTokenSet_cleanup(&tset);
                goto fail;
            }
            _mongocrypt_buffer_cleanup(&asBsonValue);

            if (appendCount > 1) {
                mc_TextSuffixTokenSet_t tset_copy;
                mc_TextSuffixTokenSet_shallow_copy(&tset, &tset_copy);
                for (; appendCount > 1; appendCount--) {
                    _mc_array_append_val(&tsts->suffixArray, tset_copy);
                }
            }
            _mc_array_append_val(&tsts->suffixArray, tset); // array now owns tset
        }
    }

    // Generate array of prefix token sets
    if (encodeSets->prefix_set) {
        mc_affix_set_iter_t set_itr;
        mc_affix_set_iter_init(&set_itr, encodeSets->prefix_set);

        while (mc_affix_set_iter_next(&set_itr, &substring, &bytelen, &appendCount)) {
            BSON_ASSERT(appendCount > 0);
            BSON_ASSERT(bytelen < INT_MAX);

            mc_TextPrefixTokenSet_t tset = {{0}};
            mc_TextPrefixTokenSet_init(&tset);

            _mongocrypt_buffer_t asBsonValue;
            _mongocrypt_buffer_init(&asBsonValue);
            _mongocrypt_buffer_copy_from_string_as_bson_value(&asBsonValue, substring, (int)bytelen);

            if (!_fle2_generate_TextPrefixTokenSet(kb,
                                                   &tset,
                                                   &asBsonValue,
                                                   contentionFactor,
                                                   0 /* msize */,
                                                   common.collectionsLevel1Token,
                                                   common.serverTokenDerivationLevel1Token,
                                                   status)) {
                _mongocrypt_buffer_cleanup(&asBsonValue);
                mc_TextPrefixTokenSet_cleanup(&tset);
                goto fail;
            }
            _mongocrypt_buffer_cleanup(&asBsonValue);

            if (appendCount > 1) {
                mc_TextPrefixTokenSet_t tset_copy;
                mc_TextPrefixTokenSet_shallow_copy(&tset, &tset_copy);
                for (; appendCount > 1; appendCount--) {
                    _mc_array_append_val(&tsts->prefixArray, tset_copy); // array now owns tset_copy
                }
            }
            _mc_array_append_val(&tsts->prefixArray, tset); // moves ownership of tset
        }
    }
    payload->textSearchTokenSets.set = true;
    res = true;
fail:
    _FLE2EncryptedPayloadCommon_cleanup(&common);
    mc_str_encode_sets_destroy(encodeSets);
    return res;
}

static bool _fle2_generate_TextSearchFindTokenSets(_mongocrypt_key_broker_t *kb,
                                                   mc_TextSearchFindTokenSets_t *out,
                                                   const _mongocrypt_buffer_t *indexKeyId,
                                                   const mc_FLE2TextSearchInsertSpec_t *spec,
                                                   mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(kb->crypt);
    BSON_ASSERT_PARAM(out);
    BSON_ASSERT_PARAM(indexKeyId);
    BSON_ASSERT_PARAM(spec);

    _mongocrypt_crypto_t *crypto = kb->crypt->crypto;
    _FLE2EncryptedPayloadCommon_t common = {{0}};
    _mongocrypt_buffer_t asBsonValue = {0};
    bool res = false;

    int operator_count = (int)spec->substr.set + (int)spec->suffix.set + (int)spec->prefix.set;
    if (operator_count > 1) {
        CLIENT_ERR("Text search query specification cannot contain multiple query type specifications");
        goto fail;
    }

    if (!mc_text_search_str_query(spec, &asBsonValue, status)) {
        goto fail;
    }

    // Start the token derivations
    if (!_get_tokenKey(kb, indexKeyId, &common.tokenKey, status)) {
        goto fail;
    }

    common.collectionsLevel1Token = mc_CollectionsLevel1Token_new(crypto, &common.tokenKey, status);
    if (!common.collectionsLevel1Token) {
        CLIENT_ERR("unable to derive collectionLevel1Token");
        goto fail;
    }

    common.serverTokenDerivationLevel1Token = mc_ServerTokenDerivationLevel1Token_new(crypto, &common.tokenKey, status);
    if (!common.serverTokenDerivationLevel1Token) {
        CLIENT_ERR("unable to derive serverTokenDerivationLevel1Token");
        goto fail;
    }

    if (spec->substr.set) {
        if (!_fle2_generate_TextSubstringFindTokenSet(kb,
                                                      &out->substring.value,
                                                      &asBsonValue,
                                                      common.collectionsLevel1Token,
                                                      common.serverTokenDerivationLevel1Token,
                                                      status)) {
            goto fail;
        }
        out->substring.set = true;
    } else if (spec->suffix.set) {
        if (!_fle2_generate_TextSuffixFindTokenSet(kb,
                                                   &out->suffix.value,
                                                   &asBsonValue,
                                                   common.collectionsLevel1Token,
                                                   common.serverTokenDerivationLevel1Token,
                                                   status)) {
            goto fail;
        }
        out->suffix.set = true;

    } else if (spec->prefix.set) {
        if (!_fle2_generate_TextPrefixFindTokenSet(kb,
                                                   &out->prefix.value,
                                                   &asBsonValue,
                                                   common.collectionsLevel1Token,
                                                   common.serverTokenDerivationLevel1Token,
                                                   status)) {
            goto fail;
        }
        out->prefix.set = true;
    } else {
        if (!_fle2_generate_TextExactFindTokenSet(kb,
                                                  &out->exact.value,
                                                  &asBsonValue,
                                                  common.collectionsLevel1Token,
                                                  common.serverTokenDerivationLevel1Token,
                                                  status)) {
            goto fail;
        }
        out->exact.set = true;
    }
    res = true;
fail:
    _mongocrypt_buffer_cleanup(&asBsonValue);
    _FLE2EncryptedPayloadCommon_cleanup(&common);
    return res;
}

/**
 * Payload subtype 11: FLE2InsertUpdatePayloadV2 for text search inserts/updates
 *
 * {v: value, u: indexKeyId, t: valueType, k: contentionFactor, e: serverToken,
 *  b: { e: {d: EDC_exact, s: ESC_exact, l: svrDFDToken_exact, p: encToken_exact},
 *       s: [{d: EDC_substr, s: ESC_substr, l: svrDFDToken_substr, p: encToken_substr}, ...]
 *       u: [{d: EDC_suffix, s: ESC_suffix, l: svrDFDToken_suffix, p: encToken_suffix}, ...]
 *       p: [{d: EDC_prefix, s: ESC_prefix, l: svrDFDToken_prefix, p: encToken_prefix}, ...]
 *     },
 *  d: bogusToken, s: bogusToken, l: bogusToken, p: bogusCiphertext
 * }
 */
static bool _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForTextSearch(_mongocrypt_key_broker_t *kb,
                                                                                  _mongocrypt_marking_t *marking,
                                                                                  _mongocrypt_ciphertext_t *ciphertext,
                                                                                  mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT(kb->crypt);
    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);

    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
    BSON_ASSERT(placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH);

    _FLE2EncryptedPayloadCommon_t common = {{0}};
    mc_FLE2InsertUpdatePayloadV2_t payload;
    mc_FLE2InsertUpdatePayloadV2_init(&payload);

    _mongocrypt_buffer_t value = {0};
    bool res = false;

    mc_FLE2TextSearchInsertSpec_t insertSpec;
    if (!mc_FLE2TextSearchInsertSpec_parse(&insertSpec, &placeholder->v_iter, status)) {
        goto fail;
    }

    // One of substr/suffix/prefix must be set for inserts
    if (!(insertSpec.substr.set || insertSpec.suffix.set || insertSpec.prefix.set)) {
        CLIENT_ERR("FLE2TextSearchInsertSpec is missing a substring, suffix, or prefix index specification");
        goto fail;
    }

    // t
    payload.valueType = BSON_TYPE_UTF8;

    // k
    payload.contentionFactor = 0;
    if (placeholder->maxContentionFactor > 0) {
        /* Choose a contentionFactor in the inclusive range [0,
         * placeholder->maxContentionFactor] */
        if (!_fle2_choose_contention_factor(kb->crypt,
                                            placeholder->maxContentionFactor,
                                            &payload.contentionFactor,
                                            status)) {
            goto fail;
        }
    }

    // u
    _mongocrypt_buffer_copy_to(&placeholder->index_key_id, &payload.indexKeyId);

    _mongocrypt_buffer_from_iter(&value, &insertSpec.v_iter);
    if (!_mongocrypt_fle2_placeholder_common(kb,
                                             &common,
                                             &placeholder->index_key_id,
                                             &value,
                                             true, /* derive tokens using contentionFactor */
                                             payload.contentionFactor,
                                             status)) {
        goto fail;
    }

    // (d, s, l) are never used for text search, so just set these to bogus buffers of
    // correct length.
    BSON_ASSERT(_mongocrypt_buffer_steal_from_data_and_size(&payload.edcDerivedToken,
                                                            bson_malloc0(MONGOCRYPT_HMAC_SHA256_LEN),
                                                            MONGOCRYPT_HMAC_SHA256_LEN));
    _mongocrypt_buffer_copy_to(&payload.edcDerivedToken, &payload.escDerivedToken);
    _mongocrypt_buffer_copy_to(&payload.edcDerivedToken, &payload.serverDerivedFromDataToken);

    // p := EncryptCTR(ECOCToken, ESCDerivedFromDataTokenAndContentionFactor | 0x000000)
    // Since p is never used for text search, this just sets p to a bogus ciphertext of
    // the correct length.
    if (!_fle2_derive_encrypted_token(kb->crypt->crypto,
                                      &payload.encryptedTokens,
                                      common.collectionsLevel1Token,
                                      &payload.escDerivedToken, // bogus
                                      NULL,                     // unused in FLE2v2
                                      (struct encrypted_token_metadata){.msize = OPT_U32(0)},
                                      status)) {
        goto fail;
    }

    // v := UserKeyId + EncryptCBCAEAD(UserKey, value)
    {
        _mongocrypt_buffer_t ciphertext = {0};
        if (!_fle2_placeholder_aes_aead_encrypt(kb,
                                                _mcFLE2v2AEADAlgorithm(),
                                                &ciphertext,
                                                &placeholder->user_key_id,
                                                &value,
                                                status)) {
            goto fail;
        }
        const _mongocrypt_buffer_t v[2] = {placeholder->user_key_id, ciphertext};
        const bool ok = _mongocrypt_buffer_concat(&payload.value, v, 2);
        _mongocrypt_buffer_cleanup(&ciphertext);
        if (!ok) {
            goto fail;
        }
    }
    // e := ServerDataEncryptionLevel1Token
    _mongocrypt_buffer_copy_to(mc_ServerDataEncryptionLevel1Token_get(common.serverDataEncryptionLevel1Token),
                               &payload.serverEncryptionToken);

    // b
    if (!_fle2_generate_TextSearchTokenSets(kb,
                                            &payload,
                                            &placeholder->index_key_id,
                                            &insertSpec,
                                            payload.contentionFactor,
                                            status)) {
        goto fail;
    }

    {
        bson_t out;
        bson_init(&out);
        mc_FLE2InsertUpdatePayloadV2_serializeForTextSearch(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }
    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2InsertUpdatePayloadV2.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2InsertUpdatePayloadV2;

    res = true;
fail:
    mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
    _mongocrypt_buffer_cleanup(&value);
    _FLE2EncryptedPayloadCommon_cleanup(&common);
    return res;
}

/**
 * Payload subtype 12: FLE2FindEqualityPayloadV2
 *
 * {d: EDC, s: ESC, l: serverDerivedFromDataToken, cm: maxContentionFactor}
 */
static bool _mongocrypt_fle2_placeholder_to_find_ciphertext(_mongocrypt_key_broker_t *kb,
                                                            _mongocrypt_marking_t *marking,
                                                            _mongocrypt_ciphertext_t *ciphertext,
                                                            mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);

    _FLE2EncryptedPayloadCommon_t common = {{0}};
    _mongocrypt_buffer_t value = {0};
    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    mc_FLE2FindEqualityPayloadV2_t payload;
    bool res = false;

    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
    BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND);

    _mongocrypt_buffer_init(&value);
    mc_FLE2FindEqualityPayloadV2_init(&payload);

    _mongocrypt_buffer_from_iter(&value, &placeholder->v_iter);

    if (!_mongocrypt_fle2_placeholder_common(kb,
                                             &common,
                                             &placeholder->index_key_id,
                                             &value,
                                             false, /* derive tokens without contentionFactor */
                                             placeholder->maxContentionFactor, /* ignored */
                                             status)) {
        goto fail;
    }
    BSON_ASSERT(common.eccDerivedToken.data == NULL);

    // d := EDCDerivedToken
    _mongocrypt_buffer_steal(&payload.edcDerivedToken, &common.edcDerivedToken);
    // s := ESCDerivedToken
    _mongocrypt_buffer_steal(&payload.escDerivedToken, &common.escDerivedToken);
    // l := serverDerivedFromDataToken
    _mongocrypt_buffer_steal(&payload.serverDerivedFromDataToken, &common.serverDerivedFromDataToken);

    // cm := maxContentionFactor
    payload.maxContentionFactor = placeholder->maxContentionFactor;

    {
        bson_t out;
        bson_init(&out);
        mc_FLE2FindEqualityPayloadV2_serialize(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }
    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2FindEqualityPayloadV2.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2FindEqualityPayloadV2;

    res = true;
fail:
    mc_FLE2FindEqualityPayloadV2_cleanup(&payload);
    _mongocrypt_buffer_cleanup(&value);
    _FLE2EncryptedPayloadCommon_cleanup(&common);

    return res;
}

static bool isInfinite(bson_iter_t *iter) {
    return mc_isinf(bson_iter_double(iter));
}

// mc_get_mincover_from_FLE2RangeFindSpec creates and returns a mincover from an
// FLE2RangeFindSpec. Returns NULL on error.
mc_mincover_t *
mc_get_mincover_from_FLE2RangeFindSpec(mc_FLE2RangeFindSpec_t *findSpec, size_t sparsity, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(findSpec);
    BSON_ASSERT(findSpec->edgesInfo.set);

    bson_type_t bsonType = bson_iter_type(&findSpec->edgesInfo.value.indexMin);

    if (bson_iter_type(&findSpec->edgesInfo.value.indexMin) != bson_iter_type(&findSpec->edgesInfo.value.indexMax)) {
        CLIENT_ERR("indexMin and indexMax must have the same type. Got: %s indexMin and "
                   "%s indexMax",
                   mc_bson_type_to_string(bson_iter_type(&findSpec->edgesInfo.value.indexMin)),
                   mc_bson_type_to_string(bson_iter_type(&findSpec->edgesInfo.value.indexMax)));
        return NULL;
    }

    bson_iter_t lowerBound = findSpec->edgesInfo.value.lowerBound;
    bson_iter_t upperBound = findSpec->edgesInfo.value.upperBound;
    bool includeLowerBound = findSpec->edgesInfo.value.lbIncluded;
    bool includeUpperBound = findSpec->edgesInfo.value.ubIncluded;

    // Open-ended ranges are represented with infinity as the other endpoint.
    // Resolve infinite bounds at this point to end at the min or max for this
    // index.
    if (isInfinite(&lowerBound)) {
        lowerBound = findSpec->edgesInfo.value.indexMin;
        includeLowerBound = true;
    }
    if (isInfinite(&upperBound)) {
        upperBound = findSpec->edgesInfo.value.indexMax;
        includeUpperBound = true;
    }

    if (bson_iter_type(&lowerBound) != bsonType) {
        CLIENT_ERR("expected lowerBound to match index type %s, got %s",
                   mc_bson_type_to_string(bsonType),
                   mc_bson_type_to_string(bson_iter_type(&lowerBound)));
        return NULL;
    }

    if (bson_iter_type(&upperBound) != bsonType) {
        CLIENT_ERR("expected upperBound to match index type %s, got %s",
                   mc_bson_type_to_string(bsonType),
                   mc_bson_type_to_string(bson_iter_type(&upperBound)));
        return NULL;
    }

    switch (bsonType) {
    case BSON_TYPE_INT32:
        BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_INT32);
        BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_INT32);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_INT32);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_INT32);

        return mc_getMincoverInt32(
            (mc_getMincoverInt32_args_t){.lowerBound = bson_iter_int32(&lowerBound),
                                         .includeLowerBound = includeLowerBound,
                                         .upperBound = bson_iter_int32(&upperBound),
                                         .includeUpperBound = includeUpperBound,
                                         .min = OPT_I32(bson_iter_int32(&findSpec->edgesInfo.value.indexMin)),
                                         .max = OPT_I32(bson_iter_int32(&findSpec->edgesInfo.value.indexMax)),
                                         .sparsity = sparsity,
                                         .trimFactor = findSpec->edgesInfo.value.trimFactor},
            status);

    case BSON_TYPE_INT64:
        BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_INT64);
        BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_INT64);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_INT64);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_INT64);
        return mc_getMincoverInt64(
            (mc_getMincoverInt64_args_t){.lowerBound = bson_iter_int64(&lowerBound),
                                         .includeLowerBound = includeLowerBound,
                                         .upperBound = bson_iter_int64(&upperBound),
                                         .includeUpperBound = includeUpperBound,
                                         .min = OPT_I64(bson_iter_int64(&findSpec->edgesInfo.value.indexMin)),
                                         .max = OPT_I64(bson_iter_int64(&findSpec->edgesInfo.value.indexMax)),
                                         .sparsity = sparsity,
                                         .trimFactor = findSpec->edgesInfo.value.trimFactor},
            status);
    case BSON_TYPE_DATE_TIME:
        BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_DATE_TIME);
        BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_DATE_TIME);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_DATE_TIME);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_DATE_TIME);
        return mc_getMincoverInt64(
            (mc_getMincoverInt64_args_t){.lowerBound = bson_iter_date_time(&lowerBound),
                                         .includeLowerBound = includeLowerBound,
                                         .upperBound = bson_iter_date_time(&upperBound),
                                         .includeUpperBound = includeUpperBound,
                                         .min = OPT_I64(bson_iter_date_time(&findSpec->edgesInfo.value.indexMin)),
                                         .max = OPT_I64(bson_iter_date_time(&findSpec->edgesInfo.value.indexMax)),
                                         .sparsity = sparsity,
                                         .trimFactor = findSpec->edgesInfo.value.trimFactor},
            status);
    case BSON_TYPE_DOUBLE: {
        BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_DOUBLE);
        BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_DOUBLE);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_DOUBLE);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_DOUBLE);

        mc_getMincoverDouble_args_t args = {.lowerBound = bson_iter_double(&lowerBound),
                                            .includeLowerBound = includeLowerBound,
                                            .upperBound = bson_iter_double(&upperBound),
                                            .includeUpperBound = includeUpperBound,
                                            .sparsity = sparsity,
                                            .trimFactor = findSpec->edgesInfo.value.trimFactor};
        if (findSpec->edgesInfo.value.precision.set) {
            // If precision is set, pass min/max/precision to mc_getMincoverDouble.
            // Do not pass min/max if precision is not set. All three must be set
            // or all three must be unset in mc_getTypeInfoDouble.
            args.min = OPT_DOUBLE(bson_iter_double(&findSpec->edgesInfo.value.indexMin));
            args.max = OPT_DOUBLE(bson_iter_double(&findSpec->edgesInfo.value.indexMax));
            args.precision = findSpec->edgesInfo.value.precision;
        }
        return mc_getMincoverDouble(args, status);
    }
    case BSON_TYPE_DECIMAL128: {
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
        BSON_ASSERT(bson_iter_type(&lowerBound) == BSON_TYPE_DECIMAL128);
        BSON_ASSERT(bson_iter_type(&upperBound) == BSON_TYPE_DECIMAL128);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMin) == BSON_TYPE_DECIMAL128);
        BSON_ASSERT(bson_iter_type(&findSpec->edgesInfo.value.indexMax) == BSON_TYPE_DECIMAL128);

        mc_getMincoverDecimal128_args_t args = {.lowerBound = mc_dec128_from_bson_iter(&lowerBound),
                                                .includeLowerBound = includeLowerBound,
                                                .upperBound = mc_dec128_from_bson_iter(&upperBound),
                                                .includeUpperBound = includeUpperBound,
                                                .sparsity = sparsity,
                                                .trimFactor = findSpec->edgesInfo.value.trimFactor};
        if (findSpec->edgesInfo.value.precision.set) {
            args.min = OPT_MC_DEC128(mc_dec128_from_bson_iter(&findSpec->edgesInfo.value.indexMin));
            args.max = OPT_MC_DEC128(mc_dec128_from_bson_iter(&findSpec->edgesInfo.value.indexMax));
            args.precision = findSpec->edgesInfo.value.precision;
        }
        return mc_getMincoverDecimal128(args, status);
#else // ↑↑↑↑↑↑↑↑ With Decimal128 / Without ↓↓↓↓↓↓↓↓↓↓
        CLIENT_ERR("FLE2 find is not supported for Decimal128: libmongocrypt "
                   "was built without Decimal128 support");
        return NULL;
#endif
    }

    case BSON_TYPE_EOD:
    case BSON_TYPE_UTF8:
    case BSON_TYPE_DOCUMENT:
    case BSON_TYPE_ARRAY:
    case BSON_TYPE_BINARY:
    case BSON_TYPE_UNDEFINED:
    case BSON_TYPE_OID:
    case BSON_TYPE_BOOL:
    case BSON_TYPE_NULL:
    case BSON_TYPE_REGEX:
    case BSON_TYPE_DBPOINTER:
    case BSON_TYPE_CODE:
    case BSON_TYPE_SYMBOL:
    case BSON_TYPE_CODEWSCOPE:
    case BSON_TYPE_TIMESTAMP:
    case BSON_TYPE_MAXKEY:
    case BSON_TYPE_MINKEY:
    default: CLIENT_ERR("FLE2 find is not supported for type: %s", mc_bson_type_to_string(bsonType)); return NULL;
    }
}

/**
 * Payload subtype 13: FLE2FindRangePayloadV2
 *
 * {cm: maxContentionFactor,
 *  g: [{d: EDC, s: ESC, l: serverDerivedFromDataToken}, ...]}
 */
static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(_mongocrypt_key_broker_t *kb,
                                                                    _mongocrypt_marking_t *marking,
                                                                    _mongocrypt_ciphertext_t *ciphertext,
                                                                    mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);

    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    mc_FLE2FindRangePayloadV2_t payload;
    bool res = false;
    mc_mincover_t *mincover = NULL;
    _mongocrypt_buffer_t tokenKey = {0};

    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
    BSON_ASSERT(placeholder);
    BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND);
    BSON_ASSERT(placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);
    mc_FLE2FindRangePayloadV2_init(&payload);

    // Parse the query bounds and index bounds from FLE2EncryptionPlaceholder for
    // range find.
    mc_FLE2RangeFindSpec_t findSpec;
    if (!mc_FLE2RangeFindSpec_parse(&findSpec, &placeholder->v_iter, status)) {
        goto fail;
    }

    if (findSpec.edgesInfo.set) {
        // cm := Queryable Encryption max contentionFactor
        payload.payload.value.maxContentionFactor = placeholder->maxContentionFactor;

        // g:= array
        {
            BSON_ASSERT(placeholder->sparsity >= 0 && (uint64_t)placeholder->sparsity <= (uint64_t)SIZE_MAX);
            mincover = mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, (size_t)placeholder->sparsity, status);
            if (!mincover) {
                goto fail;
            }

            for (size_t i = 0; i < mc_mincover_len(mincover); i++) {
                // Create a EdgeFindTokenSet from each edge.
                bool loop_ok = false;
                const char *edge = mc_mincover_get(mincover, i);
                _mongocrypt_buffer_t edge_buf = {0};
                _FLE2EncryptedPayloadCommon_t edge_tokens = {{0}};
                mc_EdgeFindTokenSetV2_t eftc = {{0}};

                if (!_mongocrypt_buffer_from_string(&edge_buf, edge)) {
                    CLIENT_ERR("failed to copy edge to buffer");
                    goto fail_loop;
                }

                if (!_mongocrypt_fle2_placeholder_common(kb,
                                                         &edge_tokens,
                                                         &placeholder->index_key_id,
                                                         &edge_buf,
                                                         false, /* derive tokens without using contentionFactor */
                                                         placeholder->maxContentionFactor, /* ignored */
                                                         status)) {
                    goto fail_loop;
                }

                // d := EDCDerivedToken
                _mongocrypt_buffer_steal(&eftc.edcDerivedToken, &edge_tokens.edcDerivedToken);
                // s := ESCDerivedToken
                _mongocrypt_buffer_steal(&eftc.escDerivedToken, &edge_tokens.escDerivedToken);

                // l := serverDerivedFromDataToken
                _mongocrypt_buffer_steal(&eftc.serverDerivedFromDataToken, &edge_tokens.serverDerivedFromDataToken);

                _mc_array_append_val(&payload.payload.value.edgeFindTokenSetArray, eftc);

                loop_ok = true;
            fail_loop:
                _FLE2EncryptedPayloadCommon_cleanup(&edge_tokens);
                _mongocrypt_buffer_cleanup(&edge_buf);
                if (!loop_ok) {
                    goto fail;
                }
            }
        }
        payload.payload.set = true;

        // Include "range" payload fields introduced in SERVER-91889.
        payload.sparsity = OPT_I64(placeholder->sparsity);
        payload.precision = findSpec.edgesInfo.value.precision;
        payload.trimFactor = OPT_I32(mc_mincover_get_used_trimFactor(mincover));
        bson_value_copy(bson_iter_value(&findSpec.edgesInfo.value.indexMin), &payload.indexMin);
        bson_value_copy(bson_iter_value(&findSpec.edgesInfo.value.indexMax), &payload.indexMax);
    }

    payload.payloadId = findSpec.payloadId;
    payload.firstOperator = findSpec.firstOperator;
    payload.secondOperator = findSpec.secondOperator;

    // Serialize.
    {
        bson_t out = BSON_INITIALIZER;
        mc_FLE2FindRangePayloadV2_serialize(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }
    _mongocrypt_buffer_steal(&ciphertext->key_id, &placeholder->index_key_id);

    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2FindRangePayloadV2.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2FindRangePayloadV2;

    res = true;
fail:
    mc_mincover_destroy(mincover);
    mc_FLE2FindRangePayloadV2_cleanup(&payload);
    _mongocrypt_buffer_cleanup(&tokenKey);

    return res;
}

static bool _mongocrypt_fle2_placeholder_to_find_ciphertextForTextSearch(_mongocrypt_key_broker_t *kb,
                                                                         _mongocrypt_marking_t *marking,
                                                                         _mongocrypt_ciphertext_t *ciphertext,
                                                                         mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT(kb->crypt);
    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);

    bool res = false;
    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    BSON_ASSERT(placeholder->type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND);
    BSON_ASSERT(placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH);

    mc_FLE2FindTextPayload_t payload;
    mc_FLE2FindTextPayload_init(&payload);

    mc_FLE2TextSearchInsertSpec_t spec;
    if (!mc_FLE2TextSearchInsertSpec_parse(&spec, &placeholder->v_iter, status)) {
        goto fail;
    }

    if (!_fle2_generate_TextSearchFindTokenSets(kb, &payload.tokenSets, &placeholder->index_key_id, &spec, status)) {
        goto fail;
    }

    payload.caseFold = spec.casef;
    payload.diacriticFold = spec.diacf;
    payload.maxContentionFactor = placeholder->maxContentionFactor;
    if (spec.substr.set) {
        payload.substringSpec.set = true;
        payload.substringSpec.value = spec.substr.value;
    } else if (spec.suffix.set) {
        payload.suffixSpec.set = true;
        payload.suffixSpec.value = spec.suffix.value;
    } else if (spec.prefix.set) {
        payload.prefixSpec.set = true;
        payload.prefixSpec.value = spec.prefix.value;
    }

    // Serialize.
    {
        bson_t out = BSON_INITIALIZER;
        mc_FLE2FindTextPayload_serialize(&payload, &out);
        _mongocrypt_buffer_steal_from_bson(&ciphertext->data, &out);
    }

    // Do not set ciphertext->original_bson_type and ciphertext->key_id. They are
    // not used for FLE2FindTextPayload.
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2FindTextPayload;

    res = true;

fail:
    mc_FLE2FindTextPayload_cleanup(&payload);
    return res;
}

static bool _mongocrypt_fle2_placeholder_to_FLE2UnindexedEncryptedValue(_mongocrypt_key_broker_t *kb,
                                                                        _mongocrypt_marking_t *marking,
                                                                        _mongocrypt_ciphertext_t *ciphertext,
                                                                        mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);

    _mongocrypt_buffer_t plaintext = {0};
    mc_FLE2EncryptionPlaceholder_t *placeholder = &marking->u.fle2;
    _mongocrypt_buffer_t user_key = {0};
    bool res = false;

    BSON_ASSERT(marking->type == MONGOCRYPT_MARKING_FLE2_ENCRYPTION);
    BSON_ASSERT(placeholder);
    BSON_ASSERT(placeholder->algorithm == MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED);
    _mongocrypt_buffer_from_iter(&plaintext, &placeholder->v_iter);

    if (!_mongocrypt_key_broker_decrypted_key_by_id(kb, &placeholder->user_key_id, &user_key)) {
        CLIENT_ERR("unable to retrieve key");
        goto fail;
    }

    BSON_ASSERT(kb->crypt);
    res = mc_FLE2UnindexedEncryptedValueV2_encrypt(kb->crypt->crypto,
                                                   &placeholder->user_key_id,
                                                   bson_iter_type(&placeholder->v_iter),
                                                   &plaintext,
                                                   &user_key,
                                                   &ciphertext->data,
                                                   status);
    ciphertext->blob_subtype = MC_SUBTYPE_FLE2UnindexedEncryptedValueV2;

    if (!res) {
        goto fail;
    }

    _mongocrypt_buffer_steal(&ciphertext->key_id, &placeholder->user_key_id);
    ciphertext->original_bson_type = (uint8_t)bson_iter_type(&placeholder->v_iter);

    res = true;
fail:
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&user_key);

    return res;
}

static bool _mongocrypt_fle1_marking_to_ciphertext(_mongocrypt_key_broker_t *kb,
                                                   _mongocrypt_marking_t *marking,
                                                   _mongocrypt_ciphertext_t *ciphertext,
                                                   mongocrypt_status_t *status) {
    const _mongocrypt_value_encryption_algorithm_t *fle1 = _mcFLE1Algorithm();
    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t iv;
    _mongocrypt_buffer_t associated_data;
    _mongocrypt_buffer_t key_material;
    _mongocrypt_buffer_t key_id;
    bool ret = false;
    bool key_found;
    uint32_t bytes_written;

    BSON_ASSERT_PARAM(kb);
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);

    BSON_ASSERT((marking->type == MONGOCRYPT_MARKING_FLE1_BY_ID)
                || (marking->type == MONGOCRYPT_MARKING_FLE1_BY_ALTNAME));

    _mongocrypt_buffer_init(&plaintext);
    _mongocrypt_buffer_init(&associated_data);
    _mongocrypt_buffer_init(&iv);
    _mongocrypt_buffer_init(&key_id);
    _mongocrypt_buffer_init(&key_material);

    /* Get the decrypted key for this marking.u.fle1. */
    if (marking->type == MONGOCRYPT_MARKING_FLE1_BY_ALTNAME) {
        key_found =
            _mongocrypt_key_broker_decrypted_key_by_name(kb, &marking->u.fle1.key_alt_name, &key_material, &key_id);
    } else if (!_mongocrypt_buffer_empty(&marking->u.fle1.key_id)) {
        key_found = _mongocrypt_key_broker_decrypted_key_by_id(kb, &marking->u.fle1.key_id, &key_material);
        _mongocrypt_buffer_copy_to(&marking->u.fle1.key_id, &key_id);
    } else {
        CLIENT_ERR("marking must have either key_id or key_alt_name");
        goto fail;
    }

    if (!key_found) {
        _mongocrypt_status_copy_to(kb->status, status);
        goto fail;
    }

    ciphertext->original_bson_type = (uint8_t)bson_iter_type(&marking->u.fle1.v_iter);
    if (marking->u.fle1.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC) {
        ciphertext->blob_subtype = MC_SUBTYPE_FLE1DeterministicEncryptedValue;
    } else {
        BSON_ASSERT(marking->u.fle1.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM);
        ciphertext->blob_subtype = MC_SUBTYPE_FLE1RandomEncryptedValue;
    }
    _mongocrypt_buffer_copy_to(&key_id, &ciphertext->key_id);
    if (!_mongocrypt_ciphertext_serialize_associated_data(ciphertext, &associated_data)) {
        CLIENT_ERR("could not serialize associated data");
        goto fail;
    }

    _mongocrypt_buffer_from_iter(&plaintext, &marking->u.fle1.v_iter);
    ciphertext->data.len = fle1->get_ciphertext_len(plaintext.len, status);
    if (ciphertext->data.len == 0) {
        goto fail;
    }
    ciphertext->data.data = bson_malloc(ciphertext->data.len);
    BSON_ASSERT(ciphertext->data.data);

    ciphertext->data.owned = true;

    BSON_ASSERT(kb->crypt);
    switch (marking->u.fle1.algorithm) {
    case MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC:
        /* Use deterministic encryption. */
        _mongocrypt_buffer_resize(&iv, MONGOCRYPT_IV_LEN);
        ret = _mongocrypt_calculate_deterministic_iv(kb->crypt->crypto,
                                                     &key_material,
                                                     &plaintext,
                                                     &associated_data,
                                                     &iv,
                                                     status);
        if (!ret) {
            goto fail;
        }

        ret = fle1->do_encrypt(kb->crypt->crypto,
                               &iv,
                               &associated_data,
                               &key_material,
                               &plaintext,
                               &ciphertext->data,
                               &bytes_written,
                               status);
        break;
    case MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM:
        /* Use randomized encryption.
         * In this case, we must generate a new, random iv. */
        _mongocrypt_buffer_resize(&iv, MONGOCRYPT_IV_LEN);
        if (!_mongocrypt_random(kb->crypt->crypto, &iv, MONGOCRYPT_IV_LEN, status)) {
            goto fail;
        }
        ret = fle1->do_encrypt(kb->crypt->crypto,
                               &iv,
                               &associated_data,
                               &key_material,
                               &plaintext,
                               &ciphertext->data,
                               &bytes_written,
                               status);
        break;
    case MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE:
    default:
        /* Error. */
        CLIENT_ERR("Unsupported value for encryption algorithm");
        goto fail;
    }

    if (!ret) {
        goto fail;
    }

    BSON_ASSERT(bytes_written == ciphertext->data.len);

    ret = true;

fail:
    _mongocrypt_buffer_cleanup(&iv);
    _mongocrypt_buffer_cleanup(&key_id);
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&associated_data);
    _mongocrypt_buffer_cleanup(&key_material);
    return ret;
}

bool _mongocrypt_marking_to_ciphertext(void *ctx,
                                       _mongocrypt_marking_t *marking,
                                       _mongocrypt_ciphertext_t *ciphertext,
                                       mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(marking);
    BSON_ASSERT_PARAM(ciphertext);
    BSON_ASSERT_PARAM(ctx);

    _mongocrypt_key_broker_t *kb = (_mongocrypt_key_broker_t *)ctx;

    switch (marking->type) {
    case MONGOCRYPT_MARKING_FLE2_ENCRYPTION:
        switch (marking->u.fle2.algorithm) {
        case MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED:
            return _mongocrypt_fle2_placeholder_to_FLE2UnindexedEncryptedValue(kb, marking, ciphertext, status);
        case MONGOCRYPT_FLE2_ALGORITHM_RANGE:
            switch (marking->u.fle2.type) {
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT:
                return _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForRange(kb,
                                                                                        marking,
                                                                                        ciphertext,
                                                                                        status);
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND:
                return _mongocrypt_fle2_placeholder_to_find_ciphertextForRange(kb, marking, ciphertext, status);
            default: CLIENT_ERR("unexpected fle2 type: %d", (int)marking->u.fle2.type); return false;
            }
        case MONGOCRYPT_FLE2_ALGORITHM_EQUALITY:
            switch (marking->u.fle2.type) {
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT:
                return _mongocrypt_fle2_placeholder_to_insert_update_ciphertext(kb, marking, ciphertext, status);
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND:
                return _mongocrypt_fle2_placeholder_to_find_ciphertext(kb, marking, ciphertext, status);
            default: CLIENT_ERR("unexpected fle2 type: %d", (int)marking->u.fle2.type); return false;
            }
        case MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH:
            switch (marking->u.fle2.type) {
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT:
                return _mongocrypt_fle2_placeholder_to_insert_update_ciphertextForTextSearch(kb,
                                                                                             marking,
                                                                                             ciphertext,
                                                                                             status);
            case MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND:
                return _mongocrypt_fle2_placeholder_to_find_ciphertextForTextSearch(kb, marking, ciphertext, status);
            default: CLIENT_ERR("unexpected fle2 type: %d", (int)marking->u.fle2.type); return false;
            }
        default: CLIENT_ERR("unexpected algorithm: %d", (int)marking->u.fle1.algorithm); return false;
        }
    case MONGOCRYPT_MARKING_FLE1_BY_ID:
    case MONGOCRYPT_MARKING_FLE1_BY_ALTNAME:
        return _mongocrypt_fle1_marking_to_ciphertext(kb, marking, ciphertext, status);
    default: CLIENT_ERR("unexpected marking type: %d", (int)marking->type); return false;
    }
}
libmongocrypt-1.19.0/src/mongocrypt-mutex-private.h000066400000000000000000000025741521103432300224450ustar00rootroot00000000000000/*
 * Copyright 2018-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.
 */

#ifndef MONGOCRYPT_MUTEX_PRIVATE_H
#define MONGOCRYPT_MUTEX_PRIVATE_H

#include 

#if defined(BSON_OS_UNIX)
#include 
#define mongocrypt_mutex_t pthread_mutex_t
#else
#define mongocrypt_mutex_t CRITICAL_SECTION
#endif

void _mongocrypt_mutex_init(mongocrypt_mutex_t *mutex);

void _mongocrypt_mutex_cleanup(mongocrypt_mutex_t *mutex);

void _mongocrypt_mutex_lock(mongocrypt_mutex_t *mutex);

void _mongocrypt_mutex_unlock(mongocrypt_mutex_t *mutex);

#define MONGOCRYPT_WITH_MUTEX(Mutex)                                                                                   \
    for (int only_once = (_mongocrypt_mutex_lock(&(Mutex)), 1); only_once; _mongocrypt_mutex_unlock(&(Mutex)))         \
        for (; only_once; only_once = 0)

#endif /* MONGOCRYPT_MUTEX_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-opts-private.h000066400000000000000000000240541521103432300222650ustar00rootroot00000000000000/*
 * Copyright 2018-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.
 */

#ifndef MONGOCRYPT_OPTS_PRIVATE_H
#define MONGOCRYPT_OPTS_PRIVATE_H

#include 

#include "mc-mlib/str.h"

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-endpoint-private.h"
#include "mongocrypt-kek-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt.h"
#include 

typedef struct {
    char *tenant_id;
    char *client_id;
    char *client_secret;
    _mongocrypt_endpoint_t *identity_platform_endpoint;
    char *access_token;
} _mongocrypt_opts_kms_provider_azure_t;

typedef struct {
    char *email;
    _mongocrypt_buffer_t private_key;
    _mongocrypt_endpoint_t *endpoint;
    char *access_token;
} _mongocrypt_opts_kms_provider_gcp_t;

typedef struct {
    char *secret_access_key;
    char *access_key_id;
    char *session_token;
} _mongocrypt_opts_kms_provider_aws_t;

typedef struct {
    _mongocrypt_buffer_t key;
} _mongocrypt_opts_kms_provider_local_t;

typedef struct {
    _mongocrypt_endpoint_t *endpoint;
} _mongocrypt_opts_kms_provider_kmip_t;

typedef struct {
    // `type` identifies the set field in `value`.
    _mongocrypt_kms_provider_t type;

    union {
        _mongocrypt_opts_kms_provider_local_t local;
        _mongocrypt_opts_kms_provider_aws_t aws;
        _mongocrypt_opts_kms_provider_azure_t azure;
        _mongocrypt_opts_kms_provider_gcp_t gcp;
        _mongocrypt_opts_kms_provider_kmip_t kmip;
    } value;
} mc_kms_creds_t;

typedef struct {
    int configured_providers; /* A bit set of _mongocrypt_kms_provider_t */
    int need_credentials;     /* A bit set of _mongocrypt_kms_provider_t */
    // Fields suffixed with `_mut` are mutated when constructing the `_mongocrypt_opts_kms_providers_t`.
    // Prefer using `_mongocrypt_opts_kms_providers_lookup` to read the values.
    _mongocrypt_opts_kms_provider_local_t local_mut;
    _mongocrypt_opts_kms_provider_aws_t aws_mut;
    _mongocrypt_opts_kms_provider_azure_t azure_mut;
    _mongocrypt_opts_kms_provider_gcp_t gcp_mut;
    _mongocrypt_opts_kms_provider_kmip_t kmip_mut;
    // `named_mut` stores a list of named KMS providers.
    mc_array_t named_mut;
} _mongocrypt_opts_kms_providers_t;

typedef bool (*_mongocrypt_contention_factor_fn)(int64_t exclusive_upper_bound, int64_t *out);

void _mongocrypt_opts_kms_providers_init(_mongocrypt_opts_kms_providers_t *kms_providers);

bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
                                     _mongocrypt_opts_kms_providers_t *kms_providers,
                                     mongocrypt_status_t *status,
                                     _mongocrypt_log_t *log);

bool _mongocrypt_opts_kms_providers_lookup(const _mongocrypt_opts_kms_providers_t *kms_providers,
                                           const char *kmsid,
                                           mc_kms_creds_t *out);

typedef struct {
    mongocrypt_log_fn_t log_fn;
    void *log_ctx;
    _mongocrypt_buffer_t schema_map;
    _mongocrypt_buffer_t encrypted_field_config_map;

    _mongocrypt_opts_kms_providers_t kms_providers;
    mongocrypt_hmac_fn sign_rsaes_pkcs1_v1_5;
    void *sign_ctx;

    // Support overriding random contentionFactor (when null, default) with a custom setter.
    _mongocrypt_contention_factor_fn contention_factor_fn;

    /// Keep an array of search paths for finding the crypt_shared library
    /// during mongocrypt_init()
    int n_crypt_shared_lib_search_paths;
    mstr *crypt_shared_lib_search_paths;
    /// Optionally, a user may override the default search behavior by specifying
    /// a specifiy path to the library. If this is set, this suppresses the
    /// search behavior.
    mstr crypt_shared_lib_override_path;

    bool use_need_kms_credentials_state;
    bool use_need_mongo_collinfo_with_db_state;
    bool bypass_query_analysis;

} _mongocrypt_opts_t;

void _mongocrypt_opts_kms_providers_cleanup(_mongocrypt_opts_kms_providers_t *kms_providers);

/* Merge `source` into `dest`. Does not perform any memory ownership management;
 * values in `dest` will be overwritten with values from `source` without
 * being released. */
void _mongocrypt_opts_merge_kms_providers(_mongocrypt_opts_kms_providers_t *dest,
                                          const _mongocrypt_opts_kms_providers_t *source);

void _mongocrypt_opts_init(_mongocrypt_opts_t *opts);

void _mongocrypt_opts_cleanup(_mongocrypt_opts_t *opts);

bool _mongocrypt_opts_validate(_mongocrypt_opts_t *opts, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_opts_kms_providers_validate(_mongocrypt_opts_t *opts,
                                             _mongocrypt_opts_kms_providers_t *kms_providers,
                                             mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

// For testing only: register a custom contention factor function with crypt.
void _mongocrypt_opts_set_contention_factor_fn(mongocrypt_t *crypt,
                                               _mongocrypt_contention_factor_fn contention_factor_fn);

/*
 * Parse an optional UTF-8 value from BSON.
 * @dotkey may be a dot separated key like: "a.b.c".
 * @*out is set to a copy of the string if found, NULL otherwise. Caller must
 * clean up with bson_free (*out).
 * Returns true if no error occured.
 */
bool _mongocrypt_parse_optional_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status);

/*
 * Parse an optional boolean value from BSON.
 * @dotkey may be a dot separated key like: "a.b.c".
 * @*out is set to a copy of the value if found, false otherwise.
 * Returns true if no error occured.
 */
bool _mongocrypt_parse_optional_bool(const bson_t *bson, const char *dotkey, bool *out, mongocrypt_status_t *status);

/*
 * Parse a required UTF-8 value from BSON.
 * @dotkey may be a dot separated key like: "a.b.c".
 * @*out is set to a copy of the string if found, NULL otherwise. Caller must
 * clean up with bson_free (*out).
 * Returns true if no error occured.
 */
bool _mongocrypt_parse_required_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status);

/*
 * Parse an optional endpoint UTF-8 from BSON.
 * @dotkey may be a dot separated key like: "a.b.c".
 * @*out is set to a new _mongocrypt_endpoint_t of the if found, NULL otherwise.
 * @*opts may be set to configure endpoint parsing. It is optional and may be
 * NULL. Caller must clean up with _mongocrypt_endpoint_destroy (*out). Returns
 * true if no error occured.
 */
bool _mongocrypt_parse_optional_endpoint(const bson_t *bson,
                                         const char *dotkey,
                                         _mongocrypt_endpoint_t **out,
                                         _mongocrypt_endpoint_parse_opts_t *opts,
                                         mongocrypt_status_t *status);

/*
 * Parse a required endpoint UTF-8 from BSON.
 * @dotkey may be a dot separated key like: "a.b.c".
 * @*out is set to a new _mongocrypt_endpoint_t of the if found, NULL otherwise.
 * @*opts may be set to configure endpoint parsing. It is optional and may be
 * NULL. Caller must clean up with _mongocrypt_endpoint_destroy (*out). Returns
 * true if no error occured.
 */
bool _mongocrypt_parse_required_endpoint(const bson_t *bson,
                                         const char *dotkey,
                                         _mongocrypt_endpoint_t **out,
                                         _mongocrypt_endpoint_parse_opts_t *opts,
                                         mongocrypt_status_t *status);

/*
 * Parse an optional binary type from BSON.
 * The field parsed is accepted as:
 * - A BSON binary value (of any subtype).
 * - A BSON UTF-8 value, set to base64 encoded data.
 *
 * @dotkey may be a dot separated key like: "a.b.c"
 * @out is initialized with the parsed data, or initialized to empty on error.
 * Caller must clean up with _mongocrypt_buffer_cleanup (out).
 * Returns true if no error occurred.
 */
bool _mongocrypt_parse_optional_binary(const bson_t *bson,
                                       const char *dotkey,
                                       _mongocrypt_buffer_t *out,
                                       mongocrypt_status_t *status);

/*
 * Parse a required binary type from BSON.
 * The field parsed is accepted as:
 * - A BSON binary value (of any subtype).
 * - A BSON UTF-8 value, set to base64 encoded data.
 *
 * @dotkey may be a dot separated key like: "a.b.c"
 * @out is initialized with the parsed data, or initialized to empty on error.
 * Caller must clean up with _mongocrypt_buffer_cleanup (out).
 * Returns true if no error occurred.
 */
bool _mongocrypt_parse_required_binary(const bson_t *bson,
                                       const char *dotkey,
                                       _mongocrypt_buffer_t *out,
                                       mongocrypt_status_t *status);

/*
 * Checks for unrecognized fields in parsing @bson.
 * @dotkey is a dot separated path to a document field, like "a.b.c" or NULL.
 * Pass a list of allowed fields.
 * Returns true if no error occurred.
 */
bool _mongocrypt_check_allowed_fields_va(const bson_t *bson, const char *dotkey, mongocrypt_status_t *status, ...);

#define _mongocrypt_check_allowed_fields(bson, path, status, ...)                                                      \
    _mongocrypt_check_allowed_fields_va(bson, path, status, __VA_ARGS__, NULL)

bool mc_kmsid_parse(const char *kmsid,
                    _mongocrypt_kms_provider_t *type_out,
                    const char **name_out,
                    mongocrypt_status_t *status);
#endif /* MONGOCRYPT_OPTS_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-opts.c000066400000000000000000001120111521103432300205770ustar00rootroot00000000000000/*
 * Copyright 2018-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 

#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
#include  // mc_iter_document_as_bson

#include 

typedef struct {
    mc_kms_creds_t creds;
    char *kmsid;
} mc_kms_creds_with_id_t;

void _mongocrypt_opts_kms_providers_init(_mongocrypt_opts_kms_providers_t *kms_providers) {
    _mc_array_init(&kms_providers->named_mut, sizeof(mc_kms_creds_with_id_t));
}

void _mongocrypt_opts_init(_mongocrypt_opts_t *opts) {
    BSON_ASSERT_PARAM(opts);
    memset(opts, 0, sizeof(*opts));
    _mongocrypt_opts_kms_providers_init(&opts->kms_providers);
}

static void _mongocrypt_opts_kms_provider_azure_cleanup(_mongocrypt_opts_kms_provider_azure_t *kms_provider_azure) {
    if (!kms_provider_azure) {
        return;
    }
    bson_free(kms_provider_azure->client_id);
    bson_free(kms_provider_azure->client_secret);
    bson_free(kms_provider_azure->tenant_id);
    bson_free(kms_provider_azure->access_token);
    _mongocrypt_endpoint_destroy(kms_provider_azure->identity_platform_endpoint);
}

static void _mongocrypt_opts_kms_provider_gcp_cleanup(_mongocrypt_opts_kms_provider_gcp_t *kms_provider_gcp) {
    if (!kms_provider_gcp) {
        return;
    }
    bson_free(kms_provider_gcp->email);
    _mongocrypt_endpoint_destroy(kms_provider_gcp->endpoint);
    _mongocrypt_buffer_cleanup(&kms_provider_gcp->private_key);
    bson_free(kms_provider_gcp->access_token);
}

static void _mongocrypt_opts_kms_provider_local_cleanup(_mongocrypt_opts_kms_provider_local_t *kms_provider_local) {
    _mongocrypt_buffer_cleanup(&kms_provider_local->key);
}

static void _mongocrypt_opts_kms_provider_aws_cleanup(_mongocrypt_opts_kms_provider_aws_t *kms_provider_aws) {
    bson_free(kms_provider_aws->secret_access_key);
    bson_free(kms_provider_aws->access_key_id);
    bson_free(kms_provider_aws->session_token);
}

static void _mongocrypt_opts_kms_provider_kmip_cleanup(_mongocrypt_opts_kms_provider_kmip_t *kms_provider_kmip) {
    _mongocrypt_endpoint_destroy(kms_provider_kmip->endpoint);
}

void _mongocrypt_opts_kms_providers_cleanup(_mongocrypt_opts_kms_providers_t *kms_providers) {
    if (!kms_providers) {
        return;
    }
    _mongocrypt_opts_kms_provider_aws_cleanup(&kms_providers->aws_mut);
    _mongocrypt_opts_kms_provider_local_cleanup(&kms_providers->local_mut);
    _mongocrypt_opts_kms_provider_azure_cleanup(&kms_providers->azure_mut);
    _mongocrypt_opts_kms_provider_gcp_cleanup(&kms_providers->gcp_mut);
    _mongocrypt_opts_kms_provider_kmip_cleanup(&kms_providers->kmip_mut);
    for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
        mc_kms_creds_with_id_t kcwid = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
        switch (kcwid.creds.type) {
        default:
        case MONGOCRYPT_KMS_PROVIDER_NONE: break;
        case MONGOCRYPT_KMS_PROVIDER_AWS: {
            _mongocrypt_opts_kms_provider_aws_cleanup(&kcwid.creds.value.aws);
            break;
        }
        case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
            _mongocrypt_opts_kms_provider_local_cleanup(&kcwid.creds.value.local);
            break;
        }
        case MONGOCRYPT_KMS_PROVIDER_AZURE: {
            _mongocrypt_opts_kms_provider_azure_cleanup(&kcwid.creds.value.azure);
            break;
        }
        case MONGOCRYPT_KMS_PROVIDER_GCP: {
            _mongocrypt_opts_kms_provider_gcp_cleanup(&kcwid.creds.value.gcp);
            break;
        }
        case MONGOCRYPT_KMS_PROVIDER_KMIP: {
            _mongocrypt_endpoint_destroy(kcwid.creds.value.kmip.endpoint);
            break;
        }
        }
        bson_free(kcwid.kmsid);
    }
    _mc_array_destroy(&kms_providers->named_mut);
}

void _mongocrypt_opts_merge_kms_providers(_mongocrypt_opts_kms_providers_t *dest,
                                          const _mongocrypt_opts_kms_providers_t *source) {
    BSON_ASSERT_PARAM(dest);
    BSON_ASSERT_PARAM(source);

    if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) {
        memcpy(&dest->aws_mut, &source->aws_mut, sizeof(source->aws_mut));
        dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
    }
    if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) {
        memcpy(&dest->local_mut, &source->local_mut, sizeof(source->local_mut));
        dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
    }
    if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE) {
        memcpy(&dest->azure_mut, &source->azure_mut, sizeof(source->azure_mut));
        dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
    }
    if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP) {
        memcpy(&dest->gcp_mut, &source->gcp_mut, sizeof(source->gcp_mut));
        dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
    }
    if (source->configured_providers & MONGOCRYPT_KMS_PROVIDER_KMIP) {
        memcpy(&dest->kmip_mut, &source->kmip_mut, sizeof(source->kmip_mut));
        dest->configured_providers |= MONGOCRYPT_KMS_PROVIDER_KMIP;
    }
    /* ensure all providers were copied */
    BSON_ASSERT(!(source->configured_providers & ~dest->configured_providers));
}

void _mongocrypt_opts_cleanup(_mongocrypt_opts_t *opts) {
    if (!opts) {
        return;
    }
    _mongocrypt_opts_kms_providers_cleanup(&opts->kms_providers);
    _mongocrypt_buffer_cleanup(&opts->schema_map);
    _mongocrypt_buffer_cleanup(&opts->encrypted_field_config_map);
    // Free any lib search paths added by the caller
    for (int i = 0; i < opts->n_crypt_shared_lib_search_paths; ++i) {
        mstr_free(opts->crypt_shared_lib_search_paths[i]);
    }
    bson_free(opts->crypt_shared_lib_search_paths);
    mstr_free(opts->crypt_shared_lib_override_path);
}

bool _mongocrypt_opts_kms_providers_validate(_mongocrypt_opts_t *opts,
                                             _mongocrypt_opts_kms_providers_t *kms_providers,
                                             mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(opts);
    BSON_ASSERT_PARAM(kms_providers);

    if (!kms_providers->configured_providers && !kms_providers->need_credentials && kms_providers->named_mut.len == 0) {
        CLIENT_ERR("no kms provider set");
        return false;
    }

    if (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) {
        if (!kms_providers->aws_mut.access_key_id || !kms_providers->aws_mut.secret_access_key) {
            CLIENT_ERR("aws credentials unset");
            return false;
        }
    }

    if (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) {
        if (_mongocrypt_buffer_empty(&kms_providers->local_mut.key)) {
            CLIENT_ERR("local data key unset");
            return false;
        }
    }

    if (kms_providers->need_credentials && !opts->use_need_kms_credentials_state) {
        CLIENT_ERR("on-demand credentials not enabled");
        return false;
    }

    return true;
}

/* _shares_bson_fields checks if @one or @two share any top-level field names.
 * Returns false on error and sets @status. Returns true if no error
 * occurred. Sets @found to the first shared field name found.
 * If no shared field names are found, @found is set to NULL.
 */
static bool _shares_bson_fields(bson_t *one, bson_t *two, const char **found, mongocrypt_status_t *status) {
    bson_iter_t iter1;
    bson_iter_t iter2;

    BSON_ASSERT_PARAM(one);
    BSON_ASSERT_PARAM(two);
    BSON_ASSERT_PARAM(found);
    *found = NULL;
    if (!bson_iter_init(&iter1, one)) {
        CLIENT_ERR("error iterating one BSON in _shares_bson_fields");
        return false;
    }
    while (bson_iter_next(&iter1)) {
        const char *key1 = bson_iter_key(&iter1);

        if (!bson_iter_init(&iter2, two)) {
            CLIENT_ERR("error iterating two BSON in _shares_bson_fields");
            return false;
        }
        while (bson_iter_next(&iter2)) {
            const char *key2 = bson_iter_key(&iter2);
            if (0 == strcmp(key1, key2)) {
                *found = key1;
                return true;
            }
        }
    }
    return true;
}

/* _validate_encrypted_field_config_map_and_schema_map validates that the same
 * namespace is not both in encrypted_field_config_map and schema_map. */
static bool _validate_encrypted_field_config_map_and_schema_map(_mongocrypt_buffer_t *encrypted_field_config_map,
                                                                _mongocrypt_buffer_t *schema_map,
                                                                mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(encrypted_field_config_map);
    BSON_ASSERT_PARAM(schema_map);

    const char *found;
    bson_t schema_map_bson;
    bson_t encrypted_field_config_map_bson;

    /* If either map is unset, there is nothing to validate. Return true to
     * signal no error. */
    if (_mongocrypt_buffer_empty(encrypted_field_config_map)) {
        return true;
    }
    if (_mongocrypt_buffer_empty(schema_map)) {
        return true;
    }

    if (!_mongocrypt_buffer_to_bson(schema_map, &schema_map_bson)) {
        CLIENT_ERR("error converting schema_map to BSON");
        return false;
    }
    if (!_mongocrypt_buffer_to_bson(encrypted_field_config_map, &encrypted_field_config_map_bson)) {
        CLIENT_ERR("error converting encrypted_field_config_map to BSON");
        return false;
    }
    if (!_shares_bson_fields(&schema_map_bson, &encrypted_field_config_map_bson, &found, status)) {
        return false;
    }
    if (found != NULL) {
        CLIENT_ERR("%s is present in both schema_map and encrypted_field_config_map", found);
        return false;
    }
    return true;
}

bool _mongocrypt_opts_validate(_mongocrypt_opts_t *opts, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(opts);

    if (!_validate_encrypted_field_config_map_and_schema_map(&opts->encrypted_field_config_map,
                                                             &opts->schema_map,
                                                             status)) {
        return false;
    }
    return _mongocrypt_opts_kms_providers_validate(opts, &opts->kms_providers, status);
}

bool _mongocrypt_opts_kms_providers_lookup(const _mongocrypt_opts_kms_providers_t *kms_providers,
                                           const char *kmsid,
                                           mc_kms_creds_t *out) {
    *out = (mc_kms_creds_t){0};
    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS) && 0 == strcmp(kmsid, "aws")) {
        out->type = MONGOCRYPT_KMS_PROVIDER_AWS;
        out->value.aws = kms_providers->aws_mut;
        return true;
    }
    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE) && 0 == strcmp(kmsid, "azure")) {
        out->type = MONGOCRYPT_KMS_PROVIDER_AZURE;
        out->value.azure = kms_providers->azure_mut;
        return true;
    }

    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP) && 0 == strcmp(kmsid, "gcp")) {
        out->type = MONGOCRYPT_KMS_PROVIDER_GCP;
        out->value.gcp = kms_providers->gcp_mut;
        return true;
    }

    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL) && 0 == strcmp(kmsid, "local")) {
        out->type = MONGOCRYPT_KMS_PROVIDER_LOCAL;
        out->value.local = kms_providers->local_mut;
        return true;
    }

    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_KMIP) && 0 == strcmp(kmsid, "kmip")) {
        out->type = MONGOCRYPT_KMS_PROVIDER_KMIP;
        out->value.kmip = kms_providers->kmip_mut;
        return true;
    }

    // Check for KMS providers with a name.
    for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
        mc_kms_creds_with_id_t kcwi = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
        if (0 == strcmp(kmsid, kcwi.kmsid)) {
            *out = kcwi.creds;
            return true;
        }
    }

    return false;
}

void _mongocrypt_opts_set_contention_factor_fn(mongocrypt_t *crypt,
                                               _mongocrypt_contention_factor_fn contention_factor_fn) {
    BSON_ASSERT_PARAM(crypt);
    crypt->opts.contention_factor_fn = contention_factor_fn;
}

bool _mongocrypt_parse_optional_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status) {
    bson_iter_t iter;
    bson_iter_t child;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    *out = NULL;

    if (!bson_iter_init(&iter, bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }
    if (!bson_iter_find_descendant(&iter, dotkey, &child)) {
        /* Not found. Not an error. */
        return true;
    }
    if (!BSON_ITER_HOLDS_UTF8(&child)) {
        CLIENT_ERR("expected UTF-8 %s", dotkey);
        return false;
    }

    *out = bson_strdup(bson_iter_utf8(&child, NULL));
    return true;
}

bool _mongocrypt_parse_optional_bool(const bson_t *bson, const char *dotkey, bool *out, mongocrypt_status_t *status) {
    bson_iter_t iter;
    bson_iter_t child;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    *out = false;

    if (!bson_iter_init(&iter, bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }
    if (!bson_iter_find_descendant(&iter, dotkey, &child)) {
        /* Not found. Not an error. */
        return true;
    }
    if (!BSON_ITER_HOLDS_BOOL(&child)) {
        CLIENT_ERR("expected bool %s", dotkey);
        return false;
    }

    *out = bson_iter_bool(&child);
    return true;
}

bool _mongocrypt_parse_required_utf8(const bson_t *bson, const char *dotkey, char **out, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    if (!_mongocrypt_parse_optional_utf8(bson, dotkey, out, status)) {
        return false;
    }

    if (!*out) {
        CLIENT_ERR("expected UTF-8 %s", dotkey);
        return false;
    }

    return true;
}

bool _mongocrypt_parse_optional_endpoint(const bson_t *bson,
                                         const char *dotkey,
                                         _mongocrypt_endpoint_t **out,
                                         _mongocrypt_endpoint_parse_opts_t *opts,
                                         mongocrypt_status_t *status) {
    char *endpoint_raw;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    *out = NULL;

    if (!_mongocrypt_parse_optional_utf8(bson, dotkey, &endpoint_raw, status)) {
        return false;
    }

    /* Not found. Not an error. */
    if (!endpoint_raw) {
        return true;
    }

    *out = _mongocrypt_endpoint_new(endpoint_raw, -1, opts, status);
    bson_free(endpoint_raw);
    return (*out) != NULL;
}

bool _mongocrypt_parse_required_endpoint(const bson_t *bson,
                                         const char *dotkey,
                                         _mongocrypt_endpoint_t **out,
                                         _mongocrypt_endpoint_parse_opts_t *opts,
                                         mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    if (!_mongocrypt_parse_optional_endpoint(bson, dotkey, out, opts, status)) {
        return false;
    }

    if (!*out) {
        CLIENT_ERR("expected endpoint %s", dotkey);
        return false;
    }

    return true;
}

bool _mongocrypt_parse_optional_binary(const bson_t *bson,
                                       const char *dotkey,
                                       _mongocrypt_buffer_t *out,
                                       mongocrypt_status_t *status) {
    bson_iter_t iter;
    bson_iter_t child;

    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    _mongocrypt_buffer_init(out);

    if (!bson_iter_init(&iter, bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }
    if (!bson_iter_find_descendant(&iter, dotkey, &child)) {
        /* Not found. Not an error. */
        return true;
    }
    if (BSON_ITER_HOLDS_UTF8(&child)) {
        size_t out_len;
        /* Attempt to base64 decode. */
        out->data = kms_message_b64_to_raw(bson_iter_utf8(&child, NULL), &out_len);
        if (!out->data) {
            CLIENT_ERR("unable to parse base64 from UTF-8 field %s", dotkey);
            return false;
        }
        BSON_ASSERT(out_len <= UINT32_MAX);
        out->len = (uint32_t)out_len;
        out->owned = true;
    } else if (BSON_ITER_HOLDS_BINARY(&child)) {
        if (!_mongocrypt_buffer_copy_from_binary_iter(out, &child)) {
            CLIENT_ERR("unable to parse binary from field %s", dotkey);
            return false;
        }
    } else {
        CLIENT_ERR("expected UTF-8 or binary %s", dotkey);
        return false;
    }

    return true;
}

bool _mongocrypt_parse_required_binary(const bson_t *bson,
                                       const char *dotkey,
                                       _mongocrypt_buffer_t *out,
                                       mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT_PARAM(dotkey);
    BSON_ASSERT_PARAM(out);

    if (!_mongocrypt_parse_optional_binary(bson, dotkey, out, status)) {
        return false;
    }

    if (out->len == 0) {
        CLIENT_ERR("expected UTF-8 or binary %s", dotkey);
        return false;
    }

    return true;
}

bool _mongocrypt_check_allowed_fields_va(const bson_t *bson, const char *dotkey, mongocrypt_status_t *status, ...) {
    va_list args;
    const char *field;
    bson_iter_t iter;

    BSON_ASSERT_PARAM(bson);

    if (dotkey) {
        bson_iter_t parent;

        bson_iter_init(&parent, bson);
        if (!bson_iter_find_descendant(&parent, dotkey, &iter) || !BSON_ITER_HOLDS_DOCUMENT(&iter)) {
            CLIENT_ERR("invalid BSON, expected %s", dotkey);
            return false;
        }
        bson_iter_recurse(&iter, &iter);
    } else {
        bson_iter_init(&iter, bson);
    }

    while (bson_iter_next(&iter)) {
        bool found = false;

        va_start(args, status);
        field = va_arg(args, const char *);
        while (field) {
            if (0 == strcmp(field, bson_iter_key(&iter))) {
                found = true;
                break;
            }
            field = va_arg(args, const char *);
        }
        va_end(args);

        if (!found) {
            CLIENT_ERR("Unexpected field: '%s'", bson_iter_key(&iter));
            return false;
        }
    }
    return true;
}

#define KEY_HELP "Expected `` or `:`. Example: `local` or `local:name`."

bool mc_kmsid_parse(const char *kmsid,
                    _mongocrypt_kms_provider_t *type_out,
                    const char **name_out,
                    mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(kmsid);
    BSON_ASSERT_PARAM(type_out);
    BSON_ASSERT_PARAM(name_out);
    BSON_ASSERT(status || true); // Optional.

    *type_out = MONGOCRYPT_KMS_PROVIDER_NONE;
    *name_out = NULL;

    const char *type_end = strstr(kmsid, ":");
    size_t type_nchars;

    if (type_end == NULL) {
        // Parse `kmsid` as ``.
        type_nchars = strlen(kmsid);
    } else {
        // Parse `kmsid` as `:`.
        ptrdiff_t diff = type_end - kmsid;
        BSON_ASSERT(diff >= 0 && (uint64_t)diff < SIZE_MAX);
        type_nchars = (size_t)diff;
    }

    if (0 == strncmp("aws", kmsid, type_nchars)) {
        *type_out = MONGOCRYPT_KMS_PROVIDER_AWS;
    } else if (0 == strncmp("azure", kmsid, type_nchars)) {
        *type_out = MONGOCRYPT_KMS_PROVIDER_AZURE;
    } else if (0 == strncmp("gcp", kmsid, type_nchars)) {
        *type_out = MONGOCRYPT_KMS_PROVIDER_GCP;
    } else if (0 == strncmp("kmip", kmsid, type_nchars)) {
        *type_out = MONGOCRYPT_KMS_PROVIDER_KMIP;
    } else if (0 == strncmp("local", kmsid, type_nchars)) {
        *type_out = MONGOCRYPT_KMS_PROVIDER_LOCAL;
    } else {
        CLIENT_ERR("unrecognized KMS provider `%s`: unrecognized type. " KEY_HELP, kmsid);
        return false;
    }

    if (type_end != NULL) {
        // Parse name.
        *name_out = type_end + 1;
        if (0 == strlen(*name_out)) {
            CLIENT_ERR("unrecognized KMS provider `%s`: empty name. " KEY_HELP, kmsid);
            return false;
        }

        // Validate name only contains: [a-zA-Z0-9_]
        for (const char *cp = *name_out; *cp != '\0'; cp++) {
            char c = *cp;
            if (c >= 'a' && c <= 'z') {
                continue;
            }
            if (c >= 'A' && c <= 'Z') {
                continue;
            }
            if (c >= '0' && c <= '9') {
                continue;
            }
            if (c == '_') {
                continue;
            }
            CLIENT_ERR("unrecognized KMS provider `%s`: unsupported character `%c`. Must be of the form `:` where `` only contain characters [a-zA-Z0-9_]",
                       kmsid,
                       c);
            return false;
        }
    }
    return true;
}

static bool _mongocrypt_opts_kms_provider_local_parse(_mongocrypt_opts_kms_provider_local_t *local,
                                                      const char *kmsid,
                                                      const bson_t *def,
                                                      mongocrypt_status_t *status) {
    bool ok = false;
    if (!_mongocrypt_parse_required_binary(def, "key", &local->key, status)) {
        goto fail;
    }

    if (local->key.len != MONGOCRYPT_KEY_LEN) {
        CLIENT_ERR("local key must be %d bytes", MONGOCRYPT_KEY_LEN);
        goto fail;
    }

    if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "key")) {
        goto fail;
    }
    ok = true;
fail:
    if (!ok) {
        // Wrap error to identify the failing `kmsid`.
        CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
    }
    return ok;
}

static bool _mongocrypt_opts_kms_provider_azure_parse(_mongocrypt_opts_kms_provider_azure_t *azure,
                                                      const char *kmsid,
                                                      const bson_t *def,
                                                      mongocrypt_status_t *status) {
    bool ok = false;
    if (!_mongocrypt_parse_optional_utf8(def, "accessToken", &azure->access_token, status)) {
        goto done;
    }

    if (azure->access_token) {
        // Caller provides an accessToken directly
        if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "accessToken")) {
            goto done;
        }
        ok = true;
        goto done;
    }

    // No accessToken given, so we'll need to look one up on our own later
    // using the Azure API

    if (!_mongocrypt_parse_required_utf8(def, "tenantId", &azure->tenant_id, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_required_utf8(def, "clientId", &azure->client_id, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_required_utf8(def, "clientSecret", &azure->client_secret, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_optional_endpoint(def,
                                             "identityPlatformEndpoint",
                                             &azure->identity_platform_endpoint,
                                             NULL /* opts */,
                                             status)) {
        goto done;
    }

    if (!_mongocrypt_check_allowed_fields(def,
                                          NULL /* root */,
                                          status,
                                          "tenantId",
                                          "clientId",
                                          "clientSecret",
                                          "identityPlatformEndpoint")) {
        goto done;
    }

    ok = true;
done:
    if (!ok) {
        // Wrap error to identify the failing `kmsid`.
        CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
    }
    return ok;
}

static bool _mongocrypt_opts_kms_provider_gcp_parse(_mongocrypt_opts_kms_provider_gcp_t *gcp,
                                                    const char *kmsid,
                                                    const bson_t *def,
                                                    mongocrypt_status_t *status) {
    bool ok = false;
    if (!_mongocrypt_parse_optional_utf8(def, "accessToken", &gcp->access_token, status)) {
        goto done;
    }

    if (gcp->access_token) {
        // Caller provides an accessToken directly
        if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "accessToken")) {
            goto done;
        }
        ok = true;
        goto done;
    }

    // No accessToken given, so we'll need to look one up on our own later
    // using the GCP API

    if (!_mongocrypt_parse_required_utf8(def, "email", &gcp->email, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_required_binary(def, "privateKey", &gcp->private_key, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_optional_endpoint(def, "endpoint", &gcp->endpoint, NULL /* opts */, status)) {
        goto done;
    }

    if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "email", "privateKey", "endpoint")) {
        goto done;
    }

    ok = true;
done:
    if (!ok) {
        // Wrap error to identify the failing `kmsid`.
        CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
    }
    return ok;
}

static bool _mongocrypt_opts_kms_provider_aws_parse(_mongocrypt_opts_kms_provider_aws_t *aws,
                                                    const char *kmsid,
                                                    const bson_t *def,
                                                    mongocrypt_status_t *status) {
    bool ok = false;

    if (!_mongocrypt_parse_required_utf8(def, "accessKeyId", &aws->access_key_id, status)) {
        goto done;
    }
    if (!_mongocrypt_parse_required_utf8(def, "secretAccessKey", &aws->secret_access_key, status)) {
        goto done;
    }

    if (!_mongocrypt_parse_optional_utf8(def, "sessionToken", &aws->session_token, status)) {
        goto done;
    }

    if (!_mongocrypt_check_allowed_fields(def,
                                          NULL /* root */,
                                          status,
                                          "accessKeyId",
                                          "secretAccessKey",
                                          "sessionToken")) {
        goto done;
    }

    ok = true;
done:
    if (!ok) {
        // Wrap error to identify the failing `kmsid`.
        CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
    }
    return ok;
}

static bool _mongocrypt_opts_kms_provider_kmip_parse(_mongocrypt_opts_kms_provider_kmip_t *kmip,
                                                     const char *kmsid,
                                                     const bson_t *def,
                                                     mongocrypt_status_t *status) {
    bool ok = false;

    _mongocrypt_endpoint_parse_opts_t opts = {0};

    opts.allow_empty_subdomain = true;
    if (!_mongocrypt_parse_required_endpoint(def, "endpoint", &kmip->endpoint, &opts, status)) {
        goto done;
    }

    if (!_mongocrypt_check_allowed_fields(def, NULL /* root */, status, "endpoint")) {
        goto done;
    }

    ok = true;
done:
    if (!ok) {
        // Wrap error to identify the failing `kmsid`.
        CLIENT_ERR("Failed to parse KMS provider `%s`: %s", kmsid, mongocrypt_status_message(status, NULL /* len */));
    }
    return ok;
}

bool _mongocrypt_parse_kms_providers(mongocrypt_binary_t *kms_providers_definition,
                                     _mongocrypt_opts_kms_providers_t *kms_providers,
                                     mongocrypt_status_t *status,
                                     _mongocrypt_log_t *log) {
    bson_t as_bson;
    bson_iter_t iter;

    BSON_ASSERT_PARAM(kms_providers_definition);
    BSON_ASSERT_PARAM(kms_providers);
    if (!_mongocrypt_binary_to_bson(kms_providers_definition, &as_bson) || !bson_iter_init(&iter, &as_bson)) {
        CLIENT_ERR("invalid BSON");
        return false;
    }

    while (bson_iter_next(&iter)) {
        const char *field_name;
        bson_t field_bson;

        field_name = bson_iter_key(&iter);
        if (!mc_iter_document_as_bson(&iter, &field_bson, status)) {
            return false;
        }

        const char *name;
        _mongocrypt_kms_provider_t type;
        if (!mc_kmsid_parse(field_name, &type, &name, status)) {
            return false;
        }

        if (name != NULL) {
            // Check if named provider already is configured.
            for (size_t i = 0; i < kms_providers->named_mut.len; i++) {
                mc_kms_creds_with_id_t kcwi = _mc_array_index(&kms_providers->named_mut, mc_kms_creds_with_id_t, i);
                if (0 == strcmp(kcwi.kmsid, field_name)) {
                    CLIENT_ERR("Got unexpected duplicate entry for KMS provider: `%s`", field_name);
                    return false;
                }
            }
            // Prohibit configuring with an empty document. Named KMS providers do not support on-demand credentials.
            if (bson_empty(&field_bson)) {
                CLIENT_ERR("Unexpected empty document for named KMS provider: '%s'. On-demand credentials are not "
                           "supported for named KMS providers.",
                           field_name);
                return false;
            }
            switch (type) {
            default:
            case MONGOCRYPT_KMS_PROVIDER_NONE: {
                CLIENT_ERR("Unexpected parsing KMS type: none");
                return false;
            }
            case MONGOCRYPT_KMS_PROVIDER_AWS: {
                _mongocrypt_opts_kms_provider_aws_t aws = {0};
                if (!_mongocrypt_opts_kms_provider_aws_parse(&aws, field_name, &field_bson, status)) {
                    _mongocrypt_opts_kms_provider_aws_cleanup(&aws);
                    return false;
                }
                mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
                                               .creds = {.type = type, .value = {.aws = aws}}};
                _mc_array_append_val(&kms_providers->named_mut, kcwi);
                break;
            }
            case MONGOCRYPT_KMS_PROVIDER_LOCAL: {
                _mongocrypt_opts_kms_provider_local_t local = {
                    // specify .key to avoid erroneous missing-braces warning in GCC. Refer:
                    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
                    .key = {0}};
                if (!_mongocrypt_opts_kms_provider_local_parse(&local, field_name, &field_bson, status)) {
                    _mongocrypt_opts_kms_provider_local_cleanup(&local);
                    return false;
                }
                mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
                                               .creds = {.type = type, .value = {.local = local}}};
                _mc_array_append_val(&kms_providers->named_mut, kcwi);
                break;
            }
            case MONGOCRYPT_KMS_PROVIDER_AZURE: {
                _mongocrypt_opts_kms_provider_azure_t azure = {0};
                if (!_mongocrypt_opts_kms_provider_azure_parse(&azure, field_name, &field_bson, status)) {
                    _mongocrypt_opts_kms_provider_azure_cleanup(&azure);
                    return false;
                }
                mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
                                               .creds = {.type = type, .value = {.azure = azure}}};
                _mc_array_append_val(&kms_providers->named_mut, kcwi);
                break;
            }
            case MONGOCRYPT_KMS_PROVIDER_GCP: {
                _mongocrypt_opts_kms_provider_gcp_t gcp = {0};
                if (!_mongocrypt_opts_kms_provider_gcp_parse(&gcp, field_name, &field_bson, status)) {
                    _mongocrypt_opts_kms_provider_gcp_cleanup(&gcp);
                    return false;
                }
                mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
                                               .creds = {.type = type, .value = {.gcp = gcp}}};
                _mc_array_append_val(&kms_providers->named_mut, kcwi);
                break;
            }
            case MONGOCRYPT_KMS_PROVIDER_KMIP: {
                _mongocrypt_opts_kms_provider_kmip_t kmip = {0};
                if (!_mongocrypt_opts_kms_provider_kmip_parse(&kmip, field_name, &field_bson, status)) {
                    _mongocrypt_opts_kms_provider_kmip_cleanup(&kmip);
                    return false;
                }
                mc_kms_creds_with_id_t kcwi = {.kmsid = bson_strdup(field_name),
                                               .creds = {.type = type, .value = {.kmip = kmip}}};
                _mc_array_append_val(&kms_providers->named_mut, kcwi);
                break;
            }
            }
        } else if (0 == strcmp(field_name, "azure") && bson_empty(&field_bson)) {
            kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AZURE;
        } else if (0 == strcmp(field_name, "azure")) {
            if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AZURE)) {
                CLIENT_ERR("azure KMS provider already set");
                return false;
            }

            if (!_mongocrypt_opts_kms_provider_azure_parse(&kms_providers->azure_mut,
                                                           field_name,
                                                           &field_bson,
                                                           status)) {
                return false;
            }
            kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AZURE;
        } else if (0 == strcmp(field_name, "gcp") && bson_empty(&field_bson)) {
            kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_GCP;
        } else if (0 == strcmp(field_name, "gcp")) {
            if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_GCP)) {
                CLIENT_ERR("gcp KMS provider already set");
                return false;
            }
            if (!_mongocrypt_opts_kms_provider_gcp_parse(&kms_providers->gcp_mut, field_name, &field_bson, status)) {
                return false;
            }
            kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_GCP;
        } else if (0 == strcmp(field_name, "local") && bson_empty(&field_bson)) {
            kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
        } else if (0 == strcmp(field_name, "local")) {
            if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL)) {
                CLIENT_ERR("local KMS provider already set");
                return false;
            }
            if (!_mongocrypt_opts_kms_provider_local_parse(&kms_providers->local_mut,
                                                           field_name,
                                                           &field_bson,
                                                           status)) {
                return false;
            }
            kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
        } else if (0 == strcmp(field_name, "aws") && bson_empty(&field_bson)) {
            kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_AWS;
        } else if (0 == strcmp(field_name, "aws")) {
            if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
                CLIENT_ERR("aws KMS provider already set");
                return false;
            }
            if (!_mongocrypt_opts_kms_provider_aws_parse(&kms_providers->aws_mut, field_name, &field_bson, status)) {
                return false;
            }
            kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
        } else if (0 == strcmp(field_name, "kmip") && bson_empty(&field_bson)) {
            kms_providers->need_credentials |= MONGOCRYPT_KMS_PROVIDER_KMIP;
        } else if (0 == strcmp(field_name, "kmip")) {
            if (!_mongocrypt_opts_kms_provider_kmip_parse(&kms_providers->kmip_mut, field_name, &field_bson, status)) {
                return false;
            }
            kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_KMIP;
        } else {
            CLIENT_ERR("unsupported KMS provider: %s", field_name);
            return false;
        }
    }

    return true;
}
libmongocrypt-1.19.0/src/mongocrypt-private.h000066400000000000000000000207161521103432300213030ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_PRIVATE_H
#define MONGOCRYPT_PRIVATE_H

#include "bson/bson.h"
#include "mongocrypt-config.h"
#include "mongocrypt.h"

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-cache-key-private.h"
#include "mongocrypt-cache-oauth-private.h"
#include "mongocrypt-cache-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-dll-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-mutex-private.h"
#include "mongocrypt-opts-private.h"

#include "mongo_crypt-v1.h"

#include 

#define MONGOCRYPT_GENERIC_ERROR_CODE 1

#define CLIENT_ERR_W_CODE(code, ...) _mongocrypt_set_error(status, MONGOCRYPT_STATUS_ERROR_CLIENT, code, __VA_ARGS__)

#define CLIENT_ERR(...) CLIENT_ERR_W_CODE(MONGOCRYPT_GENERIC_ERROR_CODE, __VA_ARGS__)

#define KMS_ERR_W_CODE(code, ...) _mongocrypt_set_error(status, MONGOCRYPT_STATUS_ERROR_KMS, code, __VA_ARGS__)

#define KMS_ERR(...) KMS_ERR_W_CODE(MONGOCRYPT_GENERIC_ERROR_CODE, __VA_ARGS__)

#define MONGOCRYPT_STR_AND_LEN(x) (x), (sizeof(x) / sizeof((x)[0]) - 1)

#define MONGOCRYPT_DATA_AND_LEN(x) ((uint8_t *)x), (sizeof(x) / sizeof((x)[0]) - 1)

#define LATEST_STR_ENCODE_VERSION 1

#define MIN_STR_ENCODE_VERSION 1

/* TODO: Move these to mongocrypt-log-private.h? */
const char *tmp_json(const bson_t *bson);

const char *tmp_buf(const _mongocrypt_buffer_t *buf);

// _mongocrypt_set_error sets an error status.
// `status->message` is set after evaluating `format` and variadic args.
// It is safe to uses a `status->message` as an argument to wrap an error. For
// example: _mongocrypt_set_error (
//    status,
//    MONGOCRYPT_STATUS_ERROR_CLIENT,
//    MONGOCRYPT_GENERIC_ERROR_CODE,
//    "Error occurred. Original error: %s",
//    mongocrypt_status_message (status, NULL /* len */));
MLIB_ANNOTATE_PRINTF(4, 5)
void _mongocrypt_set_error(mongocrypt_status_t *status,
                           mongocrypt_status_type_t type,
                           uint32_t code,
                           const char *format,
                           ...);

typedef struct _mongo_crypt_v1_vtable {
#define MONGOC_CSFLE_FUNCTIONS_X                                                                                       \
    /* status methods */                                                                                               \
    X_FUNC(status_create, mongo_crypt_v1_status *, void)                                                               \
    X_FUNC(status_destroy, void, mongo_crypt_v1_status *status)                                                        \
    X_FUNC(status_get_error, int, const mongo_crypt_v1_status *status)                                                 \
    X_FUNC(status_get_explanation, const char *, const mongo_crypt_v1_status *status)                                  \
    X_FUNC(status_get_code, int, const mongo_crypt_v1_status *status)                                                  \
    /* lib methods */                                                                                                  \
    X_FUNC(lib_create, mongo_crypt_v1_lib *, mongo_crypt_v1_status *status)                                            \
    X_FUNC(lib_destroy, int, mongo_crypt_v1_lib *lib, mongo_crypt_v1_status *status)                                   \
    /* query_analyzer methods */                                                                                       \
    X_FUNC(query_analyzer_create,                                                                                      \
           mongo_crypt_v1_query_analyzer *,                                                                            \
           mongo_crypt_v1_lib *lib,                                                                                    \
           mongo_crypt_v1_status *status)                                                                              \
    X_FUNC(query_analyzer_destroy, void, mongo_crypt_v1_query_analyzer *analyzer)                                      \
    X_FUNC(analyze_query,                                                                                              \
           uint8_t *,                                                                                                  \
           mongo_crypt_v1_query_analyzer *analyer,                                                                     \
           const uint8_t *documentBSON,                                                                                \
           const char *ns_str,                                                                                         \
           uint32_t ns_len,                                                                                            \
           uint32_t *bson_len,                                                                                         \
           mongo_crypt_v1_status *status)                                                                              \
    X_FUNC(get_version, uint64_t, void)                                                                                \
    X_FUNC(get_version_str, const char *, void)                                                                        \
    /* Free bson data created by csfle */                                                                              \
    X_FUNC(bson_free, void, uint8_t *bson)

#define X_FUNC(Name, RetType, ...) RetType (*Name)(__VA_ARGS__);
    MONGOC_CSFLE_FUNCTIONS_X
#undef X_FUNC

    /// Whether the vtable is valid and complete
    bool okay;
} _mongo_crypt_v1_vtable;

struct _mongocrypt_t {
    bool initialized;
    _mongocrypt_opts_t opts;
    mongocrypt_mutex_t mutex;
    /* The collinfo and key cache are protected with an internal mutex. */
    _mongocrypt_cache_t cache_collinfo;
    _mongocrypt_cache_t cache_key;
    _mongocrypt_log_t log;
    mongocrypt_status_t *status;
    _mongocrypt_crypto_t *crypto;
    /* A counter, protected by mutex, for generating unique context ids */
    uint32_t ctx_counter;
    mc_mapof_kmsid_to_token_t *cache_oauth;
    /// A CSFLE DLL vtable, initialized by mongocrypt_init
    _mongo_crypt_v1_vtable csfle;
    /// Pointer to the global csfle_lib object. Should not be freed directly.
    mongo_crypt_v1_lib *csfle_lib;
    bool retry_enabled;
    bool multiple_collinfo_enabled;
};

typedef enum {
    MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE = 0,
    MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC = 1,
    MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM = 2
} mongocrypt_encryption_algorithm_t;

typedef enum {
    MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT = 1,
    MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND = 2
} mongocrypt_fle2_placeholder_type_t;

typedef enum {
    MONGOCRYPT_FLE2_ALGORITHM_UNINDEXED = 1,
    MONGOCRYPT_FLE2_ALGORITHM_EQUALITY = 2,
    MONGOCRYPT_FLE2_ALGORITHM_RANGE = 3,
    MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH = 4
} mongocrypt_fle2_encryption_algorithm_t;

typedef enum {
    MONGOCRYPT_INDEX_TYPE_NONE = 1,
    MONGOCRYPT_INDEX_TYPE_EQUALITY = 2,
    MONGOCRYPT_INDEX_TYPE_RANGE = 3,
    MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED = 4,
    MONGOCRYPT_INDEX_TYPE_STRING = 5,
} mongocrypt_index_type_t;

bool _mongocrypt_validate_and_copy_string(const char *in, int32_t in_len, char **out) MONGOCRYPT_WARN_UNUSED_RESULT;

char *_mongocrypt_new_string_from_bytes(const void *in, int len);

/* _mongocrypt_needs_credentials returns true if @crypt was configured to
 * request credentials for any KMS provider. */
bool _mongocrypt_needs_credentials(mongocrypt_t *crypt);

/* _mongocrypt_needs_credentials returns true if @crypt was configured to
 * request credentials for @provider and optional @name. @name may be NULL. */
bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt,
                                                _mongocrypt_kms_provider_t provider,
                                                const char *name);

void _bson_value_from_string(const char *string, bson_value_t *value);

#endif /* MONGOCRYPT_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-status-private.h000066400000000000000000000023011521103432300226120ustar00rootroot00000000000000/*
 * Copyright 2018-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.
 */

#ifndef MONGOCRYPT_STATUS_PRIVATE_H
#define MONGOCRYPT_STATUS_PRIVATE_H

#include "mongocrypt.h"

void _mongocrypt_status_copy_to(mongocrypt_status_t *src, mongocrypt_status_t *dst);

void _mongocrypt_status_reset(mongocrypt_status_t *status);

/* Append the message of @to_append in @status.
 * Example:
 * - @status has the message "status error"
 * - @to_append has the message "append error"
 * After calling, @status will have the message: "status error: append error"
 */
void _mongocrypt_status_append(mongocrypt_status_t *status, mongocrypt_status_t *to_append);

#endif /* MONGOCRYPT_STATUS_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-status.c000066400000000000000000000071651521103432300211520ustar00rootroot00000000000000/*
 * Copyright 2018-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 

#include "mongocrypt-status-private.h"

struct _mongocrypt_status_t {
    mongocrypt_status_type_t type;
    uint32_t code;
    char *message;
    uint32_t len;
};

mongocrypt_status_t *mongocrypt_status_new(void) {
    return bson_malloc0(sizeof(mongocrypt_status_t));
}

void mongocrypt_status_set(mongocrypt_status_t *status,
                           mongocrypt_status_type_t type,
                           uint32_t code,
                           const char *message,
                           int32_t message_len) {
    if (!status) {
        return;
    }

    if (message_len < 0) {
        message_len = (int32_t)strlen(message) + 1;
    } else if (message_len == 0) {
        /* This is really an error, since message_len should be one more than the
         * string length. But interpret as the empty string */
        message_len = 1;
    }

    bson_free(status->message);
    status->message = bson_malloc((size_t)message_len);
    BSON_ASSERT(status->message);
    status->message[message_len - 1] = '\0';
    memcpy(status->message, message, (size_t)message_len - 1);
    status->len = (uint32_t)message_len - 1;
    status->type = type;
    status->code = code;
}

const char *mongocrypt_status_message(mongocrypt_status_t *status, uint32_t *len) {
    BSON_ASSERT_PARAM(status);

    if (mongocrypt_status_ok(status)) {
        return NULL;
    }
    if (len) {
        *len = status->len;
    }
    return status->message;
}

uint32_t mongocrypt_status_code(mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(status);

    return status->code;
}

mongocrypt_status_type_t mongocrypt_status_type(mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(status);

    return status->type;
}

bool mongocrypt_status_ok(mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(status);

    return (status->type == MONGOCRYPT_STATUS_OK);
}

void _mongocrypt_status_copy_to(mongocrypt_status_t *src, mongocrypt_status_t *dst) {
    BSON_ASSERT_PARAM(dst);
    BSON_ASSERT_PARAM(src);

    if (dst == src) {
        return;
    }

    dst->type = src->type;
    dst->code = src->code;
    dst->len = src->len;
    if (dst->message) {
        bson_free(dst->message);
        dst->message = NULL;
    }

    if (src->message) {
        dst->message = bson_strdup(src->message);
    }
}

void _mongocrypt_status_reset(mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(status);

    status->type = MONGOCRYPT_STATUS_OK;
    status->code = 0;
    status->len = 0;
    bson_free(status->message);
    status->message = NULL;
}

void mongocrypt_status_destroy(mongocrypt_status_t *status) {
    if (!status) {
        return;
    }

    bson_free(status->message);
    bson_free(status);
}

void _mongocrypt_status_append(mongocrypt_status_t *status, mongocrypt_status_t *to_append) {
    char *orig;

    BSON_ASSERT_PARAM(status);
    BSON_ASSERT_PARAM(to_append);

    orig = status->message;

    if (mongocrypt_status_ok(to_append)) {
        return;
    }
    status->message = bson_strdup_printf("%s: %s", orig, to_append->message);
    bson_free(orig);
}
libmongocrypt-1.19.0/src/mongocrypt-traverse-util-private.h000066400000000000000000000040551521103432300241050ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MONGOCRYPT_TRAVERSE_UTIL_H
#define MONGOCRYPT_TRAVERSE_UTIL_H

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-status-private.h"

typedef enum {
    TRAVERSE_MATCH_CIPHERTEXT,
    TRAVERSE_MATCH_MARKING,
    TRAVERSE_MATCH_SUBTYPE6,
} traversal_match_t;

typedef bool (*_mongocrypt_traverse_callback_t)(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status);

typedef bool (*_mongocrypt_transform_callback_t)(void *ctx,
                                                 _mongocrypt_buffer_t *in,
                                                 bson_value_t *out,
                                                 mongocrypt_status_t *status);

bool _mongocrypt_traverse_binary_in_bson(_mongocrypt_traverse_callback_t cb,
                                         void *ctx,
                                         traversal_match_t match,
                                         bson_iter_t *iter,
                                         mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

bool _mongocrypt_transform_binary_in_bson(_mongocrypt_transform_callback_t cb,
                                          void *ctx,
                                          traversal_match_t match,
                                          bson_iter_t *iter,
                                          bson_t *out,
                                          mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT;

#endif /* MONGOCRYPT_TRAVERSE_UTIL_H */
libmongocrypt-1.19.0/src/mongocrypt-traverse-util.c000066400000000000000000000165731521103432300224400ustar00rootroot00000000000000/*
 * Copyright 2018-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 

#include "mc-fle-blob-subtype-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-status-private.h"
#include "mongocrypt-traverse-util-private.h"

typedef struct {
    void *ctx;
    bson_iter_t iter;
    bson_t *copy; /* implies transform */
    char *path;   /* only enabled during tracing. */
    _mongocrypt_traverse_callback_t traverse_cb;
    _mongocrypt_transform_callback_t transform_cb;
    mongocrypt_status_t *status;
    traversal_match_t match;
    bson_t child;
} _recurse_state_t;

static bool _check_first_byte(uint8_t byte, traversal_match_t match) {
    switch (match) {
    case TRAVERSE_MATCH_MARKING:
        return byte == MC_SUBTYPE_FLE1EncryptionPlaceholder || byte == MC_SUBTYPE_FLE2EncryptionPlaceholder;
    case TRAVERSE_MATCH_CIPHERTEXT:
        return byte == MC_SUBTYPE_FLE1DeterministicEncryptedValue || byte == MC_SUBTYPE_FLE1RandomEncryptedValue
            || byte == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue || byte == MC_SUBTYPE_FLE2UnindexedEncryptedValue
            || byte == MC_SUBTYPE_FLE2InsertUpdatePayload || byte == MC_SUBTYPE_FLE2IndexedRangeEncryptedValue
            || byte == MC_SUBTYPE_FLE2InsertUpdatePayloadV2 || byte == MC_SUBTYPE_FLE2UnindexedEncryptedValueV2
            || byte == MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2
            || byte == MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2 || byte == MC_SUBTYPE_FLE2IndexedTextEncryptedValue;
    case TRAVERSE_MATCH_SUBTYPE6: return true;
    default: break;
    }
    return false;
}

static bool _recurse(_recurse_state_t *state) {
    mongocrypt_status_t *status;

    BSON_ASSERT_PARAM(state);

    status = state->status;
    while (bson_iter_next(&state->iter)) {
        if (BSON_ITER_HOLDS_BINARY(&state->iter)) {
            _mongocrypt_buffer_t value;

            BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&value, &state->iter));

            if (value.subtype == BSON_SUBTYPE_ENCRYPTED && value.len > 0
                && _check_first_byte(value.data[0], state->match)) {
                bool ret;
                /* call the right callback. */
                if (state->copy) {
                    bson_value_t value_out;
                    ret = state->transform_cb(state->ctx, &value, &value_out, status);
                    if (ret) {
                        const uint32_t key_len = bson_iter_key_len(&state->iter);
                        BSON_ASSERT(key_len <= INT_MAX);
                        bson_append_value(state->copy, bson_iter_key(&state->iter), (int)key_len, &value_out);
                        bson_value_destroy(&value_out);
                    }
                } else {
                    ret = state->traverse_cb(state->ctx, &value, status);
                }

                if (!ret) {
                    return false;
                }
                continue;
            }
            /* fall through and copy */
        }

        if (BSON_ITER_HOLDS_ARRAY(&state->iter)) {
            _recurse_state_t child_state;
            bool ret;

            memcpy(&child_state, state, sizeof(_recurse_state_t));
            if (!bson_iter_recurse(&state->iter, &child_state.iter)) {
                CLIENT_ERR("error recursing into array");
                return false;
            }

            if (state->copy) {
                const uint32_t key_len = bson_iter_key_len(&state->iter);
                BSON_ASSERT(key_len <= INT_MAX);
                bson_append_array_unsafe_begin(state->copy, bson_iter_key(&state->iter), (int)key_len, &state->child);
                child_state.copy = &state->child;
            }
            ret = _recurse(&child_state);

            if (state->copy) {
                bson_append_array_end(state->copy, &state->child);
            }
            if (!ret) {
                return false;
            }
            continue;
        }

        if (BSON_ITER_HOLDS_DOCUMENT(&state->iter)) {
            _recurse_state_t child_state;
            bool ret;

            memcpy(&child_state, state, sizeof(_recurse_state_t));
            if (!bson_iter_recurse(&state->iter, &child_state.iter)) {
                CLIENT_ERR("error recursing into document");
                return false;
            }
            /* TODO: check for errors everywhere. */
            if (state->copy) {
                const uint32_t key_len = bson_iter_key_len(&state->iter);
                BSON_ASSERT(key_len <= INT_MAX);
                bson_append_document_begin(state->copy, bson_iter_key(&state->iter), (int)key_len, &state->child);
                child_state.copy = &state->child;
            }

            ret = _recurse(&child_state);

            if (state->copy) {
                if (!bson_append_document_end(state->copy, &state->child)) {
                    CLIENT_ERR("error appending document");
                    return false;
                }
            }

            if (!ret) {
                return false;
            }
            continue;
        }

        if (state->copy) {
            const uint32_t key_len = bson_iter_key_len(&state->iter);
            BSON_ASSERT(key_len <= INT_MAX);
            bson_append_value(state->copy, bson_iter_key(&state->iter), (int)key_len, bson_iter_value(&state->iter));
        }
    }
    return true;
}

bool _mongocrypt_transform_binary_in_bson(_mongocrypt_transform_callback_t cb,
                                          void *ctx,
                                          traversal_match_t match,
                                          bson_iter_t *iter,
                                          bson_t *out,
                                          mongocrypt_status_t *status) {
    _recurse_state_t starting_state =
        {ctx, *iter, out /* copy */, NULL /* path */, NULL /* traverse callback */, cb, status, match, {0}};

    return _recurse(&starting_state);
}

/*-----------------------------------------------------------------------------
 *
 * _mongocrypt_traverse_binary_in_bson
 *
 *    Traverse the BSON being iterated with iter, and call cb for every binary
 *    subtype 06 (BSON_SUBTYPE_ENCRYPTED) value where the first byte corresponds
 *    to 'match'.
 *
 * Return:
 *    True on success. Returns false on failure and sets error.
 *
 *-----------------------------------------------------------------------------
 */
bool _mongocrypt_traverse_binary_in_bson(_mongocrypt_traverse_callback_t cb,
                                         void *ctx,
                                         traversal_match_t match,
                                         bson_iter_t *iter,
                                         mongocrypt_status_t *status) {
    _recurse_state_t starting_state =
        {ctx, *iter, NULL /* copy */, NULL /* path */, cb, NULL /* transform callback */, status, match, {0}};

    return _recurse(&starting_state);
}
libmongocrypt-1.19.0/src/mongocrypt-util-private.h000066400000000000000000000046531521103432300222600ustar00rootroot00000000000000/*
 * 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.
 */

#ifndef MONGOCRYPT_UTIL_PRIVATE_H
#define MONGOCRYPT_UTIL_PRIVATE_H

#include "mongocrypt-status-private.h"

#include "mc-mlib/str.h"

#include 

#include 
#include 
#include 

/* A utility for safely casting from size_t to uint32_t.
 * Returns false if @in exceeds the maximum value of a uint32_t. */
bool size_to_uint32(size_t in, uint32_t *out);

/**
 * @brief The result type of mpath_current_exe_path()
 *
 * The @ref current_module_result::path member must be freed with mstr_free()
 */
typedef struct current_module_result {
    /// The resulting executable path
    mstr path;
    /// An error, if the path could not be obtained
    int error;
} current_module_result;

/**
 * @brief Obtain the path to the calling executable module
 *
 * If this function is contained in a dynamic library, this will return the path
 * to that library file, otherwise it will return the path to the running
 * executable.
 *
 * @return current_module_result A result object of the operation. Check the
 * `.error` member for non-zero. The `.path` member must be freed with
 * mtsr_free()
 */
current_module_result current_module_path(void);

/* mc_bson_type_to_string returns the string representation of a BSON type. */
const char *mc_bson_type_to_string(bson_type_t bson_type);

/* mc_iter_document_as_bson attempts to read the document from @iter into
 * @bson. */
bool mc_iter_document_as_bson(const bson_iter_t *iter, bson_t *bson, mongocrypt_status_t *status);

// mc_isnan is a wrapper around isnan. It avoids a conversion warning on glibc.
bool mc_isnan(double d);
// mc_isinf is a wrapper around isinf. It avoids a conversion warning on glibc.
bool mc_isinf(double d);
// mc_isfinite is a wrapper around isfinite. It avoids a conversion warning on
// glibc.
bool mc_isfinite(double d);

#endif /* MONGOCRYPT_UTIL_PRIVATE_H */
libmongocrypt-1.19.0/src/mongocrypt-util.c000066400000000000000000000126621521103432300206020ustar00rootroot00000000000000/*
 * 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.
 */

// Turn on libc extensions so that we can use dladdr() on Unix-like systems
#if defined(__has_include) && !(defined(_GNU_SOURCE) || defined(_DARWIN_C_SOURCE))
#if __has_include()
// We're using a glibc-compatible library
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#elif __has_include()
// We're on Apple/Darwin
#define _DARWIN_C_SOURCE
#endif
#else // No __has_include
#if __GNUC__ < 5 && !defined(_GNU_SOURCE)
// Best guess on older GCC is that we are using glibc
#define _GNU_SOURCE
#endif
#endif

#include "mongocrypt-dll-private.h"
#include "mongocrypt-private.h" // CLIENT_ERR
#include "mongocrypt-util-private.h"

#include "mc-mlib/thread.h"

#include 
#include  // isinf, isnan, isfinite

#ifdef _WIN32
#include 
#else
#include 
#endif

bool size_to_uint32(size_t in, uint32_t *out) {
    BSON_ASSERT_PARAM(out);

    if (in > UINT32_MAX) {
        return false;
    }
    *out = (uint32_t)in;
    return true;
}

current_module_result current_module_path(void) {
    mstr ret_str = MSTR_NULL;
    int ret_error = 0;
#ifdef _WIN32
    DWORD acc_size = 512;
    while (!ret_str.raw.data && !ret_error) {
        // Loop until we allocate a large enough buffer or get an error
        wchar_t *path = calloc(acc_size + 1, sizeof(wchar_t));
        SetLastError(0);
        GetModuleFileNameW(NULL, path, acc_size);
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            // Try again with more buffer
            acc_size *= 2;
        } else if (GetLastError() != 0) {
            ret_error = GetLastError();
        } else {
            mstr_narrow_result narrow = mstr_win32_narrow(path);
            // GetModuleFileNameW should never return invalid Unicode:
            assert(narrow.error == 0);
            ret_str = narrow.string;
        }
        free(path);
    }
#elif defined(_GNU_SOURCE) || defined(_DARWIN_C_SOURCE) || defined(__FreeBSD__)
    // Darwin/BSD/glibc define extensions for finding dynamic library info from
    // the address of a symbol.
    Dl_info info = {0};
    MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE
    int rc = dladdr((const void *)current_module_path, &info);
    MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE
    if (rc == 0) {
        // Failed to resolve the symbol
        ret_error = ENOENT;
    } else {
        ret_str = mstr_copy_cstr(info.dli_fname);
    }
#else
    // Not supported on this system.
    ret_error = ENOSYS;
#endif
    return (current_module_result){.path = ret_str, .error = ret_error};
}

const char *mc_bson_type_to_string(bson_type_t t) {
    switch (t) {
    case BSON_TYPE_EOD: return "EOD";
    case BSON_TYPE_DOUBLE: return "DOUBLE";
    case BSON_TYPE_UTF8: return "UTF8";
    case BSON_TYPE_DOCUMENT: return "DOCUMENT";
    case BSON_TYPE_ARRAY: return "ARRAY";
    case BSON_TYPE_BINARY: return "BINARY";
    case BSON_TYPE_UNDEFINED: return "UNDEFINED";
    case BSON_TYPE_OID: return "OID";
    case BSON_TYPE_BOOL: return "BOOL";
    case BSON_TYPE_DATE_TIME: return "DATE_TIME";
    case BSON_TYPE_NULL: return "NULL";
    case BSON_TYPE_REGEX: return "REGEX";
    case BSON_TYPE_DBPOINTER: return "DBPOINTER";
    case BSON_TYPE_CODE: return "CODE";
    case BSON_TYPE_SYMBOL: return "SYMBOL";
    case BSON_TYPE_CODEWSCOPE: return "CODEWSCOPE";
    case BSON_TYPE_INT32: return "INT32";
    case BSON_TYPE_TIMESTAMP: return "TIMESTAMP";
    case BSON_TYPE_INT64: return "INT64";
    case BSON_TYPE_MAXKEY: return "MAXKEY";
    case BSON_TYPE_MINKEY: return "MINKEY";
    case BSON_TYPE_DECIMAL128: return "DECIMAL128";
    default: return "Unknown";
    }
}

bool mc_iter_document_as_bson(const bson_iter_t *iter, bson_t *bson, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(iter);
    BSON_ASSERT_PARAM(bson);
    BSON_ASSERT(status || true);

    uint32_t len;
    const uint8_t *data;

    if (!BSON_ITER_HOLDS_DOCUMENT(iter)) {
        CLIENT_ERR("expected BSON document for field: %s", bson_iter_key(iter));
        return false;
    }

    bson_iter_document(iter, &len, &data);
    if (!bson_init_static(bson, data, len)) {
        CLIENT_ERR("unable to initialize BSON document from field: %s", bson_iter_key(iter));
        return false;
    }

    return true;
}

/* Avoid a conversion warning on glibc for isnan, isinf, and isfinite. Refer:
 * MONGOCRYPT-501. */
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
// gcc 4.6 added support for "diagnostic push".
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#endif

bool mc_isnan(double d) {
    return isnan(d);
}

bool mc_isinf(double d) {
    return isinf(d);
}

bool mc_isfinite(double d) {
    return isfinite(d);
}

#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic pop
#endif
libmongocrypt-1.19.0/src/mongocrypt.c000066400000000000000000001224121521103432300176220ustar00rootroot00000000000000/*
 * Copyright 2018-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 "mc-mlib/error.h"
#include "mc-mlib/path.h"
#include "mc-mlib/thread.h"

#include 
#include 

#include "mongocrypt-binary-private.h"
#include "mongocrypt-cache-collinfo-private.h"
#include "mongocrypt-cache-key-private.h"
#include "mongocrypt-cache-private.h"
#include "mongocrypt-config.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-dll-private.h"
#include "mongocrypt-log-private.h"
#include "mongocrypt-mutex-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt-status-private.h"
#include "mongocrypt-util-private.h"

/* Assert size for interop with wrapper purposes */
BSON_STATIC_ASSERT(sizeof(mongocrypt_log_level_t) == 4);

const char *mongocrypt_version(uint32_t *len) {
    if (len) {
        *len = (uint32_t)strlen(MONGOCRYPT_VERSION);
    }
    return MONGOCRYPT_VERSION;
}

void _mongocrypt_set_error(mongocrypt_status_t *status,
                           mongocrypt_status_type_t type,
                           uint32_t code,
                           const char *format,
                           ...) {
    va_list args;
    char *prepared_message;

    if (status) {
        va_start(args, format);
        prepared_message = bson_strdupv_printf(format, args);
        if (!prepared_message) {
            mongocrypt_status_set(status, type, code, "Out of memory", -1);
        } else {
            mongocrypt_status_set(status, type, code, prepared_message, -1);
            bson_free(prepared_message);
        }
        va_end(args);
    }
}

const char *tmp_json(const bson_t *bson) {
    static char storage[1024];
    char *json;

    BSON_ASSERT_PARAM(bson);

    memset(storage, 0, 1024);
    json = bson_as_canonical_extended_json(bson, NULL);
    BSON_ASSERT(0 < bson_snprintf(storage, sizeof(storage), "%s", json)); // Truncation OK.
    bson_free(json);
    return (const char *)storage;
}

static void _mongocrypt_do_init(void) {
    (void)kms_message_init();
    _native_crypto_init();
}

mongocrypt_t *mongocrypt_new(void) {
    mongocrypt_t *crypt;

    crypt = bson_malloc0(sizeof(mongocrypt_t));
    BSON_ASSERT(crypt);
    crypt->crypto = bson_malloc0(sizeof(*crypt->crypto));
    BSON_ASSERT(crypt->crypto);

    _mongocrypt_mutex_init(&crypt->mutex);
    _mongocrypt_cache_collinfo_init(&crypt->cache_collinfo);
    _mongocrypt_cache_key_init(&crypt->cache_key);
    crypt->status = mongocrypt_status_new();
    _mongocrypt_opts_init(&crypt->opts);
    _mongocrypt_log_init(&crypt->log);
    crypt->ctx_counter = 1;
    crypt->cache_oauth = mc_mapof_kmsid_to_token_new();
    crypt->csfle = (_mongo_crypt_v1_vtable){.okay = false};

    static mlib_once_flag init_flag = MLIB_ONCE_INITIALIZER;

    if (!mlib_call_once(&init_flag, _mongocrypt_do_init) || !_native_crypto_initialized) {
        mongocrypt_status_t *status = crypt->status;

        CLIENT_ERR("failed to initialize");
        /* Return crypt with failure status so caller can obtain error when
         * calling mongocrypt_init */
    }

    return crypt;
}

#define ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt)                                                                          \
    if (1) {                                                                                                           \
        const mongocrypt_t *_crypt = (crypt);                                                                          \
        BSON_ASSERT_PARAM(_crypt);                                                                                     \
        if (_crypt->initialized) {                                                                                     \
            mongocrypt_status_t *status = _crypt->status;                                                              \
            CLIENT_ERR("options cannot be set after initialization");                                                  \
            return false;                                                                                              \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

bool mongocrypt_setopt_use_range_v2(mongocrypt_t *crypt) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    // Nothing to do. As of MONGOCRYPT-661, rangeV2 is the default.
    return true;
}

bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_fn, void *log_ctx) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
    crypt->opts.log_fn = log_fn;
    crypt->opts.log_ctx = log_ctx;
    return true;
}

bool mongocrypt_setopt_retry_kms(mongocrypt_t *crypt, bool enable) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
    crypt->retry_enabled = enable;
    return true;
}

bool mongocrypt_setopt_enable_multiple_collinfo(mongocrypt_t *crypt) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
    crypt->multiple_collinfo_enabled = true;
    return true;
}

bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
                                        const char *aws_access_key_id,
                                        int32_t aws_access_key_id_len,
                                        const char *aws_secret_access_key,
                                        int32_t aws_secret_access_key_len) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    mongocrypt_status_t *status = crypt->status;
    _mongocrypt_opts_kms_providers_t *const kms_providers = &crypt->opts.kms_providers;

    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_AWS)) {
        CLIENT_ERR("aws kms provider already set");
        return false;
    }

    if (!_mongocrypt_validate_and_copy_string(aws_access_key_id,
                                              aws_access_key_id_len,
                                              &kms_providers->aws_mut.access_key_id)) {
        CLIENT_ERR("invalid aws access key id");
        return false;
    }

    if (!_mongocrypt_validate_and_copy_string(aws_secret_access_key,
                                              aws_secret_access_key_len,
                                              &kms_providers->aws_mut.secret_access_key)) {
        CLIENT_ERR("invalid aws secret access key");
        return false;
    }

    kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_AWS;
    return true;
}

bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
    if (cache_expiration_ms > INT64_MAX) {
        mongocrypt_status_t *status = crypt->status;
        CLIENT_ERR("expiration time must be less than %" PRId64 ", but got %" PRIu64, INT64_MAX, cache_expiration_ms);
        return false;
    }
    crypt->cache_key.expiration = cache_expiration_ms;
    return true;
}

bool mongocrypt_setopt_schema_map(mongocrypt_t *crypt, mongocrypt_binary_t *schema_map) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    bson_t tmp;
    bson_error_t bson_err;
    mongocrypt_status_t *status = crypt->status;

    if (!schema_map || !mongocrypt_binary_data(schema_map)) {
        CLIENT_ERR("passed null schema map");
        return false;
    }

    if (!_mongocrypt_buffer_empty(&crypt->opts.schema_map)) {
        CLIENT_ERR("already set schema map");
        return false;
    }

    _mongocrypt_buffer_copy_from_binary(&crypt->opts.schema_map, schema_map);

    /* validate bson */
    if (!_mongocrypt_buffer_to_bson(&crypt->opts.schema_map, &tmp)) {
        CLIENT_ERR("invalid bson");
        return false;
    }

    if (!bson_validate_with_error(&tmp, BSON_VALIDATE_NONE, &bson_err)) {
        CLIENT_ERR("%s", bson_err.message);
        return false;
    }

    return true;
}

bool mongocrypt_setopt_encrypted_field_config_map(mongocrypt_t *crypt, mongocrypt_binary_t *efc_map) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    mongocrypt_status_t *status = crypt->status;
    bson_t as_bson;
    bson_error_t bson_err;

    if (!efc_map || !mongocrypt_binary_data(efc_map)) {
        CLIENT_ERR("passed null encrypted_field_config_map");
        return false;
    }

    if (!_mongocrypt_buffer_empty(&crypt->opts.encrypted_field_config_map)) {
        CLIENT_ERR("already set encrypted_field_config_map");
        return false;
    }

    _mongocrypt_buffer_copy_from_binary(&crypt->opts.encrypted_field_config_map, efc_map);

    /* validate bson */
    if (!_mongocrypt_buffer_to_bson(&crypt->opts.encrypted_field_config_map, &as_bson)) {
        CLIENT_ERR("invalid bson");
        return false;
    }

    if (!bson_validate_with_error(&as_bson, BSON_VALIDATE_NONE, &bson_err)) {
        CLIENT_ERR("%s", bson_err.message);
        return false;
    }

    return true;
}

bool mongocrypt_setopt_kms_provider_local(mongocrypt_t *crypt, mongocrypt_binary_t *key) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    mongocrypt_status_t *status = crypt->status;
    _mongocrypt_opts_kms_providers_t *const kms_providers = &crypt->opts.kms_providers;

    if (0 != (kms_providers->configured_providers & MONGOCRYPT_KMS_PROVIDER_LOCAL)) {
        CLIENT_ERR("local kms provider already set");
        return false;
    }

    if (!key) {
        CLIENT_ERR("passed null key");
        return false;
    }

    if (mongocrypt_binary_len(key) != MONGOCRYPT_KEY_LEN) {
        CLIENT_ERR("local key must be %d bytes", MONGOCRYPT_KEY_LEN);
        return false;
    }

    _mongocrypt_buffer_copy_from_binary(&kms_providers->local_mut.key, key);
    kms_providers->configured_providers |= MONGOCRYPT_KMS_PROVIDER_LOCAL;
    return true;
}

typedef struct {
    /// Whether the load is successful
    bool okay;
    /// The DLL handle to the opened library.
    mcr_dll lib;
    /// A vtable for the functions in the DLL
    _mongo_crypt_v1_vtable vtable;
} _loaded_csfle;

/**
 * @brief Attempt to open the CSFLE dynamic library and initialize a vtable for
 * it.
 *
 * @param status is an optional status to set an error message if `mcr_dll_open` fails.
 */
static _loaded_csfle _try_load_csfle(const char *filepath, mongocrypt_status_t *status, _mongocrypt_log_t *log) {
    // Try to open the dynamic lib
    mcr_dll lib = mcr_dll_open(filepath);
    // Check for errors, which are represented by strings
    if (lib.error_string.raw.data) {
        // Error opening candidate
        _mongocrypt_log(log,
                        MONGOCRYPT_LOG_LEVEL_WARNING,
                        "Error while opening candidate for crypt_shared dynamic library [%s]: %s",
                        filepath,
                        lib.error_string.raw.data);
        CLIENT_ERR("Error while opening candidate for crypt_shared dynamic library [%s]: %s",
                   filepath,
                   lib.error_string.raw.data);
        // Free resources, which will include the error string
        mcr_dll_close(lib);
        // Bad:
        return (_loaded_csfle){.okay = false};
    }

    // Construct the library vtable
    _mongo_crypt_v1_vtable vtable = {.okay = true};
#define X_FUNC(Name, RetType, ...)                                                                                     \
    {                                                                                                                  \
        /* Symbol names are qualified by the lib name and version: */                                                  \
        const char *symname = "mongo_crypt_v1_" #Name;                                                                 \
        MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE                                                                      \
        vtable.Name = (RetType(*)(__VA_ARGS__))mcr_dll_sym(lib, symname);                                              \
        MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE                                                                        \
        if (vtable.Name == NULL) {                                                                                     \
            /* The requested symbol is not present */                                                                  \
            _mongocrypt_log(log,                                                                                       \
                            MONGOCRYPT_LOG_LEVEL_ERROR,                                                                \
                            "Missing required symbol '%s' from crypt_shared dynamic library [%s]",                     \
                            symname,                                                                                   \
                            filepath);                                                                                 \
            /* Mark the vtable as broken, but keep trying to load more symbols to                                      \
             * produce error messages for all missing symbols */                                                       \
            vtable.okay = false;                                                                                       \
        }                                                                                                              \
    }
    MONGOC_CSFLE_FUNCTIONS_X
#undef X_FUNC

    if (!vtable.okay) {
        // A common mistake is to pass the path to libmongocrypt instead of than crypt_shared.
        // Check if the library has a libmongocrypt symbol.
        if (mcr_dll_sym(lib, "mongocrypt_version")) {
            CLIENT_ERR("Tried to load crypt_shared dynamic library at path [%s] but detected libmongocrypt", filepath);
            mcr_dll_close(lib);
            return (_loaded_csfle){.okay = false};
        }
        mcr_dll_close(lib);
        _mongocrypt_log(log,
                        MONGOCRYPT_LOG_LEVEL_ERROR,
                        "One or more required symbols are missing from crypt_shared dynamic library "
                        "[%s], so this dynamic library will not be used.",
                        filepath);
        CLIENT_ERR("One or more required symbols are missing from crypt_shared dynamic library "
                   "[%s], so this dynamic library will not be used.",
                   filepath);
        return (_loaded_csfle){.okay = false};
    }

    // Success!
    _mongocrypt_log(log, MONGOCRYPT_LOG_LEVEL_INFO, "Opened crypt_shared dynamic library [%s]", filepath);
    return (_loaded_csfle){.okay = true, .lib = lib, .vtable = vtable};
}

/**
 * @brief If the leading path element in `filepath` is $ORIGIN, replace that
 * with the directory containing the current executing module.
 *
 * @return true If no error occurred and the path is valid
 * @return false If there was an error and `filepath` cannot be processed
 */
static bool _try_replace_dollar_origin(mstr *filepath, _mongocrypt_log_t *log) {
    const mstr_view dollar_origin = mstrv_lit("$ORIGIN");

    BSON_ASSERT_PARAM(filepath);

    if (!mstr_starts_with(filepath->view, dollar_origin)) {
        // Nothing to replace
        return true;
    }
    // Check that the next char is a path separator or end-of-string:
    char peek = filepath->raw.data[dollar_origin.len];
    if (peek != 0 && !mpath_is_sep(peek, MPATH_NATIVE)) {
        // Not a single path element
        return true;
    }
    // Replace $ORIGIN with the directory of the current module
    const current_module_result self_exe_r = current_module_path();
    if (self_exe_r.error) {
        // Failed to get the current module to load replace $ORIGIN
        mstr error = merror_system_error_string(self_exe_r.error);
        _mongocrypt_log(log,
                        MONGOCRYPT_LOG_LEVEL_WARNING,
                        "Error while loading the executable module path for "
                        "substitution of $ORIGIN in crypt_shared search path [%s]: %s",
                        filepath->raw.data,
                        error.raw.data);
        mstr_free(error);
        return false;
    }
    const mstr_view self_dir = mpath_parent(self_exe_r.path.view, MPATH_NATIVE);
    mstr_inplace_splice(filepath, 0, dollar_origin.len, self_dir);
    mstr_free(self_exe_r.path);
    return true;
}

static _loaded_csfle _try_find_csfle(mongocrypt_t *crypt) {
    _loaded_csfle candidate_csfle = {0};
    mstr csfle_cand_filepath = MSTR_NULL;

    BSON_ASSERT_PARAM(crypt);

    if (crypt->opts.crypt_shared_lib_override_path.raw.data) {
        // If an override path was specified, skip the library searching behavior
        csfle_cand_filepath = mstr_copy(crypt->opts.crypt_shared_lib_override_path.view);
        if (_try_replace_dollar_origin(&csfle_cand_filepath, &crypt->log)) {
            // Succesfully substituted $ORIGIN
            // Do not allow a plain filename to go through, as that will cause the
            // DLL load to search the system.
            mstr_assign(&csfle_cand_filepath, mpath_absolute(csfle_cand_filepath.view, MPATH_NATIVE));
            candidate_csfle = _try_load_csfle(csfle_cand_filepath.raw.data, crypt->status, &crypt->log);
        }
    } else {
        // No override path was specified, so try to find it on the provided
        // search paths.
        for (int i = 0; i < crypt->opts.n_crypt_shared_lib_search_paths; ++i) {
            mstr_view cand_dir = crypt->opts.crypt_shared_lib_search_paths[i].view;
            mstr_view csfle_filename = mstrv_lit("mongo_crypt_v1" MCR_DLL_SUFFIX);
            if (mstr_eq(cand_dir, mstrv_lit("$SYSTEM"))) {
                // Caller wants us to search for the library on the system's default
                // library paths. Pass only the library's filename to cause dll_open
                // to search on the library paths.
                mstr_assign(&csfle_cand_filepath, mstr_copy(csfle_filename));
            } else {
                // Compose the candidate filepath:
                mstr_assign(&csfle_cand_filepath, mpath_join(cand_dir, csfle_filename, MPATH_NATIVE));
                if (!_try_replace_dollar_origin(&csfle_cand_filepath, &crypt->log)) {
                    // Error while substituting $ORIGIN
                    continue;
                }
            }
            // Try to load the file:
            candidate_csfle = _try_load_csfle(csfle_cand_filepath.raw.data, NULL /* status */, &crypt->log);
            if (candidate_csfle.okay) {
                // Stop searching:
                break;
            }
        }
    }

    mstr_free(csfle_cand_filepath);
    return candidate_csfle;
}

/// Global state for the application's csfle library
typedef struct csfle_global_lib_state {
    /// Synchronization around the reference count:
    mongocrypt_mutex_t mtx;
    int refcount;
    /// The open library handle:
    mcr_dll dll;
    /// vtable for the APIs:
    _mongo_crypt_v1_vtable vtable;
    /// The global library state managed by the csfle library:
    mongo_crypt_v1_lib *csfle_lib;
} csfle_global_lib_state;

static csfle_global_lib_state g_csfle_state;

static void init_csfle_state(void) {
    _mongocrypt_mutex_init(&g_csfle_state.mtx);
}

static mlib_once_flag g_csfle_init_flag = MLIB_ONCE_INITIALIZER;

/**
 * @brief Verify that `found` refers to the same library that is globally loaded
 * for the application.
 *
 * @param crypt The requesting mongocrypt_t. Error information may be set
 * through here.
 * @param found The result of _try_load_csfle()
 * @return true If `found` matches the global state
 * @return false Otherwise
 *
 * @note This function assumes that the global csfle state is valid and will not
 * be destroyed by any other thread. (One must hold the reference count >= 1)
 */
static bool _validate_csfle_singleton(mongocrypt_t *crypt, _loaded_csfle found) {
    mongocrypt_status_t *status;

    BSON_ASSERT_PARAM(crypt);

    if (!mcr_dll_path_supported()) {
        _mongocrypt_log(&crypt->log,
                        MONGOCRYPT_LOG_LEVEL_WARNING,
                        "Cannot get path of loaded library on this platform. Skipping validation to ensure "
                        "exactly one csfle library is loaded.");
        return true;
    }

    status = crypt->status;

    // Path to the existing loaded csfle:
    mcr_dll_path_result existing_path_ = mcr_dll_path(g_csfle_state.dll);
    assert(existing_path_.path.raw.data && "Failed to get path to already-loaded csfle library");
    mstr_view existing_path = existing_path_.path.view;
    bool okay = true;
    if (!found.okay) {
        // There is one loaded, but we failed to find that same library. Error:
        CLIENT_ERR("An existing crypt_shared library is loaded by the application at "
                   "[%s], but the current call to mongocrypt_init() failed to "
                   "find that same library.",
                   existing_path.data);
        okay = false;
    } else {
        // Get the path to what we found:
        mcr_dll_path_result found_path = mcr_dll_path(found.lib);
        assert(found_path.path.raw.data
               && "Failed to get the dynamic library filepath of the library that "
                  "was loaded for csfle");
        if (!mstr_eq(found_path.path.view, existing_path)) {
            // Our find-result should only ever find the existing same library.
            // Error:
            CLIENT_ERR("An existing crypt_shared library is loaded by the application at [%s], "
                       "but the current call to mongocrypt_init() attempted to load a "
                       "second crypt_shared library from [%s]. This is not allowed.",
                       existing_path.data,
                       found_path.path.raw.data);
            okay = false;
        }
        mstr_free(found_path.path);
        mstr_free(found_path.error_string);
    }

    mstr_free(existing_path_.path);
    mstr_free(existing_path_.error_string);
    return okay;
}

/**
 * @brief Drop a reference count to the global csfle loaded library.
 *
 * This should be called as part of mongocrypt_t destruction following a
 * successful loading of csfle.
 */
static void _csfle_drop_global_ref(void) {
    mlib_call_once(&g_csfle_init_flag, init_csfle_state);

    MONGOCRYPT_WITH_MUTEX(g_csfle_state.mtx) {
        assert(g_csfle_state.refcount > 0);
        int new_rc = --g_csfle_state.refcount;
        if (new_rc == 0) {
            mongo_crypt_v1_status *status = g_csfle_state.vtable.status_create();
            const int destroy_rc = g_csfle_state.vtable.lib_destroy(g_csfle_state.csfle_lib, status);
            if (destroy_rc != MONGO_CRYPT_V1_SUCCESS && status) {
                fprintf(stderr,
                        "csfle lib_destroy() failed: %s [Error %d, code %d]\n",
                        g_csfle_state.vtable.status_get_explanation(status),
                        g_csfle_state.vtable.status_get_error(status),
                        g_csfle_state.vtable.status_get_code(status));
            }
            g_csfle_state.vtable.status_destroy(status);
#ifndef __linux__
            mcr_dll_close(g_csfle_state.dll);
#else
            /// NOTE: On Linux, skip closing the CSFLE library itself, since a bug in
            /// the way ld-linux and GCC interact causes static destructors to not run
            /// during dlclose(). Still, free the error string:
            ///
            /// Please see: https://jira.mongodb.org/browse/SERVER-63710
            mstr_free(g_csfle_state.dll.error_string);
#endif
        }
    }
}

/**
 * @brief Following a call to _try_find_csfle, reconcile the result with the
 * current application-global csfle status.
 *
 * csfle contains global state that can only be loaded once for the entire
 * application. For this reason, there is a global object that manages the
 * loaded library. Attempts to create more than one mongocrypt_t that all
 * request csfle requires that all instances attempt to open the same csfle
 * library.
 *
 * This function checks if there is already a csfle loaded for the process. If
 * there is, we validate that the given find-result found the same library
 * that is already loaded. If not, then this function sets an error and returns
 * `false`.
 *
 * If there was no prior loaded csfle and the find-result indicates that it
 * found the library, this function will store the find-result in the global
 * state for later calls to mongocrypt_init() that request csfle.
 *
 * This function performs reference counting on the global state. Following a
 * successful call to this function (i.e. it returns `true`), one must have a
 * corresponding call to _csfle_drop_global_ref(), which will release the
 * resources acquired by this function.
 *
 * @param crypt The requesting mongocrypt_t instance. An error may be set
 * through this object.
 * @param found The result of _try_find_csfle().
 * @return true Upon success AND `found->okay`
 * @return false Otherwise.
 *
 * @note If there was no prior global state loaded, this function will steal
 * the library referenced by `found`. The caller should release `found->lib`
 * regardless.
 */
static bool _csfle_replace_or_take_validate_singleton(mongocrypt_t *crypt, _loaded_csfle *found) {
    mlib_call_once(&g_csfle_init_flag, init_csfle_state);

    // If we have a loaded library, create a csfle_status object to use with
    // lib_create
    mongo_crypt_v1_status *csfle_status = NULL;

    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(found);

    if (found->okay) {
        // Create the status. Note that this may fail, so do not assume
        // csfle_status is non-null.
        csfle_status = found->vtable.status_create();
    }

    /**
     * Atomically:
     *
     * 1. If there is an existing global library, increment its reference count.
     * 2. Otherwise, if we have successfully loaded a new csfle, replace the
     *    global library and set its reference count to 1.
     * 3. Otherwise, do nothing.
     */
    enum {
        TOOK_REFERENCE,
        DID_NOTHING,
        REPLACED_GLOBAL,
        LIB_CREATE_FAILED,
    } action;

    MONGOCRYPT_WITH_MUTEX(g_csfle_state.mtx) {
        if (g_csfle_state.refcount) {
            // Increment the refcount to prevent the global csfle library from
            // disappearing
            ++g_csfle_state.refcount;
            action = TOOK_REFERENCE;
        } else if (found->okay) {
            // We have found csfle, and no one else is holding one. Our result will
            // now become the global result.
            // Create the single csfle_lib object for the application:
            mongo_crypt_v1_lib *csfle_lib = found->vtable.lib_create(csfle_status);
            if (csfle_lib == NULL) {
                // Creation failed:
                action = LIB_CREATE_FAILED;
            } else {
                // Creation succeeded: Store the result:
                g_csfle_state.dll = found->lib;
                g_csfle_state.vtable = found->vtable;
                g_csfle_state.csfle_lib = csfle_lib;
                g_csfle_state.refcount = 1;
                action = REPLACED_GLOBAL;
            }
        } else {
            // We failed to load the library, and no one else has one either.
            // Nothing to do.
            action = DID_NOTHING;
        }
    }

    // Get the possible failure status information.
    mstr message = MSTR_NULL;
    int err = 0;
    int code = 0;
    if (csfle_status) {
        assert(found->okay);
        message = mstr_copy_cstr(found->vtable.status_get_explanation(csfle_status));
        err = found->vtable.status_get_error(csfle_status);
        code = found->vtable.status_get_code(csfle_status);
        found->vtable.status_destroy(csfle_status);
    }

    bool have_csfle = true;
    switch (action) {
    case TOOK_REFERENCE: {
        const bool is_valid = _validate_csfle_singleton(crypt, *found);
        if (!is_valid) {
            //  We've failed validation, so we're not going to continue to
            //  reference the global instance it. Drop it now:
            _csfle_drop_global_ref();
        }
        have_csfle = is_valid;
        break;
    }
    case REPLACED_GLOBAL:
        // Reset the library in the caller so they can't unload the DLL. The DLL
        // is now managed in the global variable.
        found->lib = MCR_DLL_NULL;
        have_csfle = true;
        break;
    case LIB_CREATE_FAILED:
        if (!message.raw.data) {
            // We failed to obtain a message about the failure
            _mongocrypt_set_error(crypt->status,
                                  MONGOCRYPT_STATUS_ERROR_CRYPT_SHARED,
                                  MONGOCRYPT_GENERIC_ERROR_CODE,
                                  "csfle lib_create() failed");
        } else {
            // Record the message, error, and code from csfle about the failure
            _mongocrypt_set_error(crypt->status,
                                  MONGOCRYPT_STATUS_ERROR_CRYPT_SHARED,
                                  MONGOCRYPT_GENERIC_ERROR_CODE,
                                  "csfle lib_create() failed: %s [Error %d, code %d]",
                                  message.raw.data,
                                  err,
                                  code);
        }
        have_csfle = false;
        break;
    case DID_NOTHING:
    default: have_csfle = false; break;
    }

    mstr_free(message);
    return have_csfle;
}

/**
 * @return true If the given mongocrypt wants csfle
 * @return false Otherwise
 *
 * @note "Requesting csfle" means that it has set at least one search path OR
 * has set the override path
 */
static bool _wants_csfle(mongocrypt_t *c) {
    BSON_ASSERT_PARAM(c);

    if (c->opts.bypass_query_analysis) {
        return false;
    }
    return c->opts.n_crypt_shared_lib_search_paths != 0 || c->opts.crypt_shared_lib_override_path.raw.data != NULL;
}

/**
 * @brief Try to enable csfle for the given mongocrypt
 *
 * @param crypt The crypt object for which we should enable csfle
 * @return true If no errors occurred
 * @return false Otherwise
 *
 * @note Returns `true` even if loading fails to find the csfle library on the
 * requested paths. `false` is only for hard-errors, which includes failure to
 * load from the override path.
 */
static bool _try_enable_csfle(mongocrypt_t *crypt) {
    mongocrypt_status_t *status;
    _loaded_csfle found;

    BSON_ASSERT_PARAM(crypt);

    found = _try_find_csfle(crypt);

    status = crypt->status;

    // If a crypt_shared override path was specified, but we did not succeed in
    // loading crypt_shared, that is a hard-error.
    if (crypt->opts.crypt_shared_lib_override_path.raw.data && !found.okay) {
        // Wrap error with additional information.
        CLIENT_ERR("A crypt_shared override path was specified [%s], but we failed to open a dynamic "
                   "library at that location. Load error: [%s]",
                   crypt->opts.crypt_shared_lib_override_path.raw.data,
                   mongocrypt_status_message(crypt->status, NULL /* len */));
        return false;
    }

    // Attempt to validate the try-find result against the global state:
    const bool got_csfle = _csfle_replace_or_take_validate_singleton(crypt, &found);
    // Close the lib we found (may have been stolen in validate_singleton())
    mcr_dll_close(found.lib);

    if (got_csfle) {
        crypt->csfle = g_csfle_state.vtable;
        crypt->csfle_lib = g_csfle_state.csfle_lib;
    }
    // In cast of failure, validate_singleton() will set a non-ok status.
    return mongocrypt_status_type(status) == MONGOCRYPT_STATUS_OK;
}

bool mongocrypt_init(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    mongocrypt_status_t *status = crypt->status;
    if (crypt->initialized) {
        CLIENT_ERR("already initialized");
        return false;
    }

    crypt->initialized = true;

    if (!mongocrypt_status_ok(crypt->status)) {
        return false;
    }

    if (!_mongocrypt_opts_validate(&crypt->opts, status)) {
        return false;
    }

    if (crypt->opts.log_fn) {
        _mongocrypt_log_set_fn(&crypt->log, crypt->opts.log_fn, crypt->opts.log_ctx);
    }

    if (!crypt->crypto) {
#ifndef MONGOCRYPT_ENABLE_CRYPTO
        CLIENT_ERR("libmongocrypt built with native crypto disabled. crypto "
                   "hooks required");
        return false;
#else
        /* set default hooks. */
        crypt->crypto = bson_malloc0(sizeof(*crypt->crypto));
        BSON_ASSERT(crypt->crypto);
#endif
    }

    if (!_wants_csfle(crypt)) {
        // User does not want csfle. Just succeed.
        return true;
    }

    return _try_enable_csfle(crypt);
}

bool mongocrypt_is_crypto_available(void) {
#ifdef MONGOCRYPT_ENABLE_CRYPTO
    return true;
#else
    return false;
#endif
}

bool mongocrypt_status(mongocrypt_t *crypt, mongocrypt_status_t *out) {
    BSON_ASSERT_PARAM(crypt);

    if (!out) {
        mongocrypt_status_t *status = crypt->status;
        CLIENT_ERR("argument 'out' is required");
        return false;
    }

    if (!mongocrypt_status_ok(crypt->status)) {
        _mongocrypt_status_copy_to(crypt->status, out);
        return false;
    }
    _mongocrypt_status_reset(out);
    return true;
}

void mongocrypt_destroy(mongocrypt_t *crypt) {
    if (!crypt) {
        return;
    }
    _mongocrypt_opts_cleanup(&crypt->opts);
    _mongocrypt_cache_cleanup(&crypt->cache_collinfo);
    _mongocrypt_cache_cleanup(&crypt->cache_key);
    _mongocrypt_mutex_cleanup(&crypt->mutex);
    _mongocrypt_log_cleanup(&crypt->log);
    mongocrypt_status_destroy(crypt->status);
    bson_free(crypt->crypto);
    mc_mapof_kmsid_to_token_destroy(crypt->cache_oauth);

    if (crypt->csfle.okay) {
        _csfle_drop_global_ref();
        crypt->csfle.okay = false;
    }

    bson_free(crypt);
}

const char *mongocrypt_crypt_shared_lib_version_string(const mongocrypt_t *crypt, uint32_t *len) {
    BSON_ASSERT_PARAM(crypt);

    if (!crypt->csfle.okay) {
        if (len) {
            *len = 0;
        }
        return NULL;
    }
    const char *version = crypt->csfle.get_version_str();
    if (len) {
        *len = (uint32_t)(strlen(version));
    }
    return version;
}

uint64_t mongocrypt_crypt_shared_lib_version(const mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    if (!crypt->csfle.okay) {
        return 0;
    }
    return crypt->csfle.get_version();
}

bool _mongocrypt_validate_and_copy_string(const char *in, int32_t in_len, char **out) {
    BSON_ASSERT_PARAM(out);

    if (!in || in_len < -1) {
        return false;
    }

    const size_t len = in_len < 0 ? strlen(in) : (size_t)in_len;

    if (!bson_utf8_validate(in, len, false)) {
        return false;
    }
    *out = bson_strndup(in, len);
    return true;
}

bool mongocrypt_setopt_crypto_hooks(mongocrypt_t *crypt,
                                    mongocrypt_crypto_fn aes_256_cbc_encrypt,
                                    mongocrypt_crypto_fn aes_256_cbc_decrypt,
                                    mongocrypt_random_fn random,
                                    mongocrypt_hmac_fn hmac_sha_512,
                                    mongocrypt_hmac_fn hmac_sha_256,
                                    mongocrypt_hash_fn sha_256,
                                    void *ctx) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    mongocrypt_status_t *status = crypt->status;

    if (!crypt->crypto) {
        crypt->crypto = bson_malloc0(sizeof(*crypt->crypto));
        BSON_ASSERT(crypt->crypto);
    }

    crypt->crypto->hooks_enabled = true;
    crypt->crypto->ctx = ctx;

    if (!aes_256_cbc_encrypt) {
        CLIENT_ERR("aes_256_cbc_encrypt not set");
        return false;
    }
    crypt->crypto->aes_256_cbc_encrypt = aes_256_cbc_encrypt;

    if (!aes_256_cbc_decrypt) {
        CLIENT_ERR("aes_256_cbc_decrypt not set");
        return false;
    }
    crypt->crypto->aes_256_cbc_decrypt = aes_256_cbc_decrypt;

    if (!random) {
        CLIENT_ERR("random not set");
        return false;
    }
    crypt->crypto->random = random;

    if (!hmac_sha_512) {
        CLIENT_ERR("hmac_sha_512 not set");
        return false;
    }
    crypt->crypto->hmac_sha_512 = hmac_sha_512;

    if (!hmac_sha_256) {
        CLIENT_ERR("hmac_sha_256 not set");
        return false;
    }
    crypt->crypto->hmac_sha_256 = hmac_sha_256;

    if (!sha_256) {
        CLIENT_ERR("sha_256 not set");
        return false;
    }
    crypt->crypto->sha_256 = sha_256;

    return true;
}

bool mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(mongocrypt_t *crypt,
                                                         mongocrypt_hmac_fn sign_rsaes_pkcs1_v1_5,
                                                         void *sign_ctx) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    if (crypt->opts.sign_rsaes_pkcs1_v1_5) {
        mongocrypt_status_t *status = crypt->status;
        CLIENT_ERR("signature hook already set");
        return false;
    }

    crypt->opts.sign_rsaes_pkcs1_v1_5 = sign_rsaes_pkcs1_v1_5;
    crypt->opts.sign_ctx = sign_ctx;
    return true;
}

bool mongocrypt_setopt_aes_256_ctr(mongocrypt_t *crypt,
                                   mongocrypt_crypto_fn aes_256_ctr_encrypt,
                                   mongocrypt_crypto_fn aes_256_ctr_decrypt,
                                   void *ctx) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    mongocrypt_status_t *status = crypt->status;

    if (!crypt->crypto) {
        crypt->crypto = bson_malloc0(sizeof(*crypt->crypto));
        BSON_ASSERT(crypt->crypto);
    }

    if (!aes_256_ctr_encrypt) {
        CLIENT_ERR("aes_256_ctr_encrypt not set");
        return false;
    }

    if (!aes_256_ctr_decrypt) {
        CLIENT_ERR("aes_256_ctr_decrypt not set");
        return false;
    }

    crypt->crypto->aes_256_ctr_encrypt = aes_256_ctr_encrypt;
    crypt->crypto->aes_256_ctr_decrypt = aes_256_ctr_decrypt;

    return true;
}

bool mongocrypt_setopt_aes_256_ecb(mongocrypt_t *crypt, mongocrypt_crypto_fn aes_256_ecb_encrypt, void *ctx) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);

    if (!crypt->crypto) {
        crypt->crypto = bson_malloc0(sizeof(*crypt->crypto));
        BSON_ASSERT(crypt->crypto);
    }

    if (!aes_256_ecb_encrypt) {
        mongocrypt_status_t *status = crypt->status;
        CLIENT_ERR("aes_256_ecb_encrypt not set");
        return false;
    }

    crypt->crypto->aes_256_ecb_encrypt = aes_256_ecb_encrypt;

    return true;
}

bool mongocrypt_setopt_kms_providers(mongocrypt_t *crypt, mongocrypt_binary_t *kms_providers_definition) {
    ASSERT_MONGOCRYPT_PARAM_UNINIT(crypt);
    BSON_ASSERT_PARAM(kms_providers_definition);

    return _mongocrypt_parse_kms_providers(kms_providers_definition,
                                           &crypt->opts.kms_providers,
                                           crypt->status,
                                           &crypt->log);
}

void mongocrypt_setopt_append_crypt_shared_lib_search_path(mongocrypt_t *crypt, const char *path) {
    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(path);

    // Dup the path string for us to manage
    mstr pathdup = mstr_copy_cstr(path);
    // Increase array len
    BSON_ASSERT(crypt->opts.n_crypt_shared_lib_search_paths < INT_MAX);
    const int new_len = crypt->opts.n_crypt_shared_lib_search_paths + 1;
    BSON_ASSERT(new_len > 0 && sizeof(mstr) <= SIZE_MAX / (size_t)new_len);
    mstr *const new_array = bson_realloc(crypt->opts.crypt_shared_lib_search_paths, sizeof(mstr) * (size_t)new_len);

    // Store the path
    new_array[new_len - 1] = pathdup;
    // Write back opts
    crypt->opts.crypt_shared_lib_search_paths = new_array;
    crypt->opts.n_crypt_shared_lib_search_paths = new_len;
}

void mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    crypt->opts.use_need_kms_credentials_state = true;
}

void mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    crypt->opts.use_need_mongo_collinfo_with_db_state = true;
}

void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t *crypt, const char *path) {
    BSON_ASSERT_PARAM(crypt);
    BSON_ASSERT_PARAM(path);

    mstr_assign(&crypt->opts.crypt_shared_lib_override_path, mstr_copy_cstr(path));
}

bool _mongocrypt_needs_credentials(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    if (!crypt->opts.use_need_kms_credentials_state) {
        return false;
    }

    return crypt->opts.kms_providers.need_credentials != 0;
}

bool _mongocrypt_needs_credentials_for_provider(mongocrypt_t *crypt,
                                                _mongocrypt_kms_provider_t provider,
                                                const char *name) {
    BSON_ASSERT_PARAM(crypt);

    if (name != NULL) {
        // Named KMS providers do not support on-demand credentials.
        return false;
    }

    if (!crypt->opts.use_need_kms_credentials_state) {
        return false;
    }

    return (crypt->opts.kms_providers.need_credentials & (int)provider) != 0;
}

/* Given a string, populate a bson_value_t for that string */
void _bson_value_from_string(const char *string, bson_value_t *value) {
    bson_t *bson;
    bson_iter_t iter;

    bson = BCON_NEW("key", string);
    BSON_ASSERT(bson_iter_init_find(&iter, bson, "key"));
    bson_value_copy(bson_iter_value(&iter), value);

    bson_destroy(bson);
}

void mongocrypt_setopt_bypass_query_analysis(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);

    crypt->opts.bypass_query_analysis = true;
}
libmongocrypt-1.19.0/src/mongocrypt.h000066400000000000000000001665571521103432300176510ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef MONGOCRYPT_H
#define MONGOCRYPT_H

/** @file mongocrypt.h The top-level handle to libmongocrypt. */

/**
 * @mainpage libmongocrypt
 * See all public API documentation in: @ref mongocrypt.h
 */

#include "mongocrypt-compat.h"
#include "mongocrypt-export.h"

/* clang-format off */
#ifndef __has_include
   #include "mongocrypt-config.h"
#else
   #if __has_include("mongocrypt-config.h")
      #include "mongocrypt-config.h"
   #else
      #error No "mongocrypt-config.h" header is available. That file must \
             be generated in order to use libmongocrypt.
   #endif
#endif
/* clang-format on */

/**
 * Returns the version string for libmongocrypt.
 *
 * @param[out] len  An optional length of the returned string. May be NULL.
 * @returns a NULL terminated version string for libmongocrypt.
 */
MONGOCRYPT_EXPORT
const char *mongocrypt_version(uint32_t *len);

/**
 * Returns true if libmongocrypt was built with native crypto support.
 *
 * If libmongocrypt was not built with native crypto support, setting crypto
 * hooks is required.
 *
 * @returns True if libmongocrypt was built with native crypto support.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_is_crypto_available(void);

/**
 * A non-owning view of a byte buffer.
 *
 * When constructing a 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 mongocrypt_binary_t as an argument will make a copy of
 * the viewed data. For example, the following is valid:
 *
 * @code{.c}
 * mongocrypt_binary_t bin = mongocrypt_binary_new_from_data(mydata, mylen);
 * assert (mongocrypt_setopt_kms_provider_local (crypt), bin);
 * // The viewed data of bin has been copied. Ok to free the view and the data.
 * mongocrypt_binary_destroy (bin);
 * my_free_fn (mydata);
 * @endcode
 *
 * Functions with a mongocrypt_binary_t* out guarantee the lifetime of the
 * viewed data to live as long as the parent object. For example, @ref
 * mongocrypt_ctx_mongo_op guarantees that the viewed data of
 * mongocrypt_binary_t is valid until the parent ctx is destroyed with @ref
 * mongocrypt_ctx_destroy.
 *
 * The `mongocrypt_binary_t` struct definition is public.
 * Consumers may rely on the struct layout.
 */
typedef struct _mongocrypt_binary_t {
    void *data;
    uint32_t len;
} mongocrypt_binary_t;

/**
 * Create a new non-owning view of a buffer (data + length).
 *
 * Use this to create a mongocrypt_binary_t used for output parameters.
 *
 * @returns A new mongocrypt_binary_t.
 */
MONGOCRYPT_EXPORT
mongocrypt_binary_t *mongocrypt_binary_new(void);

/**
 * Create a new non-owning view of a buffer (data + length).
 *
 * @param[in] data A pointer to an array of bytes. This data is not copied. @p
 * data must outlive the binary object.
 * @param[in] len The length of the @p data byte array.
 *
 * @returns A new @ref mongocrypt_binary_t.
 */
MONGOCRYPT_EXPORT
mongocrypt_binary_t *mongocrypt_binary_new_from_data(uint8_t *data, uint32_t len);

/**
 * Get a pointer to the viewed data.
 *
 * @param[in] binary The @ref mongocrypt_binary_t.
 *
 * @returns A pointer to the viewed data.
 */
MONGOCRYPT_EXPORT
uint8_t *mongocrypt_binary_data(const mongocrypt_binary_t *binary);

/**
 * Get the length of the viewed data.
 *
 * @param[in] binary The @ref mongocrypt_binary_t.
 *
 * @returns The length of the viewed data.
 */
MONGOCRYPT_EXPORT
uint32_t mongocrypt_binary_len(const mongocrypt_binary_t *binary);

/**
 * Free the @ref mongocrypt_binary_t.
 *
 * This does not free the viewed data.
 *
 * @param[in] binary The mongocrypt_binary_t destroy.
 */
MONGOCRYPT_EXPORT
void mongocrypt_binary_destroy(mongocrypt_binary_t *binary);

/**
 * Indicates success or contains error information.
 *
 * Functions like @ref mongocrypt_ctx_encrypt_init follow a pattern to expose a
 * status. A boolean is returned. True indicates success, and false indicates
 * failure. On failure a status on the handle is set, and is accessible with a
 * corresponding (handle)_status function. E.g. @ref mongocrypt_ctx_status.
 */
typedef struct _mongocrypt_status_t mongocrypt_status_t;

/**
 * Indicates the type of error.
 */
typedef enum {
    MONGOCRYPT_STATUS_OK = 0,
    MONGOCRYPT_STATUS_ERROR_CLIENT = 1,
    MONGOCRYPT_STATUS_ERROR_KMS = 2,
    MONGOCRYPT_STATUS_ERROR_CRYPT_SHARED = 3,
} mongocrypt_status_type_t;

/**
 * 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.
 *
 * @returns A new status object.
 */
MONGOCRYPT_EXPORT
mongocrypt_status_t *mongocrypt_status_new(void);

/**
 * Set a status object with message, type, and code.
 *
 * Use this to set the @ref mongocrypt_status_t given in the crypto hooks.
 *
 * @param[in] type The status type.
 * @param[in] code The status code.
 * @param[in] message The message.
 * @param[in] message_len Due to historical behavior, pass 1 + the string length
 * of @p 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.
 *
 */
MONGOCRYPT_EXPORT
void mongocrypt_status_set(mongocrypt_status_t *status,
                           mongocrypt_status_type_t type,
                           uint32_t code,
                           const char *message,
                           int32_t message_len);

/**
 * Indicates success or the type of error.
 *
 * @param[in] status The status object.
 *
 * @returns A @ref mongocrypt_status_type_t.
 */
MONGOCRYPT_EXPORT
mongocrypt_status_type_t mongocrypt_status_type(mongocrypt_status_t *status);

/**
 * Get an error code or 0.
 *
 * @param[in] status The status object.
 *
 * @returns An error code.
 */
MONGOCRYPT_EXPORT
uint32_t mongocrypt_status_code(mongocrypt_status_t *status);

/**
 * Get the error message associated with a status or NULL.
 *
 * @param[in] status The status object.
 * @param[out] len An optional length of the returned string (excluding the
 * trailing NULL byte). May be NULL.
 *
 * @returns A NULL terminated error message or NULL.
 */
MONGOCRYPT_EXPORT
const char *mongocrypt_status_message(mongocrypt_status_t *status, uint32_t *len);

/**
 * Returns true if the status indicates success.
 *
 * @param[in] status The status to check.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_status_ok(mongocrypt_status_t *status);

/**
 * Free the memory for a status object.
 *
 * @param[in] status The status to destroy.
 */
MONGOCRYPT_EXPORT
void mongocrypt_status_destroy(mongocrypt_status_t *status);

/**
 * Indicates the type of log message.
 */
typedef enum {
    MONGOCRYPT_LOG_LEVEL_FATAL = 0,
    MONGOCRYPT_LOG_LEVEL_ERROR = 1,
    MONGOCRYPT_LOG_LEVEL_WARNING = 2,
    MONGOCRYPT_LOG_LEVEL_INFO = 3,
    MONGOCRYPT_LOG_LEVEL_TRACE = 4
} mongocrypt_log_level_t;

/**
 * A log callback function. Set a custom log callback with @ref
 * mongocrypt_setopt_log_handler.
 *
 * @param[in] message A NULL terminated message.
 * @param[in] message_len The length of message.
 * @param[in] ctx A context provided by the caller of @ref
 * mongocrypt_setopt_log_handler.
 */
typedef void (*mongocrypt_log_fn_t)(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx);

/**
 * The top-level handle to libmongocrypt.
 *
 * Create a mongocrypt_t handle to perform operations within libmongocrypt:
 * encryption, decryption, registering log callbacks, etc.
 *
 * Functions on a mongocrypt_t are thread safe, though functions on derived
 * 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.
 */
typedef struct _mongocrypt_t mongocrypt_t;

/**
 * Allocate a new @ref mongocrypt_t object.
 *
 * Set options using mongocrypt_setopt_* functions, then initialize with @ref
 * mongocrypt_init. When done with the @ref mongocrypt_t, free with @ref
 * mongocrypt_destroy.
 *
 * @returns A new @ref mongocrypt_t object.
 */
MONGOCRYPT_EXPORT
mongocrypt_t *mongocrypt_new(void);

/**
 * Set a handler on the @ref mongocrypt_t object to get called on every log
 * message.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] log_fn The log callback.
 * @param[in] log_ctx A context passed as an argument to the log callback every
 * invocation.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_log_handler(mongocrypt_t *crypt, mongocrypt_log_fn_t log_fn, void *log_ctx);

/**
 * Enable or disable KMS retry behavior.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] enable A boolean indicating whether to retry operations.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_retry_kms(mongocrypt_t *crypt, bool enable);

/**
 * Enable support for multiple collection schemas. Required to support $lookup.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_enable_multiple_collinfo(mongocrypt_t *crypt);

/**
 * Configure an AWS KMS provider on the @ref mongocrypt_t object.
 *
 * This has been superseded by the more flexible:
 * @ref mongocrypt_setopt_kms_providers
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] aws_access_key_id The AWS access key ID used to generate KMS
 * messages.
 * @param[in] 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[in] aws_secret_access_key The AWS secret access key used to generate
 * KMS messages.
 * @param[in] 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).
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_kms_provider_aws(mongocrypt_t *crypt,
                                        const char *aws_access_key_id,
                                        int32_t aws_access_key_id_len,
                                        const char *aws_secret_access_key,
                                        int32_t aws_secret_access_key_len);

/**
 * Configure a local KMS provider on the @ref mongocrypt_t object.
 *
 * This has been superseded by the more flexible:
 * @ref mongocrypt_setopt_kms_providers
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] key A 96 byte master key used to encrypt and decrypt key vault
 * keys. The viewed data is copied. It is valid to destroy @p key with @ref
 * mongocrypt_binary_destroy immediately after.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_kms_provider_local(mongocrypt_t *crypt, mongocrypt_binary_t *key);

/**
 * Configure KMS providers with a BSON document.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] kms_providers A BSON document mapping the KMS provider names
 * to credentials. Set a KMS provider value to an empty document to supply
 * credentials on-demand with @ref mongocrypt_ctx_provide_kms_providers.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_kms_providers(mongocrypt_t *crypt, mongocrypt_binary_t *kms_providers);

/**
 * Set a local schema map for encryption.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] schema_map A BSON document representing the schema map supplied by
 * the user. The keys are collection namespaces and values are JSON schemas. The
 * viewed data copied. It is valid to destroy @p schema_map with @ref
 * mongocrypt_binary_destroy immediately after.
 * @pre @p crypt has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_schema_map(mongocrypt_t *crypt, mongocrypt_binary_t *schema_map);

/**
 * Set a local EncryptedFieldConfigMap for encryption.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] efc_map 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.
 * @pre @p crypt has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_encrypted_field_config_map(mongocrypt_t *crypt, mongocrypt_binary_t *efc_map);

/**
 * @brief Append an additional search directory to the search path for loading
 * the crypt_shared dynamic library.
 *
 * @param[in] crypt The @ref mongocrypt_t object to update
 * @param[in] 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.
 *
 * @warning Use of "$SYSTEM" will search system directories for the
 * mongo_crypt_v1.(dll,so,dylib) following the search behavior of LoadLibrary
 * on Windows and dlopen on Unix. Ensure secure deployment of the library.
 *
 * @note 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.
 *
 * @note 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.
 *
 * @note 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.
 */
MONGOCRYPT_EXPORT
void mongocrypt_setopt_append_crypt_shared_lib_search_path(mongocrypt_t *crypt, const char *path);

/**
 * @brief Set a single override path for loading the crypt_shared dynamic
 * library.
 *
 * @param[in] crypt The @ref mongocrypt_t object to update
 * @param[in] 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.
 *
 * @note This function will do no IO nor path validation. All validation will
 * occur during the call to @ref mongocrypt_init.
 *
 * @note 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.
 *
 * @note 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.
 */
MONGOCRYPT_EXPORT
void mongocrypt_setopt_set_crypt_shared_lib_path_override(mongocrypt_t *crypt, const char *path);

/**
 * @brief Opt-into handling the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state.
 *
 * If set, before entering the MONGOCRYPT_CTX_NEED_KMS state,
 * contexts may enter the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state
 * and then wait for credentials to be supplied through
 * @ref mongocrypt_ctx_provide_kms_providers.
 *
 * A context will only enter MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS
 * if an empty document was set for a KMS provider in @ref
 * mongocrypt_setopt_kms_providers.
 *
 * @param[in] crypt The @ref mongocrypt_t object to update
 */
MONGOCRYPT_EXPORT
void mongocrypt_setopt_use_need_kms_credentials_state(mongocrypt_t *crypt);

/**
 * @brief Opt-into handling the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state.
 *
 * A context enters the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state when
 * processing a `bulkWrite` command. The target database of the `bulkWrite` may differ from the command database
 * ("admin").
 *
 * @param[in] crypt The @ref mongocrypt_t object to update
 */
MONGOCRYPT_EXPORT
void mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(mongocrypt_t *crypt);

/**
 * Initialize new @ref mongocrypt_t object.
 *
 * Set options before using @ref mongocrypt_setopt_kms_provider_local, @ref
 * mongocrypt_setopt_kms_provider_aws, or @ref mongocrypt_setopt_log_handler.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status Failure may occur if previously
 * set
 * options are invalid.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_init(mongocrypt_t *crypt);

/**
 * Get the status associated with a @ref mongocrypt_t object.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[out] status Receives the status.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_status(mongocrypt_t *crypt, mongocrypt_status_t *status);

/**
 * Destroy the @ref mongocrypt_t object.
 *
 * @param[in] crypt The @ref mongocrypt_t object to destroy.
 */
MONGOCRYPT_EXPORT
void mongocrypt_destroy(mongocrypt_t *crypt);

/**
 * Obtain a nul-terminated version string of the loaded crypt_shared dynamic
 * library, if available.
 *
 * If no crypt_shared was successfully loaded, this function returns NULL.
 *
 * @param[in] crypt The mongocrypt_t object after a successful call to
 * mongocrypt_init.
 * @param[out] len An optional output parameter to which the length of the
 * returned string is written. If provided and no crypt_shared library was
 * loaded, zero is written to *len.
 *
 * @return A nul-terminated string of the dynamically loaded crypt_shared
 * library.
 *
 * @note For a numeric value that can be compared against, use
 * @ref mongocrypt_crypt_shared_lib_version.
 */
MONGOCRYPT_EXPORT
const char *mongocrypt_crypt_shared_lib_version_string(const mongocrypt_t *crypt, uint32_t *len);

/**
 * @brief Obtain a 64-bit constant encoding the version of the loaded
 * crypt_shared library, if available.
 *
 * @param[in] crypt The mongocrypt_t object after a successful call to
 * mongocrypt_init.
 *
 * @return A 64-bit encoded version number, with the version encoded as four
 * sixteen-bit integers, or zero if no crypt_shared library was loaded.
 *
 * The version is encoded as four 16-bit numbers, from high to low:
 *
 * - Major version
 * - Minor version
 * - Revision
 * - Reserved
 *
 * For example, version 6.2.1 would be encoded as: 0x0006'0002'0001'0000
 */
MONGOCRYPT_EXPORT
uint64_t mongocrypt_crypt_shared_lib_version(const mongocrypt_t *crypt);

/**
 * Manages the state machine for encryption or decryption.
 */
typedef struct _mongocrypt_ctx_t mongocrypt_ctx_t;

/**
 * Create a new uninitialized @ref mongocrypt_ctx_t.
 *
 * Initialize the context with functions like @ref mongocrypt_ctx_encrypt_init.
 * When done, destroy it with @ref mongocrypt_ctx_destroy.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @returns A new context.
 */
MONGOCRYPT_EXPORT
mongocrypt_ctx_t *mongocrypt_ctx_new(mongocrypt_t *crypt);

/**
 * Get the status associated with a @ref mongocrypt_ctx_t object.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[out] status Receives the status.
 *
 * @returns True if the output is an ok status, false if it is an error
 * status.
 *
 * @see mongocrypt_status_ok
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_status(mongocrypt_ctx_t *ctx, mongocrypt_status_t *status);

/**
 * Set the key id to use for explicit encryption.
 *
 * It is an error to set both this and the key alt name.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] 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
 * @p key_id with @ref mongocrypt_binary_destroy immediately after.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id);

/**
 * Set the keyAltName to use for explicit encryption or
 * data key creation.
 *
 * Pass the binary encoding a BSON document like the following:
 *
 *   { "keyAltName" : (BSON UTF8 value) }
 *
 * For explicit encryption, it is an error to set both the keyAltName
 * and the key id.
 *
 * For creating data keys, call this function repeatedly to set
 * multiple keyAltNames.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] key_alt_name The name to use. The viewed data is copied. It is
 * valid to destroy @p key_alt_name with @ref mongocrypt_binary_destroy
 * immediately after.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_key_alt_name(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_alt_name);

/**
 * Set the keyMaterial to use for encrypting data.
 *
 * Pass the binary encoding of a BSON document like the following:
 *
 *   { "keyMaterial" : (BSON BINARY value) }
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] 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.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool 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-Random"
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] algorithm A string specifying the algorithm to
 * use for encryption.
 * @param[in] len The length of the algorithm string.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorithm, int len);

/// String constant for setopt_algorithm "Deterministic" encryption
#define MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
/// String constant for setopt_algorithm "Random" encryption
#define MONGOCRYPT_ALGORITHM_RANDOM_STR "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
/// String constant for setopt_algorithm "Indexed" explicit encryption
#define MONGOCRYPT_ALGORITHM_INDEXED_STR "Indexed"
/// String constant for setopt_algorithm "Unindexed" explicit encryption
#define MONGOCRYPT_ALGORITHM_UNINDEXED_STR "Unindexed"
// DEPRECATED: support "RangePreview" has been removed in favor of "range".
#define MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR "RangePreview"
#define MONGOCRYPT_ALGORITHM_RANGE_STR "Range"
/// DEPRECATED: "textPreview" has been removed. Use "string".
#define MONGOCRYPT_ALGORITHM_TEXTPREVIEW_DEPRECATED_STR "textPreview"
// String constant for setopt_algorithm "string" explicit encryption.
#define MONGOCRYPT_ALGORITHM_STRING_STR "string"

/**
 * Identify the AWS KMS master key to use for creating a data key.
 *
 * This has been superseded by the more flexible:
 * @ref mongocrypt_ctx_setopt_key_encryption_key
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] region The AWS region.
 * @param[in] region_len The string length of @p region. Pass -1 to determine
 * the string length with strlen (must be NULL terminated).
 * @param[in] cmk The Amazon Resource Name (ARN) of the customer master key
 * (CMK).
 * @param[in] cmk_len The string length of @p cmk_len. Pass -1 to determine the
 * string length with strlen (must be NULL terminated).
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_masterkey_aws(mongocrypt_ctx_t *ctx,
                                         const char *region,
                                         int32_t region_len,
                                         const char *cmk,
                                         int32_t cmk_len);

/**
 * Identify a custom AWS endpoint when creating a data key.
 * This is used internally to construct the correct HTTP request
 * (with the Host header set to this endpoint). This endpoint
 * is persisted in the new data key, and will be returned via
 * @ref mongocrypt_kms_ctx_endpoint.
 *
 * This has been superseded by the more flexible:
 * @ref mongocrypt_ctx_setopt_key_encryption_key
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] endpoint The endpoint.
 * @param[in] endpoint_len The string length of @p endpoint. Pass -1 to
 * determine the string length with strlen (must be NULL terminated).
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_masterkey_aws_endpoint(mongocrypt_ctx_t *ctx, const char *endpoint, int32_t endpoint_len);

/**
 * Set the master key to "local" for creating a data key.
 * This has been superseded by the more flexible:
 * @ref mongocrypt_ctx_setopt_key_encryption_key
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_masterkey_local(mongocrypt_ctx_t *ctx);

/**
 * Set key encryption key document for creating a data key or for rewrapping
 * datakeys.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] bin BSON representing the key encryption key document with
 * an additional "provider" field. The following forms are accepted:
 *
 * AWS
 * {
 *    provider: "aws",
 *    region: ,
 *    key: ,
 *    endpoint: 
 * }
 *
 * Azure
 * {
 *    provider: "azure",
 *    keyVaultEndpoint: ,
 *    keyName: ,
 *    keyVersion: 
 * }
 *
 * GCP
 * {
 *    provider: "gcp",
 *    projectId: ,
 *    location: ,
 *    keyRing: ,
 *    keyName: ,
 *    keyVersion: ,
 *    endpoint: 
 * }
 *
 * Local
 * {
 *    provider: "local"
 * }
 *
 * KMIP
 * {
 *    provider: "kmip",
 *    keyId: 
 *    endpoint: 
 * }
 *
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_key_encryption_key(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *bin);

/**
 * Initialize a context to create a data key.
 *
 * Associated options:
 * - @ref mongocrypt_ctx_setopt_masterkey_aws
 * - @ref mongocrypt_ctx_setopt_masterkey_aws_endpoint
 * - @ref mongocrypt_ctx_setopt_masterkey_local
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 * @pre A master key option has been set, and an associated KMS provider
 * has been set on the parent @ref mongocrypt_t.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_datakey_init(mongocrypt_ctx_t *ctx);

/**
 * Initialize a context for encryption.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] db The database name.
 * @param[in] db_len The byte length of @p db. Pass -1 to determine the string
 * length with strlen (must
 * be NULL terminated).
 * @param[in] cmd The BSON command to be encrypted. The viewed data is copied.
 * It is valid to destroy @p cmd with @ref mongocrypt_binary_destroy immediately
 * after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_encrypt_init(mongocrypt_ctx_t *ctx, const char *db, int32_t db_len, mongocrypt_binary_t *cmd);

/**
 * Explicit helper method to encrypt a single BSON object. Contexts
 * created for explicit encryption will not go through mongocryptd.
 *
 * To specify a key_id, algorithm, or iv to use, please use the
 * corresponding mongocrypt_setopt methods before calling this.
 *
 * This method expects the passed-in BSON to be of the form:
 * { "v" : BSON value to encrypt }
 *
 * The value of "v" is expected to be the BSON value passed to a driver
 * ClientEncryption.encrypt helper.
 *
 * Associated options for FLE 1:
 * - @ref mongocrypt_ctx_setopt_key_id
 * - @ref mongocrypt_ctx_setopt_key_alt_name
 * - @ref mongocrypt_ctx_setopt_algorithm
 *
 * Associated options for Queryable Encryption:
 * - @ref mongocrypt_ctx_setopt_key_id
 * - @ref mongocrypt_ctx_setopt_index_key_id
 * - @ref mongocrypt_ctx_setopt_contention_factor
 * - @ref mongocrypt_ctx_setopt_query_type
 * - @ref mongocrypt_ctx_setopt_algorithm_range
 *
 * An error is returned if FLE 1 and Queryable Encryption incompatible options
 * are set.
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 * @param[in] msg A @ref mongocrypt_binary_t the plaintext BSON value. The
 * viewed data is copied. It is valid to destroy @p msg with @ref
 * mongocrypt_binary_destroy immediately after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool 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".
 *
 * This method expects the passed-in BSON to be of the form:
 * { "v" : FLE2RangeFindDriverSpec }
 *
 * FLE2RangeFindDriverSpec is a BSON document with one of these forms:
 *
 * 1. A Match Expression of this form:
 *    {$and: [{: {: , {: {:  }}]}
 * 2. An Aggregate Expression of this form:
 *    {$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
 *
 * Associated options for Queryable Encryption:
 * - @ref mongocrypt_ctx_setopt_key_id
 * - @ref mongocrypt_ctx_setopt_index_key_id
 * - @ref mongocrypt_ctx_setopt_contention_factor
 * - @ref mongocrypt_ctx_setopt_query_type
 * - @ref mongocrypt_ctx_setopt_algorithm_range
 *
 * An error is returned if FLE 1 and Queryable Encryption incompatible options
 * are set.
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 * @param[in] msg A @ref mongocrypt_binary_t the plaintext BSON value. The
 * viewed data is copied. It is valid to destroy @p msg with @ref
 * mongocrypt_binary_destroy immediately after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_explicit_encrypt_expression_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg);

/**
 * Initialize a context for decryption.
 *
 * This method expects the passed-in BSON to be of the form:
 * { "v" : BSON value to encrypt }
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] doc The document to be decrypted. The viewed data is copied. It is
 * valid to destroy @p doc with @ref mongocrypt_binary_destroy immediately
 * after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *doc);

/**
 * Explicit helper method to decrypt a single BSON object.
 *
 * Pass the binary encoding of a BSON document containing the BSON value to
 * encrypt like the following:
 *
 *   { "v" : (BSON BINARY value of subtype 6) }
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 * @param[in] msg A @ref mongocrypt_binary_t the encrypted BSON. The viewed data
 * is copied. It is valid to destroy @p msg with @ref mongocrypt_binary_destroy
 * immediately after.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_explicit_decrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *msg);

/**
 * @brief Initialize a context to rewrap datakeys.
 *
 * Associated options:
 * - @ref mongocrypt_ctx_setopt_key_encryption_key
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] 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, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_rewrap_many_datakey_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *filter);

/**
 * Indicates the state of the @ref mongocrypt_ctx_t. Each state requires
 * different handling. See [the integration
 * guide](https://github.com/mongodb/libmongocrypt/blob/master/integrating.md#state-machine)
 * for information on what to do for each state.
 */
typedef enum {
    MONGOCRYPT_CTX_ERROR = 0,
    MONGOCRYPT_CTX_NEED_MONGO_COLLINFO = 1,         /* run on main MongoClient */
    MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB = 8, /* run on main MongoClient */
    MONGOCRYPT_CTX_NEED_MONGO_MARKINGS = 2,         /* run on mongocryptd. */
    MONGOCRYPT_CTX_NEED_MONGO_KEYS = 3,             /* run on key vault */
    MONGOCRYPT_CTX_NEED_KMS = 4,
    MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS = 7, /* fetch/renew KMS credentials */
    MONGOCRYPT_CTX_READY = 5,                /* ready for encryption/decryption */
    MONGOCRYPT_CTX_DONE = 6,
} mongocrypt_ctx_state_t;

/**
 * Get the current state of a context.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @returns A @ref mongocrypt_ctx_state_t.
 */
MONGOCRYPT_EXPORT
mongocrypt_ctx_state_t mongocrypt_ctx_state(mongocrypt_ctx_t *ctx);

/**
 * Get BSON necessary to run the mongo operation when mongocrypt_ctx_t
 * is in MONGOCRYPT_CTX_NEED_MONGO_* states.
 *
 * @p op_bson is a BSON document to be used for the operation.
 * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) 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 command to send to
 * mongocryptd.
 *
 * The lifetime of @p op_bson is tied to the lifetime of @p ctx. It is valid
 * until @ref mongocrypt_ctx_destroy is called.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[out] op_bson A BSON document for the MongoDB operation. The data
 * viewed by @p op_bson is guaranteed to be valid until @p ctx is destroyed with
 * @ref mongocrypt_ctx_destroy.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_mongo_op(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *op_bson);

/**
 * Get the database to run the mongo operation.
 *
 * Only applies when mongocrypt_ctx_t is in the state:
 * MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB.
 *
 * The lifetime of the returned string is tied to the lifetime of @p ctx. It is
 * valid until @ref mongocrypt_ctx_destroy is called.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @returns A string or NULL. If NULL, an error status is set. Retrieve it with
 * @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
const char *mongocrypt_ctx_mongo_db(mongocrypt_ctx_t *ctx);

/**
 * Feed a BSON reply or result when mongocrypt_ctx_t is in
 * MONGOCRYPT_CTX_NEED_MONGO_* states. This may be called multiple times
 * depending on the operation.
 *
 * reply is a BSON document result being fed back for this operation.
 * - For MONGOCRYPT_CTX_NEED_MONGO_COLLINFO(_WITH_DB) it is a doc from a listCollections
 * cursor. (Note, if listCollections returned no result, do not call this
 * function.)
 * - For MONGOCRYPT_CTX_NEED_MONGO_KEYS it is a doc from a find cursor.
 *   (Note, if find returned no results, do not call this function. reply must
 * not
 *   be NULL.)
 * - For MONGOCRYPT_CTX_NEED_MONGO_MARKINGS it is a reply from mongocryptd.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] reply A BSON document for the MongoDB operation. The viewed data
 * is copied. It is valid to destroy @p reply with @ref
 * mongocrypt_binary_destroy immediately after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_mongo_feed(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *reply);

/**
 * Call when done feeding the reply (or replies) back to the context.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_mongo_done(mongocrypt_ctx_t *ctx);

/**
 * Manages a single KMS HTTP request/response.
 */
typedef struct _mongocrypt_kms_ctx_t mongocrypt_kms_ctx_t;

/**
 * Get the next KMS handle.
 *
 * Multiple KMS handles may be retrieved at once. Drivers may do this to fan
 * out multiple concurrent KMS HTTP requests. Feeding multiple KMS requests
 * is thread-safe.
 *
 * 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 `ctx`.
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 * @returns a new @ref mongocrypt_kms_ctx_t or NULL.
 */
MONGOCRYPT_EXPORT
mongocrypt_kms_ctx_t *mongocrypt_ctx_next_kms_ctx(mongocrypt_ctx_t *ctx);

/**
 * Get the HTTP request message for a KMS handle.
 *
 * The lifetime of @p msg is tied to the lifetime of @p kms. It is valid
 * until @ref mongocrypt_ctx_kms_done is called.
 *
 * @param[in] kms A @ref mongocrypt_kms_ctx_t.
 * @param[out] msg The HTTP request to send to KMS. The data viewed by @p msg is
 * guaranteed to be valid until the call of @ref mongocrypt_ctx_kms_done of the
 * parent @ref mongocrypt_ctx_t.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_kms_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_message(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *msg);

/**
 * Get the hostname from which to connect over TLS.
 *
 * The storage for @p endpoint is not owned by the caller, but
 * is valid until calling @ref mongocrypt_ctx_kms_done.
 *
 * @param[in] kms A @ref mongocrypt_kms_ctx_t.
 * @param[out] endpoint The output endpoint as a NULL terminated string.
 * The endpoint consists of a hostname and port separated by a colon.
 * E.g. "example.com:123". A port is always present.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_kms_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_endpoint(mongocrypt_kms_ctx_t *kms, const char **endpoint);

/**
 * Indicates how many bytes to feed into @ref mongocrypt_kms_ctx_feed.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t.
 * @returns The number of requested bytes.
 */
MONGOCRYPT_EXPORT
uint32_t mongocrypt_kms_ctx_bytes_needed(mongocrypt_kms_ctx_t *kms);

/**
 * Indicates how long to sleep before sending this request.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t.
 * @returns How long to sleep in microseconds.
 */
MONGOCRYPT_EXPORT
int64_t mongocrypt_kms_ctx_usleep(mongocrypt_kms_ctx_t *kms);

/**
 * Feed bytes from the HTTP response.
 *
 * Feeding more bytes than what has been returned in @ref
 * mongocrypt_kms_ctx_bytes_needed is an error.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t.
 * @param[in] bytes The bytes to feed. The viewed data is copied. It is valid to
 * destroy @p bytes with @ref mongocrypt_binary_destroy immediately after.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_kms_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_feed(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes);

/**
 * Feed bytes from the KMS response.
 *
 * Feeding more bytes than what has been returned in @ref
 * mongocrypt_kms_ctx_bytes_needed is an error.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t.
 * @param[in] bytes The bytes to feed. The viewed data is copied. It is valid to
 * destroy @p bytes with @ref mongocrypt_binary_destroy immediately after.
 * @param[out] should_retry Whether the KMS request should be retried. Retry in-place
 * without calling @ref mongocrypt_kms_ctx_fail.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_kms_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_feed_with_retry(mongocrypt_kms_ctx_t *kms, mongocrypt_binary_t *bytes, bool *should_retry);

/**
 * Indicate a network error. Discards all data fed to this KMS context with @ref mongocrypt_kms_ctx_feed.
 * The @ref mongocrypt_kms_ctx_t may be reused.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t.
 * @return A boolean indicating whether the failed request may be retried.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_fail(mongocrypt_kms_ctx_t *kms);

/**
 * Get the status associated with a @ref mongocrypt_kms_ctx_t object.
 *
 * @param[in] kms The @ref mongocrypt_kms_ctx_t object.
 * @param[out] status Receives the status.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_kms_ctx_status(mongocrypt_kms_ctx_t *kms, mongocrypt_status_t *status);

/**
 * 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[in] kms The @ref mongocrypt_kms_ctx_t object.
 * @param[out] len Receives the length of the returned string. It may be NULL.
 * If it is not NULL, it is set to the length of the returned string without
 * the NULL terminator.
 *
 * @returns One of the NULL terminated static strings: "aws", "azure", "gcp", or
 * "kmip".
 */
MONGOCRYPT_EXPORT
const char *mongocrypt_kms_ctx_get_kms_provider(mongocrypt_kms_ctx_t *kms, uint32_t *len);

/**
 * Call when done handling all KMS contexts.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_kms_done(mongocrypt_ctx_t *ctx);

/**
 * Call in response to the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS state
 * to set per-context KMS provider settings. These follow the same format
 * as @ref mongocrypt_setopt_kms_providers. If no keys are present in the
 * BSON input, the KMS provider settings configured for the @ref mongocrypt_t
 * at initialization are used.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] kms_providers_definition A BSON document mapping the KMS provider
 * names to credentials.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_provide_kms_providers(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *kms_providers_definition);

/**
 * Perform the final encryption or decryption.
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 * @param[out] out The final BSON. The data viewed by @p out is guaranteed
 * to be valid until @p ctx is destroyed with @ref mongocrypt_ctx_destroy.
 * The meaning of this BSON depends on the type of @p ctx.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_encrypt_init, then
 * this BSON is the (possibly) encrypted command to send to the server.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_decrypt_init, then
 * this BSON is the decrypted result to return to the user.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_explicit_encrypt_init,
 * then this BSON has the form { "v": (BSON binary) } where the BSON binary
 * is the resulting encrypted value.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_explicit_decrypt_init,
 * then this BSON has the form { "v": (BSON value) } where the BSON value
 * is the resulting decrypted value.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_datakey_init, then
 * this BSON is the document containing the new data key to be inserted into
 * the key vault collection.
 *
 * If @p ctx was initialized with @ref mongocrypt_ctx_rewrap_many_datakey_init,
 * then this BSON has the form:
 *   { "v": [{ "_id": ..., "keyMaterial": ..., "masterKey": ... }, ...] }
 * where each BSON document in the array contains the updated fields of a
 * rewrapped datakey to be bulk-updated into the key vault collection.
 * Note: the updateDate field should be updated using the $currentDate operator.
 *
 * @returns a bool indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_finalize(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *out);

/**
 * Destroy and free all memory associated with a @ref mongocrypt_ctx_t.
 *
 * @param[in] ctx A @ref mongocrypt_ctx_t.
 */
MONGOCRYPT_EXPORT
void mongocrypt_ctx_destroy(mongocrypt_ctx_t *ctx);

/**
 * An crypto AES-256-CBC encrypt or decrypt function.
 *
 * Note, @p in is already padded. Encrypt with padding disabled.
 * @param[in] ctx An optional context object that may have been set when hooks
 * were enabled.
 * @param[in] key An encryption key (32 bytes for AES_256).
 * @param[in] iv An initialization vector (16 bytes for AES_256);
 * @param[in] in The input.
 * @param[out] out A preallocated byte array for the output. See @ref
 * mongocrypt_binary_data.
 * @param[out] bytes_written Set this to the number of bytes written to @p out.
 * @param[out] status An optional status to pass error messages. See @ref
 * mongocrypt_status_set.
 * @returns A boolean indicating success. If returning false, set @p status
 * with a message indicating the error using @ref mongocrypt_status_set.
 */
typedef bool (*mongocrypt_crypto_fn)(void *ctx,
                                     mongocrypt_binary_t *key,
                                     mongocrypt_binary_t *iv,
                                     mongocrypt_binary_t *in,
                                     mongocrypt_binary_t *out,
                                     uint32_t *bytes_written,
                                     mongocrypt_status_t *status);

/**
 * A crypto signature or HMAC function.
 *
 * Currently used in callbacks for HMAC SHA-512, HMAC SHA-256, and RSA SHA-256
 * signature.
 *
 * @param[in] ctx An optional context object that may have been set when hooks
 * were enabled.
 * @param[in] key An encryption key (32 bytes for HMAC_SHA512).
 * @param[in] in The input.
 * @param[out] out A preallocated byte array for the output. See @ref
 * mongocrypt_binary_data.
 * @param[out] status An optional status to pass error messages. See @ref
 * mongocrypt_status_set.
 * @returns A boolean indicating success. If returning false, set @p status
 * with a message indicating the error using @ref mongocrypt_status_set.
 */
typedef bool (*mongocrypt_hmac_fn)(void *ctx,
                                   mongocrypt_binary_t *key,
                                   mongocrypt_binary_t *in,
                                   mongocrypt_binary_t *out,
                                   mongocrypt_status_t *status);

/**
 * A crypto hash (SHA-256) function.
 *
 * @param[in] ctx An optional context object that may have been set when hooks
 * were enabled.
 * @param[in] in The input.
 * @param[out] out A preallocated byte array for the output. See @ref
 * mongocrypt_binary_data.
 * @param[out] status An optional status to pass error messages. See @ref
 * mongocrypt_status_set.
 * @returns A boolean indicating success. If returning false, set @p status
 * with a message indicating the error using @ref mongocrypt_status_set.
 */
typedef bool (*mongocrypt_hash_fn)(void *ctx,
                                   mongocrypt_binary_t *in,
                                   mongocrypt_binary_t *out,
                                   mongocrypt_status_t *status);

/**
 * A crypto secure random function.
 *
 * @param[in] ctx An optional context object that may have been set when hooks
 * were enabled.
 * @param[out] out A preallocated byte array for the output. See @ref
 * mongocrypt_binary_data.
 * @param[in] count The number of random bytes requested.
 * @param[out] status An optional status to pass error messages. See @ref
 * mongocrypt_status_set.
 * @returns A boolean indicating success. If returning false, set @p status
 * with a message indicating the error using @ref mongocrypt_status_set.
 */
typedef bool (*mongocrypt_random_fn)(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status);

MONGOCRYPT_EXPORT
bool mongocrypt_setopt_crypto_hooks(mongocrypt_t *crypt,
                                    mongocrypt_crypto_fn aes_256_cbc_encrypt,
                                    mongocrypt_crypto_fn aes_256_cbc_decrypt,
                                    mongocrypt_random_fn random,
                                    mongocrypt_hmac_fn hmac_sha_512,
                                    mongocrypt_hmac_fn hmac_sha_256,
                                    mongocrypt_hash_fn sha_256,
                                    void *ctx);

/**
 * Set a crypto hook for the AES256-CTR operations.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] aes_256_ctr_encrypt The crypto callback function for encrypt
 * operation.
 * @param[in] aes_256_ctr_decrypt The crypto callback function for decrypt
 * operation.
 * @param[in] ctx Unused.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 *
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_aes_256_ctr(mongocrypt_t *crypt,
                                   mongocrypt_crypto_fn aes_256_ctr_encrypt,
                                   mongocrypt_crypto_fn aes_256_ctr_decrypt,
                                   void *ctx);

/**
 * Set an AES256-ECB crypto hook for the AES256-CTR operations. If CTR hook was
 * configured using @ref mongocrypt_setopt_aes_256_ctr, ECB hook will be
 * ignored.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] aes_256_ecb_encrypt The crypto callback function for encrypt
 * operation.
 * @param[in] ctx Unused.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 *
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_aes_256_ecb(mongocrypt_t *crypt, mongocrypt_crypto_fn aes_256_ecb_encrypt, void *ctx);

/**
 * Set a crypto hook for the RSASSA-PKCS1-v1_5 algorithm with a SHA-256 hash.
 *
 * See: https://tools.ietf.org/html/rfc3447#section-8.2
 *
 * Note: this function has the wrong name. It should be:
 * mongocrypt_setopt_crypto_hook_sign_rsassa_pkcs1_v1_5
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 * @param[in] sign_rsaes_pkcs1_v1_5 The crypto callback function.
 * @param[in] sign_ctx A context passed as an argument to the crypto callback
 * every invocation.
 * @pre @ref mongocrypt_init has not been called on @p crypt.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 *
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(mongocrypt_t *crypt,
                                                         mongocrypt_hmac_fn sign_rsaes_pkcs1_v1_5,
                                                         void *sign_ctx);

/**
 * @brief Opt-into skipping query analysis.
 *
 * If opted in:
 * - The crypt_shared library will not attempt to be loaded.
 * - A mongocrypt_ctx_t will never enter the MONGOCRYPT_CTX_NEED_MARKINGS state.
 *
 * @param[in] crypt The @ref mongocrypt_t object to update
 */
MONGOCRYPT_EXPORT
void mongocrypt_setopt_bypass_query_analysis(mongocrypt_t *crypt);

/**
 * DEPRECATED: Use of `mongocrypt_setopt_use_range_v2` is deprecated. Range V2 is always enabled.
 *
 * @param[in] crypt The @ref mongocrypt_t object.
 *
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_use_range_v2(mongocrypt_t *crypt);

/**
 * Set the contention factor used for explicit encryption.
 * The contention factor is only used for indexed Queryable Encryption.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] contention_factor
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_contention_factor(mongocrypt_ctx_t *ctx, int64_t contention_factor);

/**
 * Set the index key id to use for explicit Queryable Encryption.
 *
 * If the index key id not set, the key id from @ref
 * mongocrypt_ctx_setopt_key_id is used.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] 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
 * @p key_id with @ref mongocrypt_binary_destroy immediately after.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_index_key_id(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *key_id);

/**
 * Set the query type to use for explicit Queryable Encryption.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] query_type The query type string
 * @param[in] len The length of query_type, or -1 for automatic
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_type, int len);

/**
 * Set options for explicit encryption with the "range" algorithm.
 *
 * @p opts is a BSON document of the form:
 * {
 *    "min": Optional,
 *    "max": Optional,
 *    "sparsity": Optional,
 *    "precision": Optional,
 *    "trimFactor": Optional
 * }
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] opts BSON.
 * @pre @p ctx has not been initialized.
 * @returns A boolean indicating success. If false, an error status is set.
 * Retrieve it with @ref mongocrypt_ctx_status
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts);

/**
 * Set options for explicit encryption with the "string" algorithm.
 *
 * NOTE: Use of the "substringPreview" query type is experimental only and may be removed in a future non-major release.
 * @p 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,
 * . },
 * }
 *
 * "prefix" and "suffix" can both be set.
 *
 * NOTE: Driver public APIs should use the name "string" rather than "text" to refer to options.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_ctx_setopt_algorithm_text(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts);

/**
 * Set the expiration time for the data encryption key cache. Defaults to 60 seconds if not set.
 *
 * @param[in] ctx The @ref mongocrypt_ctx_t object.
 * @param[in] cache_expiration_ms The cache expiration time in milliseconds. If zero, the cache
 * never expires.
 */
MONGOCRYPT_EXPORT
bool mongocrypt_setopt_key_expiration(mongocrypt_t *crypt, uint64_t cache_expiration_ms);

/// String constants for setopt_query_type
#define MONGOCRYPT_QUERY_TYPE_EQUALITY_STR "equality"
// DEPRECATED: Support "rangePreview" has been removed in favor of "range".
#define MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR "rangePreview"
#define MONGOCRYPT_QUERY_TYPE_RANGE_STR "range"
/// NOTE: "substringPreview" is experimental and may be removed in a future non-major release.
#define MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR "substringPreview"
/// DEPRECATED: Support for "suffixPreview" has been removed in favor of "suffix"
#define MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR "suffixPreview"
#define MONGOCRYPT_QUERY_TYPE_SUFFIX_STR "suffix"
/// DEPRECATED: Support for "prefixPreview" has been removed in favor of "suffix"
#define MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR "prefixPreview"
#define MONGOCRYPT_QUERY_TYPE_PREFIX_STR "prefix"

#endif /* MONGOCRYPT_H */
libmongocrypt-1.19.0/src/os_posix/000077500000000000000000000000001521103432300171165ustar00rootroot00000000000000libmongocrypt-1.19.0/src/os_posix/os_dll.c000066400000000000000000000101611521103432300205350ustar00rootroot00000000000000// Turn on libc extensions so that we can use dladdr() on Unix-like systems
#if defined(__has_include) && !(defined(_GNU_SOURCE) || defined(_DARWIN_C_SOURCE))
#if __has_include()
// We're using a glibc-compatible library
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#elif __has_include()
// We're on Apple/Darwin
#define _DARWIN_C_SOURCE
#endif
#else // No __has_include
#if __GNUC__ < 5 && !defined(_GNU_SOURCE)
// Best guess on older GCC is that we are using glibc
#define _GNU_SOURCE
#endif
#endif

#include "../mongocrypt-dll-private.h"

#ifndef _WIN32

#include 
#include 
#include 

#include 

mcr_dll mcr_dll_open(const char *filepath) {
    void *handle = dlopen(filepath, RTLD_LAZY | RTLD_LOCAL);
    if (handle == NULL) {
        // Failed to open. Return NULL and copy the error message
        return (mcr_dll){
            ._native_handle = NULL,
            .error_string = mstr_copy_cstr(dlerror()),
        };
    } else {
        // Okay
        return (mcr_dll){
            ._native_handle = handle,
            .error_string = MSTR_NULL,
        };
    }
}

void mcr_dll_close_handle(mcr_dll); // -Wmissing-prototypes: not for external use despite external linkage.

void mcr_dll_close_handle(mcr_dll dll) {
    if (dll._native_handle) {
        dlclose(dll._native_handle);
    }
}

void *mcr_dll_sym(mcr_dll dll, const char *sym) {
    return dlsym(dll._native_handle, sym);
}

#endif

#ifdef __APPLE__

#include 
#include 

mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
    // Clear the three low bits of the module handle:
    uintptr_t needle = ((uintptr_t)dll._native_handle & ~UINT64_C(0x3));
    // Iterate each loaded dyld image
    /// NOTE: Not thread safe. Is there a thread-safe way to do this?
    for (uint32_t idx = 0; idx < _dyld_image_count(); ++idx) {
        // Get the filepath:
        /// NOTE: Between here and `dlopen`, `dyld_name` could be invalidated by
        /// a concurrent call to `dlclose()`. Is there a better way?
        const char *dyld_name = _dyld_get_image_name(idx);
        // Try and open it. This will return an equivalent pointer to the original
        // handle to the loaded image since they are deduplicated and reference
        // counted.
        void *try_handle = dlopen(dyld_name, RTLD_LAZY);
        if (!dyld_name) {
            // Ouch: `idx` was invalidated before we called _dyld_get_image_name.
            // This will have caused `dlopen()` to return the default handle:
            assert(try_handle == RTLD_DEFAULT);
            continue;
        }
        // Copy the string before closing, to shrink the chance of `dyld_name`
        // being used-after-freed.
        mstr ret_name = mstr_copy_cstr(dyld_name);
        // Mask off the mode bits:
        uintptr_t cur = (uintptr_t)try_handle & ~UINT64_C(0x3);
        // Close our reference to the image. We only care about the handle value.
        dlclose(try_handle);
        if (needle == cur) {
            // We've found the handle
            return (mcr_dll_path_result){.path = ret_name};
        }
        // Not this name.
        mstr_free(ret_name);
    }
    return (mcr_dll_path_result){.error_string = mstr_copy_cstr("Handle not found in loaded modules")};
}

bool mcr_dll_path_supported(void) {
    return true;
}

#elif defined(__linux__) || defined(__FreeBSD__)

#include 

mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
    struct link_map *map = NULL;
    int rc = dlinfo(dll._native_handle, RTLD_DI_LINKMAP, &map);
    if (rc == 0) {
        assert(NULL != map);
        return (mcr_dll_path_result){.path = mstr_copy_cstr(map->l_name)};
    } else {
        return (mcr_dll_path_result){.error_string = mstr_copy_cstr(dlerror())};
    }
}

bool mcr_dll_path_supported(void) {
    return true;
}

#elif defined(_WIN32)

// Handled in os_win/os_dll.c

#else

mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
    return (mcr_dll_path_result){.error_string =
                                     mstr_copy_cstr("Don't know how to do mcr_dll_path() on this platform")};
}

bool mcr_dll_path_supported(void) {
    return false;
}

#endif
libmongocrypt-1.19.0/src/os_posix/os_mutex.c000066400000000000000000000023461521103432300211320ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "../mongocrypt-mutex-private.h"

#ifndef _WIN32

void _mongocrypt_mutex_init(mongocrypt_mutex_t *mutex) {
    int ret = pthread_mutex_init(mutex, NULL);
    if (ret) {
        abort();
    }
}

void _mongocrypt_mutex_cleanup(mongocrypt_mutex_t *mutex) {
    int ret = pthread_mutex_destroy(mutex);
    if (ret) {
        abort();
    }
}

void _mongocrypt_mutex_lock(mongocrypt_mutex_t *mutex) {
    int ret = pthread_mutex_lock(mutex);
    if (ret) {
        abort();
    }
}

void _mongocrypt_mutex_unlock(mongocrypt_mutex_t *mutex) {
    int ret = pthread_mutex_unlock(mutex);
    if (ret) {
        abort();
    }
}

#endif /* _WIN32 */
libmongocrypt-1.19.0/src/os_win/000077500000000000000000000000001521103432300165515ustar00rootroot00000000000000libmongocrypt-1.19.0/src/os_win/os_dll.c000066400000000000000000000055711521103432300202010ustar00rootroot00000000000000#include "../mongocrypt-dll-private.h"

#ifdef _WIN32

#include 
#include 
#include 

#include 
#include 

#include 

mcr_dll mcr_dll_open(const char *filepath_) {
    // Convert all slashes to the native Windows separator
    mstr filepath = mpath_to_format(MPATH_WIN32, mstrv_view_cstr(filepath_), MPATH_WIN32);
    // Check if the path is just a filename.
    bool is_just_filename = mstr_eq(mpath_filename(filepath.view, MPATH_WIN32), filepath.view);
    if (!is_just_filename) {
        // If the path is only a filename, we'll allow LoadLibrary() to do a
        // proper full DLL search. If the path is NOT just a filename, resolve the
        // given path to a single unambiguous absolute path to suppress
        // LoadLibrary()'s DLL search behavior.
        mstr_assign(&filepath, mpath_absolute(filepath.view, MPATH_WIN32));
    }
    mstr_widen_result wide = mstr_win32_widen(filepath.view);
    mstr_free(filepath);
    if (wide.error) {
        return (mcr_dll){._native_handle = NULL, .error_string = merror_system_error_string(wide.error)};
    }
    HMODULE lib = LoadLibraryW(wide.wstring);
    if (lib == NULL) {
        return (mcr_dll){._native_handle = NULL, .error_string = merror_system_error_string(GetLastError())};
    }
    free(wide.wstring);
    return (mcr_dll){.error_string = NULL, ._native_handle = lib};
}

void mcr_dll_close_handle(mcr_dll); // -Wmissing-prototypes: not for external use despite external linkage.

void mcr_dll_close_handle(mcr_dll dll) {
    if (dll._native_handle) {
        FreeLibrary(dll._native_handle);
    }
}

void *mcr_dll_sym(mcr_dll dll, const char *sym) {
    return GetProcAddress(dll._native_handle, sym);
}

mcr_dll_path_result mcr_dll_path(mcr_dll dll) {
    mstr ret_str = MSTR_NULL;
    int ret_error = 0;
    DWORD acc_size = 512;
    while (!ret_str.raw.data && !ret_error) {
        // Loop until we allocate a large enough buffer or get an error
        wchar_t *path = calloc((size_t)acc_size + 1u, sizeof(wchar_t));
        SetLastError(0);
        GetModuleFileNameW(dll._native_handle, path, acc_size);
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            // Try again with more buffer
            /* DWORD is a 32-bit unsigned integer */
            assert(acc_size <= UINT32_MAX / 2u);
            acc_size *= 2;
        } else if (GetLastError() != 0) {
            ret_error = GetLastError();
        } else {
            mstr_narrow_result narrow = mstr_win32_narrow(path);
            // GetModuleFileNameW should never return invalid Unicode:
            assert(narrow.error == 0);
            ret_str = narrow.string;
        }
        free(path);
    }
    return (mcr_dll_path_result){
        .path = ret_str,
        .error_string = merror_system_error_string(ret_error),
    };
}

bool mcr_dll_path_supported(void) {
    return true;
}

#endif
libmongocrypt-1.19.0/src/os_win/os_mutex.c000066400000000000000000000020501521103432300205550ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "../mongocrypt-mutex-private.h"

#ifdef _WIN32

void _mongocrypt_mutex_init(mongocrypt_mutex_t *mutex) {
    InitializeCriticalSection(mutex);
}

void _mongocrypt_mutex_cleanup(mongocrypt_mutex_t *mutex) {
    DeleteCriticalSection(mutex);
}

void _mongocrypt_mutex_lock(mongocrypt_mutex_t *mutex) {
    EnterCriticalSection(mutex);
}

void _mongocrypt_mutex_unlock(mongocrypt_mutex_t *mutex) {
    LeaveCriticalSection(mutex);
}

#endif /* _WIN32 */
libmongocrypt-1.19.0/src/unicode/000077500000000000000000000000001521103432300167015ustar00rootroot00000000000000libmongocrypt-1.19.0/src/unicode/case-fold-map.c000066400000000000000000001313571521103432300214670ustar00rootroot00000000000000/**
 *    Copyright (C) 2025-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    .
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 *
 *    THIS IS A GENERATED FILE, DO NOT MODIFY.
 */

#include "bson/bson.h"
#include "fold.h"

bson_unichar_t unicode_codepoint_to_lower(bson_unichar_t codepoint) {
    if (codepoint <= 0x7f) {
        if (codepoint >= 'A' && codepoint <= 'Z') {
            return codepoint | 0x20; // Set the ascii lowercase bit on the character.
        }
        return codepoint;
    }

    switch (codepoint) {
    case 0xb5: return 0x3bc;
    case 0xc0: return 0xe0;
    case 0xc1: return 0xe1;
    case 0xc2: return 0xe2;
    case 0xc3: return 0xe3;
    case 0xc4: return 0xe4;
    case 0xc5: return 0xe5;
    case 0xc6: return 0xe6;
    case 0xc7: return 0xe7;
    case 0xc8: return 0xe8;
    case 0xc9: return 0xe9;
    case 0xca: return 0xea;
    case 0xcb: return 0xeb;
    case 0xcc: return 0xec;
    case 0xcd: return 0xed;
    case 0xce: return 0xee;
    case 0xcf: return 0xef;
    case 0xd0: return 0xf0;
    case 0xd1: return 0xf1;
    case 0xd2: return 0xf2;
    case 0xd3: return 0xf3;
    case 0xd4: return 0xf4;
    case 0xd5: return 0xf5;
    case 0xd6: return 0xf6;
    case 0xd8: return 0xf8;
    case 0xd9: return 0xf9;
    case 0xda: return 0xfa;
    case 0xdb: return 0xfb;
    case 0xdc: return 0xfc;
    case 0xdd: return 0xfd;
    case 0xde: return 0xfe;
    case 0x100: return 0x101;
    case 0x102: return 0x103;
    case 0x104: return 0x105;
    case 0x106: return 0x107;
    case 0x108: return 0x109;
    case 0x10a: return 0x10b;
    case 0x10c: return 0x10d;
    case 0x10e: return 0x10f;
    case 0x110: return 0x111;
    case 0x112: return 0x113;
    case 0x114: return 0x115;
    case 0x116: return 0x117;
    case 0x118: return 0x119;
    case 0x11a: return 0x11b;
    case 0x11c: return 0x11d;
    case 0x11e: return 0x11f;
    case 0x120: return 0x121;
    case 0x122: return 0x123;
    case 0x124: return 0x125;
    case 0x126: return 0x127;
    case 0x128: return 0x129;
    case 0x12a: return 0x12b;
    case 0x12c: return 0x12d;
    case 0x12e: return 0x12f;
    case 0x132: return 0x133;
    case 0x134: return 0x135;
    case 0x136: return 0x137;
    case 0x139: return 0x13a;
    case 0x13b: return 0x13c;
    case 0x13d: return 0x13e;
    case 0x13f: return 0x140;
    case 0x141: return 0x142;
    case 0x143: return 0x144;
    case 0x145: return 0x146;
    case 0x147: return 0x148;
    case 0x14a: return 0x14b;
    case 0x14c: return 0x14d;
    case 0x14e: return 0x14f;
    case 0x150: return 0x151;
    case 0x152: return 0x153;
    case 0x154: return 0x155;
    case 0x156: return 0x157;
    case 0x158: return 0x159;
    case 0x15a: return 0x15b;
    case 0x15c: return 0x15d;
    case 0x15e: return 0x15f;
    case 0x160: return 0x161;
    case 0x162: return 0x163;
    case 0x164: return 0x165;
    case 0x166: return 0x167;
    case 0x168: return 0x169;
    case 0x16a: return 0x16b;
    case 0x16c: return 0x16d;
    case 0x16e: return 0x16f;
    case 0x170: return 0x171;
    case 0x172: return 0x173;
    case 0x174: return 0x175;
    case 0x176: return 0x177;
    case 0x178: return 0xff;
    case 0x179: return 0x17a;
    case 0x17b: return 0x17c;
    case 0x17d: return 0x17e;
    case 0x17f: return 0x73;
    case 0x181: return 0x253;
    case 0x182: return 0x183;
    case 0x184: return 0x185;
    case 0x186: return 0x254;
    case 0x187: return 0x188;
    case 0x189: return 0x256;
    case 0x18a: return 0x257;
    case 0x18b: return 0x18c;
    case 0x18e: return 0x1dd;
    case 0x18f: return 0x259;
    case 0x190: return 0x25b;
    case 0x191: return 0x192;
    case 0x193: return 0x260;
    case 0x194: return 0x263;
    case 0x196: return 0x269;
    case 0x197: return 0x268;
    case 0x198: return 0x199;
    case 0x19c: return 0x26f;
    case 0x19d: return 0x272;
    case 0x19f: return 0x275;
    case 0x1a0: return 0x1a1;
    case 0x1a2: return 0x1a3;
    case 0x1a4: return 0x1a5;
    case 0x1a6: return 0x280;
    case 0x1a7: return 0x1a8;
    case 0x1a9: return 0x283;
    case 0x1ac: return 0x1ad;
    case 0x1ae: return 0x288;
    case 0x1af: return 0x1b0;
    case 0x1b1: return 0x28a;
    case 0x1b2: return 0x28b;
    case 0x1b3: return 0x1b4;
    case 0x1b5: return 0x1b6;
    case 0x1b7: return 0x292;
    case 0x1b8: return 0x1b9;
    case 0x1bc: return 0x1bd;
    case 0x1c4: return 0x1c6;
    case 0x1c5: return 0x1c6;
    case 0x1c7: return 0x1c9;
    case 0x1c8: return 0x1c9;
    case 0x1ca: return 0x1cc;
    case 0x1cb: return 0x1cc;
    case 0x1cd: return 0x1ce;
    case 0x1cf: return 0x1d0;
    case 0x1d1: return 0x1d2;
    case 0x1d3: return 0x1d4;
    case 0x1d5: return 0x1d6;
    case 0x1d7: return 0x1d8;
    case 0x1d9: return 0x1da;
    case 0x1db: return 0x1dc;
    case 0x1de: return 0x1df;
    case 0x1e0: return 0x1e1;
    case 0x1e2: return 0x1e3;
    case 0x1e4: return 0x1e5;
    case 0x1e6: return 0x1e7;
    case 0x1e8: return 0x1e9;
    case 0x1ea: return 0x1eb;
    case 0x1ec: return 0x1ed;
    case 0x1ee: return 0x1ef;
    case 0x1f1: return 0x1f3;
    case 0x1f2: return 0x1f3;
    case 0x1f4: return 0x1f5;
    case 0x1f6: return 0x195;
    case 0x1f7: return 0x1bf;
    case 0x1f8: return 0x1f9;
    case 0x1fa: return 0x1fb;
    case 0x1fc: return 0x1fd;
    case 0x1fe: return 0x1ff;
    case 0x200: return 0x201;
    case 0x202: return 0x203;
    case 0x204: return 0x205;
    case 0x206: return 0x207;
    case 0x208: return 0x209;
    case 0x20a: return 0x20b;
    case 0x20c: return 0x20d;
    case 0x20e: return 0x20f;
    case 0x210: return 0x211;
    case 0x212: return 0x213;
    case 0x214: return 0x215;
    case 0x216: return 0x217;
    case 0x218: return 0x219;
    case 0x21a: return 0x21b;
    case 0x21c: return 0x21d;
    case 0x21e: return 0x21f;
    case 0x220: return 0x19e;
    case 0x222: return 0x223;
    case 0x224: return 0x225;
    case 0x226: return 0x227;
    case 0x228: return 0x229;
    case 0x22a: return 0x22b;
    case 0x22c: return 0x22d;
    case 0x22e: return 0x22f;
    case 0x230: return 0x231;
    case 0x232: return 0x233;
    case 0x23a: return 0x2c65;
    case 0x23b: return 0x23c;
    case 0x23d: return 0x19a;
    case 0x23e: return 0x2c66;
    case 0x241: return 0x242;
    case 0x243: return 0x180;
    case 0x244: return 0x289;
    case 0x245: return 0x28c;
    case 0x246: return 0x247;
    case 0x248: return 0x249;
    case 0x24a: return 0x24b;
    case 0x24c: return 0x24d;
    case 0x24e: return 0x24f;
    case 0x345: return 0x3b9;
    case 0x370: return 0x371;
    case 0x372: return 0x373;
    case 0x376: return 0x377;
    case 0x37f: return 0x3f3;
    case 0x386: return 0x3ac;
    case 0x388: return 0x3ad;
    case 0x389: return 0x3ae;
    case 0x38a: return 0x3af;
    case 0x38c: return 0x3cc;
    case 0x38e: return 0x3cd;
    case 0x38f: return 0x3ce;
    case 0x391: return 0x3b1;
    case 0x392: return 0x3b2;
    case 0x393: return 0x3b3;
    case 0x394: return 0x3b4;
    case 0x395: return 0x3b5;
    case 0x396: return 0x3b6;
    case 0x397: return 0x3b7;
    case 0x398: return 0x3b8;
    case 0x399: return 0x3b9;
    case 0x39a: return 0x3ba;
    case 0x39b: return 0x3bb;
    case 0x39c: return 0x3bc;
    case 0x39d: return 0x3bd;
    case 0x39e: return 0x3be;
    case 0x39f: return 0x3bf;
    case 0x3a0: return 0x3c0;
    case 0x3a1: return 0x3c1;
    case 0x3a3: return 0x3c3;
    case 0x3a4: return 0x3c4;
    case 0x3a5: return 0x3c5;
    case 0x3a6: return 0x3c6;
    case 0x3a7: return 0x3c7;
    case 0x3a8: return 0x3c8;
    case 0x3a9: return 0x3c9;
    case 0x3aa: return 0x3ca;
    case 0x3ab: return 0x3cb;
    case 0x3c2: return 0x3c3;
    case 0x3cf: return 0x3d7;
    case 0x3d0: return 0x3b2;
    case 0x3d1: return 0x3b8;
    case 0x3d5: return 0x3c6;
    case 0x3d6: return 0x3c0;
    case 0x3d8: return 0x3d9;
    case 0x3da: return 0x3db;
    case 0x3dc: return 0x3dd;
    case 0x3de: return 0x3df;
    case 0x3e0: return 0x3e1;
    case 0x3e2: return 0x3e3;
    case 0x3e4: return 0x3e5;
    case 0x3e6: return 0x3e7;
    case 0x3e8: return 0x3e9;
    case 0x3ea: return 0x3eb;
    case 0x3ec: return 0x3ed;
    case 0x3ee: return 0x3ef;
    case 0x3f0: return 0x3ba;
    case 0x3f1: return 0x3c1;
    case 0x3f4: return 0x3b8;
    case 0x3f5: return 0x3b5;
    case 0x3f7: return 0x3f8;
    case 0x3f9: return 0x3f2;
    case 0x3fa: return 0x3fb;
    case 0x3fd: return 0x37b;
    case 0x3fe: return 0x37c;
    case 0x3ff: return 0x37d;
    case 0x400: return 0x450;
    case 0x401: return 0x451;
    case 0x402: return 0x452;
    case 0x403: return 0x453;
    case 0x404: return 0x454;
    case 0x405: return 0x455;
    case 0x406: return 0x456;
    case 0x407: return 0x457;
    case 0x408: return 0x458;
    case 0x409: return 0x459;
    case 0x40a: return 0x45a;
    case 0x40b: return 0x45b;
    case 0x40c: return 0x45c;
    case 0x40d: return 0x45d;
    case 0x40e: return 0x45e;
    case 0x40f: return 0x45f;
    case 0x410: return 0x430;
    case 0x411: return 0x431;
    case 0x412: return 0x432;
    case 0x413: return 0x433;
    case 0x414: return 0x434;
    case 0x415: return 0x435;
    case 0x416: return 0x436;
    case 0x417: return 0x437;
    case 0x418: return 0x438;
    case 0x419: return 0x439;
    case 0x41a: return 0x43a;
    case 0x41b: return 0x43b;
    case 0x41c: return 0x43c;
    case 0x41d: return 0x43d;
    case 0x41e: return 0x43e;
    case 0x41f: return 0x43f;
    case 0x420: return 0x440;
    case 0x421: return 0x441;
    case 0x422: return 0x442;
    case 0x423: return 0x443;
    case 0x424: return 0x444;
    case 0x425: return 0x445;
    case 0x426: return 0x446;
    case 0x427: return 0x447;
    case 0x428: return 0x448;
    case 0x429: return 0x449;
    case 0x42a: return 0x44a;
    case 0x42b: return 0x44b;
    case 0x42c: return 0x44c;
    case 0x42d: return 0x44d;
    case 0x42e: return 0x44e;
    case 0x42f: return 0x44f;
    case 0x460: return 0x461;
    case 0x462: return 0x463;
    case 0x464: return 0x465;
    case 0x466: return 0x467;
    case 0x468: return 0x469;
    case 0x46a: return 0x46b;
    case 0x46c: return 0x46d;
    case 0x46e: return 0x46f;
    case 0x470: return 0x471;
    case 0x472: return 0x473;
    case 0x474: return 0x475;
    case 0x476: return 0x477;
    case 0x478: return 0x479;
    case 0x47a: return 0x47b;
    case 0x47c: return 0x47d;
    case 0x47e: return 0x47f;
    case 0x480: return 0x481;
    case 0x48a: return 0x48b;
    case 0x48c: return 0x48d;
    case 0x48e: return 0x48f;
    case 0x490: return 0x491;
    case 0x492: return 0x493;
    case 0x494: return 0x495;
    case 0x496: return 0x497;
    case 0x498: return 0x499;
    case 0x49a: return 0x49b;
    case 0x49c: return 0x49d;
    case 0x49e: return 0x49f;
    case 0x4a0: return 0x4a1;
    case 0x4a2: return 0x4a3;
    case 0x4a4: return 0x4a5;
    case 0x4a6: return 0x4a7;
    case 0x4a8: return 0x4a9;
    case 0x4aa: return 0x4ab;
    case 0x4ac: return 0x4ad;
    case 0x4ae: return 0x4af;
    case 0x4b0: return 0x4b1;
    case 0x4b2: return 0x4b3;
    case 0x4b4: return 0x4b5;
    case 0x4b6: return 0x4b7;
    case 0x4b8: return 0x4b9;
    case 0x4ba: return 0x4bb;
    case 0x4bc: return 0x4bd;
    case 0x4be: return 0x4bf;
    case 0x4c0: return 0x4cf;
    case 0x4c1: return 0x4c2;
    case 0x4c3: return 0x4c4;
    case 0x4c5: return 0x4c6;
    case 0x4c7: return 0x4c8;
    case 0x4c9: return 0x4ca;
    case 0x4cb: return 0x4cc;
    case 0x4cd: return 0x4ce;
    case 0x4d0: return 0x4d1;
    case 0x4d2: return 0x4d3;
    case 0x4d4: return 0x4d5;
    case 0x4d6: return 0x4d7;
    case 0x4d8: return 0x4d9;
    case 0x4da: return 0x4db;
    case 0x4dc: return 0x4dd;
    case 0x4de: return 0x4df;
    case 0x4e0: return 0x4e1;
    case 0x4e2: return 0x4e3;
    case 0x4e4: return 0x4e5;
    case 0x4e6: return 0x4e7;
    case 0x4e8: return 0x4e9;
    case 0x4ea: return 0x4eb;
    case 0x4ec: return 0x4ed;
    case 0x4ee: return 0x4ef;
    case 0x4f0: return 0x4f1;
    case 0x4f2: return 0x4f3;
    case 0x4f4: return 0x4f5;
    case 0x4f6: return 0x4f7;
    case 0x4f8: return 0x4f9;
    case 0x4fa: return 0x4fb;
    case 0x4fc: return 0x4fd;
    case 0x4fe: return 0x4ff;
    case 0x500: return 0x501;
    case 0x502: return 0x503;
    case 0x504: return 0x505;
    case 0x506: return 0x507;
    case 0x508: return 0x509;
    case 0x50a: return 0x50b;
    case 0x50c: return 0x50d;
    case 0x50e: return 0x50f;
    case 0x510: return 0x511;
    case 0x512: return 0x513;
    case 0x514: return 0x515;
    case 0x516: return 0x517;
    case 0x518: return 0x519;
    case 0x51a: return 0x51b;
    case 0x51c: return 0x51d;
    case 0x51e: return 0x51f;
    case 0x520: return 0x521;
    case 0x522: return 0x523;
    case 0x524: return 0x525;
    case 0x526: return 0x527;
    case 0x528: return 0x529;
    case 0x52a: return 0x52b;
    case 0x52c: return 0x52d;
    case 0x52e: return 0x52f;
    case 0x531: return 0x561;
    case 0x532: return 0x562;
    case 0x533: return 0x563;
    case 0x534: return 0x564;
    case 0x535: return 0x565;
    case 0x536: return 0x566;
    case 0x537: return 0x567;
    case 0x538: return 0x568;
    case 0x539: return 0x569;
    case 0x53a: return 0x56a;
    case 0x53b: return 0x56b;
    case 0x53c: return 0x56c;
    case 0x53d: return 0x56d;
    case 0x53e: return 0x56e;
    case 0x53f: return 0x56f;
    case 0x540: return 0x570;
    case 0x541: return 0x571;
    case 0x542: return 0x572;
    case 0x543: return 0x573;
    case 0x544: return 0x574;
    case 0x545: return 0x575;
    case 0x546: return 0x576;
    case 0x547: return 0x577;
    case 0x548: return 0x578;
    case 0x549: return 0x579;
    case 0x54a: return 0x57a;
    case 0x54b: return 0x57b;
    case 0x54c: return 0x57c;
    case 0x54d: return 0x57d;
    case 0x54e: return 0x57e;
    case 0x54f: return 0x57f;
    case 0x550: return 0x580;
    case 0x551: return 0x581;
    case 0x552: return 0x582;
    case 0x553: return 0x583;
    case 0x554: return 0x584;
    case 0x555: return 0x585;
    case 0x556: return 0x586;
    case 0x10a0: return 0x2d00;
    case 0x10a1: return 0x2d01;
    case 0x10a2: return 0x2d02;
    case 0x10a3: return 0x2d03;
    case 0x10a4: return 0x2d04;
    case 0x10a5: return 0x2d05;
    case 0x10a6: return 0x2d06;
    case 0x10a7: return 0x2d07;
    case 0x10a8: return 0x2d08;
    case 0x10a9: return 0x2d09;
    case 0x10aa: return 0x2d0a;
    case 0x10ab: return 0x2d0b;
    case 0x10ac: return 0x2d0c;
    case 0x10ad: return 0x2d0d;
    case 0x10ae: return 0x2d0e;
    case 0x10af: return 0x2d0f;
    case 0x10b0: return 0x2d10;
    case 0x10b1: return 0x2d11;
    case 0x10b2: return 0x2d12;
    case 0x10b3: return 0x2d13;
    case 0x10b4: return 0x2d14;
    case 0x10b5: return 0x2d15;
    case 0x10b6: return 0x2d16;
    case 0x10b7: return 0x2d17;
    case 0x10b8: return 0x2d18;
    case 0x10b9: return 0x2d19;
    case 0x10ba: return 0x2d1a;
    case 0x10bb: return 0x2d1b;
    case 0x10bc: return 0x2d1c;
    case 0x10bd: return 0x2d1d;
    case 0x10be: return 0x2d1e;
    case 0x10bf: return 0x2d1f;
    case 0x10c0: return 0x2d20;
    case 0x10c1: return 0x2d21;
    case 0x10c2: return 0x2d22;
    case 0x10c3: return 0x2d23;
    case 0x10c4: return 0x2d24;
    case 0x10c5: return 0x2d25;
    case 0x10c7: return 0x2d27;
    case 0x10cd: return 0x2d2d;
    case 0x13f8: return 0x13f0;
    case 0x13f9: return 0x13f1;
    case 0x13fa: return 0x13f2;
    case 0x13fb: return 0x13f3;
    case 0x13fc: return 0x13f4;
    case 0x13fd: return 0x13f5;
    case 0x1c80: return 0x432;
    case 0x1c81: return 0x434;
    case 0x1c82: return 0x43e;
    case 0x1c83: return 0x441;
    case 0x1c84: return 0x442;
    case 0x1c85: return 0x442;
    case 0x1c86: return 0x44a;
    case 0x1c87: return 0x463;
    case 0x1c88: return 0xa64b;
    case 0x1c90: return 0x10d0;
    case 0x1c91: return 0x10d1;
    case 0x1c92: return 0x10d2;
    case 0x1c93: return 0x10d3;
    case 0x1c94: return 0x10d4;
    case 0x1c95: return 0x10d5;
    case 0x1c96: return 0x10d6;
    case 0x1c97: return 0x10d7;
    case 0x1c98: return 0x10d8;
    case 0x1c99: return 0x10d9;
    case 0x1c9a: return 0x10da;
    case 0x1c9b: return 0x10db;
    case 0x1c9c: return 0x10dc;
    case 0x1c9d: return 0x10dd;
    case 0x1c9e: return 0x10de;
    case 0x1c9f: return 0x10df;
    case 0x1ca0: return 0x10e0;
    case 0x1ca1: return 0x10e1;
    case 0x1ca2: return 0x10e2;
    case 0x1ca3: return 0x10e3;
    case 0x1ca4: return 0x10e4;
    case 0x1ca5: return 0x10e5;
    case 0x1ca6: return 0x10e6;
    case 0x1ca7: return 0x10e7;
    case 0x1ca8: return 0x10e8;
    case 0x1ca9: return 0x10e9;
    case 0x1caa: return 0x10ea;
    case 0x1cab: return 0x10eb;
    case 0x1cac: return 0x10ec;
    case 0x1cad: return 0x10ed;
    case 0x1cae: return 0x10ee;
    case 0x1caf: return 0x10ef;
    case 0x1cb0: return 0x10f0;
    case 0x1cb1: return 0x10f1;
    case 0x1cb2: return 0x10f2;
    case 0x1cb3: return 0x10f3;
    case 0x1cb4: return 0x10f4;
    case 0x1cb5: return 0x10f5;
    case 0x1cb6: return 0x10f6;
    case 0x1cb7: return 0x10f7;
    case 0x1cb8: return 0x10f8;
    case 0x1cb9: return 0x10f9;
    case 0x1cba: return 0x10fa;
    case 0x1cbd: return 0x10fd;
    case 0x1cbe: return 0x10fe;
    case 0x1cbf: return 0x10ff;
    case 0x1e00: return 0x1e01;
    case 0x1e02: return 0x1e03;
    case 0x1e04: return 0x1e05;
    case 0x1e06: return 0x1e07;
    case 0x1e08: return 0x1e09;
    case 0x1e0a: return 0x1e0b;
    case 0x1e0c: return 0x1e0d;
    case 0x1e0e: return 0x1e0f;
    case 0x1e10: return 0x1e11;
    case 0x1e12: return 0x1e13;
    case 0x1e14: return 0x1e15;
    case 0x1e16: return 0x1e17;
    case 0x1e18: return 0x1e19;
    case 0x1e1a: return 0x1e1b;
    case 0x1e1c: return 0x1e1d;
    case 0x1e1e: return 0x1e1f;
    case 0x1e20: return 0x1e21;
    case 0x1e22: return 0x1e23;
    case 0x1e24: return 0x1e25;
    case 0x1e26: return 0x1e27;
    case 0x1e28: return 0x1e29;
    case 0x1e2a: return 0x1e2b;
    case 0x1e2c: return 0x1e2d;
    case 0x1e2e: return 0x1e2f;
    case 0x1e30: return 0x1e31;
    case 0x1e32: return 0x1e33;
    case 0x1e34: return 0x1e35;
    case 0x1e36: return 0x1e37;
    case 0x1e38: return 0x1e39;
    case 0x1e3a: return 0x1e3b;
    case 0x1e3c: return 0x1e3d;
    case 0x1e3e: return 0x1e3f;
    case 0x1e40: return 0x1e41;
    case 0x1e42: return 0x1e43;
    case 0x1e44: return 0x1e45;
    case 0x1e46: return 0x1e47;
    case 0x1e48: return 0x1e49;
    case 0x1e4a: return 0x1e4b;
    case 0x1e4c: return 0x1e4d;
    case 0x1e4e: return 0x1e4f;
    case 0x1e50: return 0x1e51;
    case 0x1e52: return 0x1e53;
    case 0x1e54: return 0x1e55;
    case 0x1e56: return 0x1e57;
    case 0x1e58: return 0x1e59;
    case 0x1e5a: return 0x1e5b;
    case 0x1e5c: return 0x1e5d;
    case 0x1e5e: return 0x1e5f;
    case 0x1e60: return 0x1e61;
    case 0x1e62: return 0x1e63;
    case 0x1e64: return 0x1e65;
    case 0x1e66: return 0x1e67;
    case 0x1e68: return 0x1e69;
    case 0x1e6a: return 0x1e6b;
    case 0x1e6c: return 0x1e6d;
    case 0x1e6e: return 0x1e6f;
    case 0x1e70: return 0x1e71;
    case 0x1e72: return 0x1e73;
    case 0x1e74: return 0x1e75;
    case 0x1e76: return 0x1e77;
    case 0x1e78: return 0x1e79;
    case 0x1e7a: return 0x1e7b;
    case 0x1e7c: return 0x1e7d;
    case 0x1e7e: return 0x1e7f;
    case 0x1e80: return 0x1e81;
    case 0x1e82: return 0x1e83;
    case 0x1e84: return 0x1e85;
    case 0x1e86: return 0x1e87;
    case 0x1e88: return 0x1e89;
    case 0x1e8a: return 0x1e8b;
    case 0x1e8c: return 0x1e8d;
    case 0x1e8e: return 0x1e8f;
    case 0x1e90: return 0x1e91;
    case 0x1e92: return 0x1e93;
    case 0x1e94: return 0x1e95;
    case 0x1e9b: return 0x1e61;
    case 0x1e9e: return 0xdf;
    case 0x1ea0: return 0x1ea1;
    case 0x1ea2: return 0x1ea3;
    case 0x1ea4: return 0x1ea5;
    case 0x1ea6: return 0x1ea7;
    case 0x1ea8: return 0x1ea9;
    case 0x1eaa: return 0x1eab;
    case 0x1eac: return 0x1ead;
    case 0x1eae: return 0x1eaf;
    case 0x1eb0: return 0x1eb1;
    case 0x1eb2: return 0x1eb3;
    case 0x1eb4: return 0x1eb5;
    case 0x1eb6: return 0x1eb7;
    case 0x1eb8: return 0x1eb9;
    case 0x1eba: return 0x1ebb;
    case 0x1ebc: return 0x1ebd;
    case 0x1ebe: return 0x1ebf;
    case 0x1ec0: return 0x1ec1;
    case 0x1ec2: return 0x1ec3;
    case 0x1ec4: return 0x1ec5;
    case 0x1ec6: return 0x1ec7;
    case 0x1ec8: return 0x1ec9;
    case 0x1eca: return 0x1ecb;
    case 0x1ecc: return 0x1ecd;
    case 0x1ece: return 0x1ecf;
    case 0x1ed0: return 0x1ed1;
    case 0x1ed2: return 0x1ed3;
    case 0x1ed4: return 0x1ed5;
    case 0x1ed6: return 0x1ed7;
    case 0x1ed8: return 0x1ed9;
    case 0x1eda: return 0x1edb;
    case 0x1edc: return 0x1edd;
    case 0x1ede: return 0x1edf;
    case 0x1ee0: return 0x1ee1;
    case 0x1ee2: return 0x1ee3;
    case 0x1ee4: return 0x1ee5;
    case 0x1ee6: return 0x1ee7;
    case 0x1ee8: return 0x1ee9;
    case 0x1eea: return 0x1eeb;
    case 0x1eec: return 0x1eed;
    case 0x1eee: return 0x1eef;
    case 0x1ef0: return 0x1ef1;
    case 0x1ef2: return 0x1ef3;
    case 0x1ef4: return 0x1ef5;
    case 0x1ef6: return 0x1ef7;
    case 0x1ef8: return 0x1ef9;
    case 0x1efa: return 0x1efb;
    case 0x1efc: return 0x1efd;
    case 0x1efe: return 0x1eff;
    case 0x1f08: return 0x1f00;
    case 0x1f09: return 0x1f01;
    case 0x1f0a: return 0x1f02;
    case 0x1f0b: return 0x1f03;
    case 0x1f0c: return 0x1f04;
    case 0x1f0d: return 0x1f05;
    case 0x1f0e: return 0x1f06;
    case 0x1f0f: return 0x1f07;
    case 0x1f18: return 0x1f10;
    case 0x1f19: return 0x1f11;
    case 0x1f1a: return 0x1f12;
    case 0x1f1b: return 0x1f13;
    case 0x1f1c: return 0x1f14;
    case 0x1f1d: return 0x1f15;
    case 0x1f28: return 0x1f20;
    case 0x1f29: return 0x1f21;
    case 0x1f2a: return 0x1f22;
    case 0x1f2b: return 0x1f23;
    case 0x1f2c: return 0x1f24;
    case 0x1f2d: return 0x1f25;
    case 0x1f2e: return 0x1f26;
    case 0x1f2f: return 0x1f27;
    case 0x1f38: return 0x1f30;
    case 0x1f39: return 0x1f31;
    case 0x1f3a: return 0x1f32;
    case 0x1f3b: return 0x1f33;
    case 0x1f3c: return 0x1f34;
    case 0x1f3d: return 0x1f35;
    case 0x1f3e: return 0x1f36;
    case 0x1f3f: return 0x1f37;
    case 0x1f48: return 0x1f40;
    case 0x1f49: return 0x1f41;
    case 0x1f4a: return 0x1f42;
    case 0x1f4b: return 0x1f43;
    case 0x1f4c: return 0x1f44;
    case 0x1f4d: return 0x1f45;
    case 0x1f59: return 0x1f51;
    case 0x1f5b: return 0x1f53;
    case 0x1f5d: return 0x1f55;
    case 0x1f5f: return 0x1f57;
    case 0x1f68: return 0x1f60;
    case 0x1f69: return 0x1f61;
    case 0x1f6a: return 0x1f62;
    case 0x1f6b: return 0x1f63;
    case 0x1f6c: return 0x1f64;
    case 0x1f6d: return 0x1f65;
    case 0x1f6e: return 0x1f66;
    case 0x1f6f: return 0x1f67;
    case 0x1f88: return 0x1f80;
    case 0x1f89: return 0x1f81;
    case 0x1f8a: return 0x1f82;
    case 0x1f8b: return 0x1f83;
    case 0x1f8c: return 0x1f84;
    case 0x1f8d: return 0x1f85;
    case 0x1f8e: return 0x1f86;
    case 0x1f8f: return 0x1f87;
    case 0x1f98: return 0x1f90;
    case 0x1f99: return 0x1f91;
    case 0x1f9a: return 0x1f92;
    case 0x1f9b: return 0x1f93;
    case 0x1f9c: return 0x1f94;
    case 0x1f9d: return 0x1f95;
    case 0x1f9e: return 0x1f96;
    case 0x1f9f: return 0x1f97;
    case 0x1fa8: return 0x1fa0;
    case 0x1fa9: return 0x1fa1;
    case 0x1faa: return 0x1fa2;
    case 0x1fab: return 0x1fa3;
    case 0x1fac: return 0x1fa4;
    case 0x1fad: return 0x1fa5;
    case 0x1fae: return 0x1fa6;
    case 0x1faf: return 0x1fa7;
    case 0x1fb8: return 0x1fb0;
    case 0x1fb9: return 0x1fb1;
    case 0x1fba: return 0x1f70;
    case 0x1fbb: return 0x1f71;
    case 0x1fbc: return 0x1fb3;
    case 0x1fbe: return 0x3b9;
    case 0x1fc8: return 0x1f72;
    case 0x1fc9: return 0x1f73;
    case 0x1fca: return 0x1f74;
    case 0x1fcb: return 0x1f75;
    case 0x1fcc: return 0x1fc3;
    case 0x1fd8: return 0x1fd0;
    case 0x1fd9: return 0x1fd1;
    case 0x1fda: return 0x1f76;
    case 0x1fdb: return 0x1f77;
    case 0x1fe8: return 0x1fe0;
    case 0x1fe9: return 0x1fe1;
    case 0x1fea: return 0x1f7a;
    case 0x1feb: return 0x1f7b;
    case 0x1fec: return 0x1fe5;
    case 0x1ff8: return 0x1f78;
    case 0x1ff9: return 0x1f79;
    case 0x1ffa: return 0x1f7c;
    case 0x1ffb: return 0x1f7d;
    case 0x1ffc: return 0x1ff3;
    case 0x2126: return 0x3c9;
    case 0x212a: return 0x6b;
    case 0x212b: return 0xe5;
    case 0x2132: return 0x214e;
    case 0x2160: return 0x2170;
    case 0x2161: return 0x2171;
    case 0x2162: return 0x2172;
    case 0x2163: return 0x2173;
    case 0x2164: return 0x2174;
    case 0x2165: return 0x2175;
    case 0x2166: return 0x2176;
    case 0x2167: return 0x2177;
    case 0x2168: return 0x2178;
    case 0x2169: return 0x2179;
    case 0x216a: return 0x217a;
    case 0x216b: return 0x217b;
    case 0x216c: return 0x217c;
    case 0x216d: return 0x217d;
    case 0x216e: return 0x217e;
    case 0x216f: return 0x217f;
    case 0x2183: return 0x2184;
    case 0x24b6: return 0x24d0;
    case 0x24b7: return 0x24d1;
    case 0x24b8: return 0x24d2;
    case 0x24b9: return 0x24d3;
    case 0x24ba: return 0x24d4;
    case 0x24bb: return 0x24d5;
    case 0x24bc: return 0x24d6;
    case 0x24bd: return 0x24d7;
    case 0x24be: return 0x24d8;
    case 0x24bf: return 0x24d9;
    case 0x24c0: return 0x24da;
    case 0x24c1: return 0x24db;
    case 0x24c2: return 0x24dc;
    case 0x24c3: return 0x24dd;
    case 0x24c4: return 0x24de;
    case 0x24c5: return 0x24df;
    case 0x24c6: return 0x24e0;
    case 0x24c7: return 0x24e1;
    case 0x24c8: return 0x24e2;
    case 0x24c9: return 0x24e3;
    case 0x24ca: return 0x24e4;
    case 0x24cb: return 0x24e5;
    case 0x24cc: return 0x24e6;
    case 0x24cd: return 0x24e7;
    case 0x24ce: return 0x24e8;
    case 0x24cf: return 0x24e9;
    case 0x2c00: return 0x2c30;
    case 0x2c01: return 0x2c31;
    case 0x2c02: return 0x2c32;
    case 0x2c03: return 0x2c33;
    case 0x2c04: return 0x2c34;
    case 0x2c05: return 0x2c35;
    case 0x2c06: return 0x2c36;
    case 0x2c07: return 0x2c37;
    case 0x2c08: return 0x2c38;
    case 0x2c09: return 0x2c39;
    case 0x2c0a: return 0x2c3a;
    case 0x2c0b: return 0x2c3b;
    case 0x2c0c: return 0x2c3c;
    case 0x2c0d: return 0x2c3d;
    case 0x2c0e: return 0x2c3e;
    case 0x2c0f: return 0x2c3f;
    case 0x2c10: return 0x2c40;
    case 0x2c11: return 0x2c41;
    case 0x2c12: return 0x2c42;
    case 0x2c13: return 0x2c43;
    case 0x2c14: return 0x2c44;
    case 0x2c15: return 0x2c45;
    case 0x2c16: return 0x2c46;
    case 0x2c17: return 0x2c47;
    case 0x2c18: return 0x2c48;
    case 0x2c19: return 0x2c49;
    case 0x2c1a: return 0x2c4a;
    case 0x2c1b: return 0x2c4b;
    case 0x2c1c: return 0x2c4c;
    case 0x2c1d: return 0x2c4d;
    case 0x2c1e: return 0x2c4e;
    case 0x2c1f: return 0x2c4f;
    case 0x2c20: return 0x2c50;
    case 0x2c21: return 0x2c51;
    case 0x2c22: return 0x2c52;
    case 0x2c23: return 0x2c53;
    case 0x2c24: return 0x2c54;
    case 0x2c25: return 0x2c55;
    case 0x2c26: return 0x2c56;
    case 0x2c27: return 0x2c57;
    case 0x2c28: return 0x2c58;
    case 0x2c29: return 0x2c59;
    case 0x2c2a: return 0x2c5a;
    case 0x2c2b: return 0x2c5b;
    case 0x2c2c: return 0x2c5c;
    case 0x2c2d: return 0x2c5d;
    case 0x2c2e: return 0x2c5e;
    case 0x2c60: return 0x2c61;
    case 0x2c62: return 0x26b;
    case 0x2c63: return 0x1d7d;
    case 0x2c64: return 0x27d;
    case 0x2c67: return 0x2c68;
    case 0x2c69: return 0x2c6a;
    case 0x2c6b: return 0x2c6c;
    case 0x2c6d: return 0x251;
    case 0x2c6e: return 0x271;
    case 0x2c6f: return 0x250;
    case 0x2c70: return 0x252;
    case 0x2c72: return 0x2c73;
    case 0x2c75: return 0x2c76;
    case 0x2c7e: return 0x23f;
    case 0x2c7f: return 0x240;
    case 0x2c80: return 0x2c81;
    case 0x2c82: return 0x2c83;
    case 0x2c84: return 0x2c85;
    case 0x2c86: return 0x2c87;
    case 0x2c88: return 0x2c89;
    case 0x2c8a: return 0x2c8b;
    case 0x2c8c: return 0x2c8d;
    case 0x2c8e: return 0x2c8f;
    case 0x2c90: return 0x2c91;
    case 0x2c92: return 0x2c93;
    case 0x2c94: return 0x2c95;
    case 0x2c96: return 0x2c97;
    case 0x2c98: return 0x2c99;
    case 0x2c9a: return 0x2c9b;
    case 0x2c9c: return 0x2c9d;
    case 0x2c9e: return 0x2c9f;
    case 0x2ca0: return 0x2ca1;
    case 0x2ca2: return 0x2ca3;
    case 0x2ca4: return 0x2ca5;
    case 0x2ca6: return 0x2ca7;
    case 0x2ca8: return 0x2ca9;
    case 0x2caa: return 0x2cab;
    case 0x2cac: return 0x2cad;
    case 0x2cae: return 0x2caf;
    case 0x2cb0: return 0x2cb1;
    case 0x2cb2: return 0x2cb3;
    case 0x2cb4: return 0x2cb5;
    case 0x2cb6: return 0x2cb7;
    case 0x2cb8: return 0x2cb9;
    case 0x2cba: return 0x2cbb;
    case 0x2cbc: return 0x2cbd;
    case 0x2cbe: return 0x2cbf;
    case 0x2cc0: return 0x2cc1;
    case 0x2cc2: return 0x2cc3;
    case 0x2cc4: return 0x2cc5;
    case 0x2cc6: return 0x2cc7;
    case 0x2cc8: return 0x2cc9;
    case 0x2cca: return 0x2ccb;
    case 0x2ccc: return 0x2ccd;
    case 0x2cce: return 0x2ccf;
    case 0x2cd0: return 0x2cd1;
    case 0x2cd2: return 0x2cd3;
    case 0x2cd4: return 0x2cd5;
    case 0x2cd6: return 0x2cd7;
    case 0x2cd8: return 0x2cd9;
    case 0x2cda: return 0x2cdb;
    case 0x2cdc: return 0x2cdd;
    case 0x2cde: return 0x2cdf;
    case 0x2ce0: return 0x2ce1;
    case 0x2ce2: return 0x2ce3;
    case 0x2ceb: return 0x2cec;
    case 0x2ced: return 0x2cee;
    case 0x2cf2: return 0x2cf3;
    case 0xa640: return 0xa641;
    case 0xa642: return 0xa643;
    case 0xa644: return 0xa645;
    case 0xa646: return 0xa647;
    case 0xa648: return 0xa649;
    case 0xa64a: return 0xa64b;
    case 0xa64c: return 0xa64d;
    case 0xa64e: return 0xa64f;
    case 0xa650: return 0xa651;
    case 0xa652: return 0xa653;
    case 0xa654: return 0xa655;
    case 0xa656: return 0xa657;
    case 0xa658: return 0xa659;
    case 0xa65a: return 0xa65b;
    case 0xa65c: return 0xa65d;
    case 0xa65e: return 0xa65f;
    case 0xa660: return 0xa661;
    case 0xa662: return 0xa663;
    case 0xa664: return 0xa665;
    case 0xa666: return 0xa667;
    case 0xa668: return 0xa669;
    case 0xa66a: return 0xa66b;
    case 0xa66c: return 0xa66d;
    case 0xa680: return 0xa681;
    case 0xa682: return 0xa683;
    case 0xa684: return 0xa685;
    case 0xa686: return 0xa687;
    case 0xa688: return 0xa689;
    case 0xa68a: return 0xa68b;
    case 0xa68c: return 0xa68d;
    case 0xa68e: return 0xa68f;
    case 0xa690: return 0xa691;
    case 0xa692: return 0xa693;
    case 0xa694: return 0xa695;
    case 0xa696: return 0xa697;
    case 0xa698: return 0xa699;
    case 0xa69a: return 0xa69b;
    case 0xa722: return 0xa723;
    case 0xa724: return 0xa725;
    case 0xa726: return 0xa727;
    case 0xa728: return 0xa729;
    case 0xa72a: return 0xa72b;
    case 0xa72c: return 0xa72d;
    case 0xa72e: return 0xa72f;
    case 0xa732: return 0xa733;
    case 0xa734: return 0xa735;
    case 0xa736: return 0xa737;
    case 0xa738: return 0xa739;
    case 0xa73a: return 0xa73b;
    case 0xa73c: return 0xa73d;
    case 0xa73e: return 0xa73f;
    case 0xa740: return 0xa741;
    case 0xa742: return 0xa743;
    case 0xa744: return 0xa745;
    case 0xa746: return 0xa747;
    case 0xa748: return 0xa749;
    case 0xa74a: return 0xa74b;
    case 0xa74c: return 0xa74d;
    case 0xa74e: return 0xa74f;
    case 0xa750: return 0xa751;
    case 0xa752: return 0xa753;
    case 0xa754: return 0xa755;
    case 0xa756: return 0xa757;
    case 0xa758: return 0xa759;
    case 0xa75a: return 0xa75b;
    case 0xa75c: return 0xa75d;
    case 0xa75e: return 0xa75f;
    case 0xa760: return 0xa761;
    case 0xa762: return 0xa763;
    case 0xa764: return 0xa765;
    case 0xa766: return 0xa767;
    case 0xa768: return 0xa769;
    case 0xa76a: return 0xa76b;
    case 0xa76c: return 0xa76d;
    case 0xa76e: return 0xa76f;
    case 0xa779: return 0xa77a;
    case 0xa77b: return 0xa77c;
    case 0xa77d: return 0x1d79;
    case 0xa77e: return 0xa77f;
    case 0xa780: return 0xa781;
    case 0xa782: return 0xa783;
    case 0xa784: return 0xa785;
    case 0xa786: return 0xa787;
    case 0xa78b: return 0xa78c;
    case 0xa78d: return 0x265;
    case 0xa790: return 0xa791;
    case 0xa792: return 0xa793;
    case 0xa796: return 0xa797;
    case 0xa798: return 0xa799;
    case 0xa79a: return 0xa79b;
    case 0xa79c: return 0xa79d;
    case 0xa79e: return 0xa79f;
    case 0xa7a0: return 0xa7a1;
    case 0xa7a2: return 0xa7a3;
    case 0xa7a4: return 0xa7a5;
    case 0xa7a6: return 0xa7a7;
    case 0xa7a8: return 0xa7a9;
    case 0xa7aa: return 0x266;
    case 0xa7ab: return 0x25c;
    case 0xa7ac: return 0x261;
    case 0xa7ad: return 0x26c;
    case 0xa7ae: return 0x26a;
    case 0xa7b0: return 0x29e;
    case 0xa7b1: return 0x287;
    case 0xa7b2: return 0x29d;
    case 0xa7b3: return 0xab53;
    case 0xa7b4: return 0xa7b5;
    case 0xa7b6: return 0xa7b7;
    case 0xa7b8: return 0xa7b9;
    case 0xa7ba: return 0xa7bb;
    case 0xa7bc: return 0xa7bd;
    case 0xa7be: return 0xa7bf;
    case 0xa7c2: return 0xa7c3;
    case 0xa7c4: return 0xa794;
    case 0xa7c5: return 0x282;
    case 0xa7c6: return 0x1d8e;
    case 0xa7c7: return 0xa7c8;
    case 0xa7c9: return 0xa7ca;
    case 0xa7f5: return 0xa7f6;
    case 0xab70: return 0x13a0;
    case 0xab71: return 0x13a1;
    case 0xab72: return 0x13a2;
    case 0xab73: return 0x13a3;
    case 0xab74: return 0x13a4;
    case 0xab75: return 0x13a5;
    case 0xab76: return 0x13a6;
    case 0xab77: return 0x13a7;
    case 0xab78: return 0x13a8;
    case 0xab79: return 0x13a9;
    case 0xab7a: return 0x13aa;
    case 0xab7b: return 0x13ab;
    case 0xab7c: return 0x13ac;
    case 0xab7d: return 0x13ad;
    case 0xab7e: return 0x13ae;
    case 0xab7f: return 0x13af;
    case 0xab80: return 0x13b0;
    case 0xab81: return 0x13b1;
    case 0xab82: return 0x13b2;
    case 0xab83: return 0x13b3;
    case 0xab84: return 0x13b4;
    case 0xab85: return 0x13b5;
    case 0xab86: return 0x13b6;
    case 0xab87: return 0x13b7;
    case 0xab88: return 0x13b8;
    case 0xab89: return 0x13b9;
    case 0xab8a: return 0x13ba;
    case 0xab8b: return 0x13bb;
    case 0xab8c: return 0x13bc;
    case 0xab8d: return 0x13bd;
    case 0xab8e: return 0x13be;
    case 0xab8f: return 0x13bf;
    case 0xab90: return 0x13c0;
    case 0xab91: return 0x13c1;
    case 0xab92: return 0x13c2;
    case 0xab93: return 0x13c3;
    case 0xab94: return 0x13c4;
    case 0xab95: return 0x13c5;
    case 0xab96: return 0x13c6;
    case 0xab97: return 0x13c7;
    case 0xab98: return 0x13c8;
    case 0xab99: return 0x13c9;
    case 0xab9a: return 0x13ca;
    case 0xab9b: return 0x13cb;
    case 0xab9c: return 0x13cc;
    case 0xab9d: return 0x13cd;
    case 0xab9e: return 0x13ce;
    case 0xab9f: return 0x13cf;
    case 0xaba0: return 0x13d0;
    case 0xaba1: return 0x13d1;
    case 0xaba2: return 0x13d2;
    case 0xaba3: return 0x13d3;
    case 0xaba4: return 0x13d4;
    case 0xaba5: return 0x13d5;
    case 0xaba6: return 0x13d6;
    case 0xaba7: return 0x13d7;
    case 0xaba8: return 0x13d8;
    case 0xaba9: return 0x13d9;
    case 0xabaa: return 0x13da;
    case 0xabab: return 0x13db;
    case 0xabac: return 0x13dc;
    case 0xabad: return 0x13dd;
    case 0xabae: return 0x13de;
    case 0xabaf: return 0x13df;
    case 0xabb0: return 0x13e0;
    case 0xabb1: return 0x13e1;
    case 0xabb2: return 0x13e2;
    case 0xabb3: return 0x13e3;
    case 0xabb4: return 0x13e4;
    case 0xabb5: return 0x13e5;
    case 0xabb6: return 0x13e6;
    case 0xabb7: return 0x13e7;
    case 0xabb8: return 0x13e8;
    case 0xabb9: return 0x13e9;
    case 0xabba: return 0x13ea;
    case 0xabbb: return 0x13eb;
    case 0xabbc: return 0x13ec;
    case 0xabbd: return 0x13ed;
    case 0xabbe: return 0x13ee;
    case 0xabbf: return 0x13ef;
    case 0xff21: return 0xff41;
    case 0xff22: return 0xff42;
    case 0xff23: return 0xff43;
    case 0xff24: return 0xff44;
    case 0xff25: return 0xff45;
    case 0xff26: return 0xff46;
    case 0xff27: return 0xff47;
    case 0xff28: return 0xff48;
    case 0xff29: return 0xff49;
    case 0xff2a: return 0xff4a;
    case 0xff2b: return 0xff4b;
    case 0xff2c: return 0xff4c;
    case 0xff2d: return 0xff4d;
    case 0xff2e: return 0xff4e;
    case 0xff2f: return 0xff4f;
    case 0xff30: return 0xff50;
    case 0xff31: return 0xff51;
    case 0xff32: return 0xff52;
    case 0xff33: return 0xff53;
    case 0xff34: return 0xff54;
    case 0xff35: return 0xff55;
    case 0xff36: return 0xff56;
    case 0xff37: return 0xff57;
    case 0xff38: return 0xff58;
    case 0xff39: return 0xff59;
    case 0xff3a: return 0xff5a;
    case 0x10400: return 0x10428;
    case 0x10401: return 0x10429;
    case 0x10402: return 0x1042a;
    case 0x10403: return 0x1042b;
    case 0x10404: return 0x1042c;
    case 0x10405: return 0x1042d;
    case 0x10406: return 0x1042e;
    case 0x10407: return 0x1042f;
    case 0x10408: return 0x10430;
    case 0x10409: return 0x10431;
    case 0x1040a: return 0x10432;
    case 0x1040b: return 0x10433;
    case 0x1040c: return 0x10434;
    case 0x1040d: return 0x10435;
    case 0x1040e: return 0x10436;
    case 0x1040f: return 0x10437;
    case 0x10410: return 0x10438;
    case 0x10411: return 0x10439;
    case 0x10412: return 0x1043a;
    case 0x10413: return 0x1043b;
    case 0x10414: return 0x1043c;
    case 0x10415: return 0x1043d;
    case 0x10416: return 0x1043e;
    case 0x10417: return 0x1043f;
    case 0x10418: return 0x10440;
    case 0x10419: return 0x10441;
    case 0x1041a: return 0x10442;
    case 0x1041b: return 0x10443;
    case 0x1041c: return 0x10444;
    case 0x1041d: return 0x10445;
    case 0x1041e: return 0x10446;
    case 0x1041f: return 0x10447;
    case 0x10420: return 0x10448;
    case 0x10421: return 0x10449;
    case 0x10422: return 0x1044a;
    case 0x10423: return 0x1044b;
    case 0x10424: return 0x1044c;
    case 0x10425: return 0x1044d;
    case 0x10426: return 0x1044e;
    case 0x10427: return 0x1044f;
    case 0x104b0: return 0x104d8;
    case 0x104b1: return 0x104d9;
    case 0x104b2: return 0x104da;
    case 0x104b3: return 0x104db;
    case 0x104b4: return 0x104dc;
    case 0x104b5: return 0x104dd;
    case 0x104b6: return 0x104de;
    case 0x104b7: return 0x104df;
    case 0x104b8: return 0x104e0;
    case 0x104b9: return 0x104e1;
    case 0x104ba: return 0x104e2;
    case 0x104bb: return 0x104e3;
    case 0x104bc: return 0x104e4;
    case 0x104bd: return 0x104e5;
    case 0x104be: return 0x104e6;
    case 0x104bf: return 0x104e7;
    case 0x104c0: return 0x104e8;
    case 0x104c1: return 0x104e9;
    case 0x104c2: return 0x104ea;
    case 0x104c3: return 0x104eb;
    case 0x104c4: return 0x104ec;
    case 0x104c5: return 0x104ed;
    case 0x104c6: return 0x104ee;
    case 0x104c7: return 0x104ef;
    case 0x104c8: return 0x104f0;
    case 0x104c9: return 0x104f1;
    case 0x104ca: return 0x104f2;
    case 0x104cb: return 0x104f3;
    case 0x104cc: return 0x104f4;
    case 0x104cd: return 0x104f5;
    case 0x104ce: return 0x104f6;
    case 0x104cf: return 0x104f7;
    case 0x104d0: return 0x104f8;
    case 0x104d1: return 0x104f9;
    case 0x104d2: return 0x104fa;
    case 0x104d3: return 0x104fb;
    case 0x10c80: return 0x10cc0;
    case 0x10c81: return 0x10cc1;
    case 0x10c82: return 0x10cc2;
    case 0x10c83: return 0x10cc3;
    case 0x10c84: return 0x10cc4;
    case 0x10c85: return 0x10cc5;
    case 0x10c86: return 0x10cc6;
    case 0x10c87: return 0x10cc7;
    case 0x10c88: return 0x10cc8;
    case 0x10c89: return 0x10cc9;
    case 0x10c8a: return 0x10cca;
    case 0x10c8b: return 0x10ccb;
    case 0x10c8c: return 0x10ccc;
    case 0x10c8d: return 0x10ccd;
    case 0x10c8e: return 0x10cce;
    case 0x10c8f: return 0x10ccf;
    case 0x10c90: return 0x10cd0;
    case 0x10c91: return 0x10cd1;
    case 0x10c92: return 0x10cd2;
    case 0x10c93: return 0x10cd3;
    case 0x10c94: return 0x10cd4;
    case 0x10c95: return 0x10cd5;
    case 0x10c96: return 0x10cd6;
    case 0x10c97: return 0x10cd7;
    case 0x10c98: return 0x10cd8;
    case 0x10c99: return 0x10cd9;
    case 0x10c9a: return 0x10cda;
    case 0x10c9b: return 0x10cdb;
    case 0x10c9c: return 0x10cdc;
    case 0x10c9d: return 0x10cdd;
    case 0x10c9e: return 0x10cde;
    case 0x10c9f: return 0x10cdf;
    case 0x10ca0: return 0x10ce0;
    case 0x10ca1: return 0x10ce1;
    case 0x10ca2: return 0x10ce2;
    case 0x10ca3: return 0x10ce3;
    case 0x10ca4: return 0x10ce4;
    case 0x10ca5: return 0x10ce5;
    case 0x10ca6: return 0x10ce6;
    case 0x10ca7: return 0x10ce7;
    case 0x10ca8: return 0x10ce8;
    case 0x10ca9: return 0x10ce9;
    case 0x10caa: return 0x10cea;
    case 0x10cab: return 0x10ceb;
    case 0x10cac: return 0x10cec;
    case 0x10cad: return 0x10ced;
    case 0x10cae: return 0x10cee;
    case 0x10caf: return 0x10cef;
    case 0x10cb0: return 0x10cf0;
    case 0x10cb1: return 0x10cf1;
    case 0x10cb2: return 0x10cf2;
    case 0x118a0: return 0x118c0;
    case 0x118a1: return 0x118c1;
    case 0x118a2: return 0x118c2;
    case 0x118a3: return 0x118c3;
    case 0x118a4: return 0x118c4;
    case 0x118a5: return 0x118c5;
    case 0x118a6: return 0x118c6;
    case 0x118a7: return 0x118c7;
    case 0x118a8: return 0x118c8;
    case 0x118a9: return 0x118c9;
    case 0x118aa: return 0x118ca;
    case 0x118ab: return 0x118cb;
    case 0x118ac: return 0x118cc;
    case 0x118ad: return 0x118cd;
    case 0x118ae: return 0x118ce;
    case 0x118af: return 0x118cf;
    case 0x118b0: return 0x118d0;
    case 0x118b1: return 0x118d1;
    case 0x118b2: return 0x118d2;
    case 0x118b3: return 0x118d3;
    case 0x118b4: return 0x118d4;
    case 0x118b5: return 0x118d5;
    case 0x118b6: return 0x118d6;
    case 0x118b7: return 0x118d7;
    case 0x118b8: return 0x118d8;
    case 0x118b9: return 0x118d9;
    case 0x118ba: return 0x118da;
    case 0x118bb: return 0x118db;
    case 0x118bc: return 0x118dc;
    case 0x118bd: return 0x118dd;
    case 0x118be: return 0x118de;
    case 0x118bf: return 0x118df;
    case 0x16e40: return 0x16e60;
    case 0x16e41: return 0x16e61;
    case 0x16e42: return 0x16e62;
    case 0x16e43: return 0x16e63;
    case 0x16e44: return 0x16e64;
    case 0x16e45: return 0x16e65;
    case 0x16e46: return 0x16e66;
    case 0x16e47: return 0x16e67;
    case 0x16e48: return 0x16e68;
    case 0x16e49: return 0x16e69;
    case 0x16e4a: return 0x16e6a;
    case 0x16e4b: return 0x16e6b;
    case 0x16e4c: return 0x16e6c;
    case 0x16e4d: return 0x16e6d;
    case 0x16e4e: return 0x16e6e;
    case 0x16e4f: return 0x16e6f;
    case 0x16e50: return 0x16e70;
    case 0x16e51: return 0x16e71;
    case 0x16e52: return 0x16e72;
    case 0x16e53: return 0x16e73;
    case 0x16e54: return 0x16e74;
    case 0x16e55: return 0x16e75;
    case 0x16e56: return 0x16e76;
    case 0x16e57: return 0x16e77;
    case 0x16e58: return 0x16e78;
    case 0x16e59: return 0x16e79;
    case 0x16e5a: return 0x16e7a;
    case 0x16e5b: return 0x16e7b;
    case 0x16e5c: return 0x16e7c;
    case 0x16e5d: return 0x16e7d;
    case 0x16e5e: return 0x16e7e;
    case 0x16e5f: return 0x16e7f;
    case 0x1e900: return 0x1e922;
    case 0x1e901: return 0x1e923;
    case 0x1e902: return 0x1e924;
    case 0x1e903: return 0x1e925;
    case 0x1e904: return 0x1e926;
    case 0x1e905: return 0x1e927;
    case 0x1e906: return 0x1e928;
    case 0x1e907: return 0x1e929;
    case 0x1e908: return 0x1e92a;
    case 0x1e909: return 0x1e92b;
    case 0x1e90a: return 0x1e92c;
    case 0x1e90b: return 0x1e92d;
    case 0x1e90c: return 0x1e92e;
    case 0x1e90d: return 0x1e92f;
    case 0x1e90e: return 0x1e930;
    case 0x1e90f: return 0x1e931;
    case 0x1e910: return 0x1e932;
    case 0x1e911: return 0x1e933;
    case 0x1e912: return 0x1e934;
    case 0x1e913: return 0x1e935;
    case 0x1e914: return 0x1e936;
    case 0x1e915: return 0x1e937;
    case 0x1e916: return 0x1e938;
    case 0x1e917: return 0x1e939;
    case 0x1e918: return 0x1e93a;
    case 0x1e919: return 0x1e93b;
    case 0x1e91a: return 0x1e93c;
    case 0x1e91b: return 0x1e93d;
    case 0x1e91c: return 0x1e93e;
    case 0x1e91d: return 0x1e93f;
    case 0x1e91e: return 0x1e940;
    case 0x1e91f: return 0x1e941;
    case 0x1e920: return 0x1e942;
    case 0x1e921: return 0x1e943;
    default: return codepoint;
    }
}
libmongocrypt-1.19.0/src/unicode/diacritic-fold-map.c000066400000000000000000002553341521103432300225110ustar00rootroot00000000000000/**
 *    Copyright (C) 2025-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    .
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 *
 *    THIS IS A GENERATED FILE, DO NOT MODIFY.
 */

#include "bson/bson.h"
#include "fold.h"

bson_unichar_t unicode_codepoint_remove_diacritics(bson_unichar_t codepoint) {
    switch (codepoint) {
    case 0x5e: return 0x0;
    case 0x60: return 0x0;
    case 0xa8: return 0x0;
    case 0xaf: return 0x0;
    case 0xb4: return 0x0;
    case 0xb7: return 0x0;
    case 0xb8: return 0x0;
    case 0xc0: return 0x41;
    case 0xc1: return 0x41;
    case 0xc2: return 0x41;
    case 0xc3: return 0x41;
    case 0xc4: return 0x41;
    case 0xc5: return 0x41;
    case 0xc7: return 0x43;
    case 0xc8: return 0x45;
    case 0xc9: return 0x45;
    case 0xca: return 0x45;
    case 0xcb: return 0x45;
    case 0xcc: return 0x49;
    case 0xcd: return 0x49;
    case 0xce: return 0x49;
    case 0xcf: return 0x49;
    case 0xd1: return 0x4e;
    case 0xd2: return 0x4f;
    case 0xd3: return 0x4f;
    case 0xd4: return 0x4f;
    case 0xd5: return 0x4f;
    case 0xd6: return 0x4f;
    case 0xd9: return 0x55;
    case 0xda: return 0x55;
    case 0xdb: return 0x55;
    case 0xdc: return 0x55;
    case 0xdd: return 0x59;
    case 0xe0: return 0x61;
    case 0xe1: return 0x61;
    case 0xe2: return 0x61;
    case 0xe3: return 0x61;
    case 0xe4: return 0x61;
    case 0xe5: return 0x61;
    case 0xe7: return 0x63;
    case 0xe8: return 0x65;
    case 0xe9: return 0x65;
    case 0xea: return 0x65;
    case 0xeb: return 0x65;
    case 0xec: return 0x69;
    case 0xed: return 0x69;
    case 0xee: return 0x69;
    case 0xef: return 0x69;
    case 0xf1: return 0x6e;
    case 0xf2: return 0x6f;
    case 0xf3: return 0x6f;
    case 0xf4: return 0x6f;
    case 0xf5: return 0x6f;
    case 0xf6: return 0x6f;
    case 0xf9: return 0x75;
    case 0xfa: return 0x75;
    case 0xfb: return 0x75;
    case 0xfc: return 0x75;
    case 0xfd: return 0x79;
    case 0xff: return 0x79;
    case 0x100: return 0x41;
    case 0x101: return 0x61;
    case 0x102: return 0x41;
    case 0x103: return 0x61;
    case 0x104: return 0x41;
    case 0x105: return 0x61;
    case 0x106: return 0x43;
    case 0x107: return 0x63;
    case 0x108: return 0x43;
    case 0x109: return 0x63;
    case 0x10a: return 0x43;
    case 0x10b: return 0x63;
    case 0x10c: return 0x43;
    case 0x10d: return 0x63;
    case 0x10e: return 0x44;
    case 0x10f: return 0x64;
    case 0x112: return 0x45;
    case 0x113: return 0x65;
    case 0x114: return 0x45;
    case 0x115: return 0x65;
    case 0x116: return 0x45;
    case 0x117: return 0x65;
    case 0x118: return 0x45;
    case 0x119: return 0x65;
    case 0x11a: return 0x45;
    case 0x11b: return 0x65;
    case 0x11c: return 0x47;
    case 0x11d: return 0x67;
    case 0x11e: return 0x47;
    case 0x11f: return 0x67;
    case 0x120: return 0x47;
    case 0x121: return 0x67;
    case 0x122: return 0x47;
    case 0x123: return 0x67;
    case 0x124: return 0x48;
    case 0x125: return 0x68;
    case 0x128: return 0x49;
    case 0x129: return 0x69;
    case 0x12a: return 0x49;
    case 0x12b: return 0x69;
    case 0x12c: return 0x49;
    case 0x12d: return 0x69;
    case 0x12e: return 0x49;
    case 0x12f: return 0x69;
    case 0x130: return 0x49;
    case 0x134: return 0x4a;
    case 0x135: return 0x6a;
    case 0x136: return 0x4b;
    case 0x137: return 0x6b;
    case 0x139: return 0x4c;
    case 0x13a: return 0x6c;
    case 0x13b: return 0x4c;
    case 0x13c: return 0x6c;
    case 0x13d: return 0x4c;
    case 0x13e: return 0x6c;
    case 0x143: return 0x4e;
    case 0x144: return 0x6e;
    case 0x145: return 0x4e;
    case 0x146: return 0x6e;
    case 0x147: return 0x4e;
    case 0x148: return 0x6e;
    case 0x14c: return 0x4f;
    case 0x14d: return 0x6f;
    case 0x14e: return 0x4f;
    case 0x14f: return 0x6f;
    case 0x150: return 0x4f;
    case 0x151: return 0x6f;
    case 0x154: return 0x52;
    case 0x155: return 0x72;
    case 0x156: return 0x52;
    case 0x157: return 0x72;
    case 0x158: return 0x52;
    case 0x159: return 0x72;
    case 0x15a: return 0x53;
    case 0x15b: return 0x73;
    case 0x15c: return 0x53;
    case 0x15d: return 0x73;
    case 0x15e: return 0x53;
    case 0x15f: return 0x73;
    case 0x160: return 0x53;
    case 0x161: return 0x73;
    case 0x162: return 0x54;
    case 0x163: return 0x74;
    case 0x164: return 0x54;
    case 0x165: return 0x74;
    case 0x168: return 0x55;
    case 0x169: return 0x75;
    case 0x16a: return 0x55;
    case 0x16b: return 0x75;
    case 0x16c: return 0x55;
    case 0x16d: return 0x75;
    case 0x16e: return 0x55;
    case 0x16f: return 0x75;
    case 0x170: return 0x55;
    case 0x171: return 0x75;
    case 0x172: return 0x55;
    case 0x173: return 0x75;
    case 0x174: return 0x57;
    case 0x175: return 0x77;
    case 0x176: return 0x59;
    case 0x177: return 0x79;
    case 0x178: return 0x59;
    case 0x179: return 0x5a;
    case 0x17a: return 0x7a;
    case 0x17b: return 0x5a;
    case 0x17c: return 0x7a;
    case 0x17d: return 0x5a;
    case 0x17e: return 0x7a;
    case 0x1a0: return 0x4f;
    case 0x1a1: return 0x6f;
    case 0x1af: return 0x55;
    case 0x1b0: return 0x75;
    case 0x1cd: return 0x41;
    case 0x1ce: return 0x61;
    case 0x1cf: return 0x49;
    case 0x1d0: return 0x69;
    case 0x1d1: return 0x4f;
    case 0x1d2: return 0x6f;
    case 0x1d3: return 0x55;
    case 0x1d4: return 0x75;
    case 0x1d5: return 0x55;
    case 0x1d6: return 0x75;
    case 0x1d7: return 0x55;
    case 0x1d8: return 0x75;
    case 0x1d9: return 0x55;
    case 0x1da: return 0x75;
    case 0x1db: return 0x55;
    case 0x1dc: return 0x75;
    case 0x1de: return 0x41;
    case 0x1df: return 0x61;
    case 0x1e0: return 0x41;
    case 0x1e1: return 0x61;
    case 0x1e2: return 0xc6;
    case 0x1e3: return 0xe6;
    case 0x1e6: return 0x47;
    case 0x1e7: return 0x67;
    case 0x1e8: return 0x4b;
    case 0x1e9: return 0x6b;
    case 0x1ea: return 0x4f;
    case 0x1eb: return 0x6f;
    case 0x1ec: return 0x4f;
    case 0x1ed: return 0x6f;
    case 0x1ee: return 0x1b7;
    case 0x1ef: return 0x292;
    case 0x1f0: return 0x6a;
    case 0x1f4: return 0x47;
    case 0x1f5: return 0x67;
    case 0x1f8: return 0x4e;
    case 0x1f9: return 0x6e;
    case 0x1fa: return 0x41;
    case 0x1fb: return 0x61;
    case 0x1fc: return 0xc6;
    case 0x1fd: return 0xe6;
    case 0x1fe: return 0xd8;
    case 0x1ff: return 0xf8;
    case 0x200: return 0x41;
    case 0x201: return 0x61;
    case 0x202: return 0x41;
    case 0x203: return 0x61;
    case 0x204: return 0x45;
    case 0x205: return 0x65;
    case 0x206: return 0x45;
    case 0x207: return 0x65;
    case 0x208: return 0x49;
    case 0x209: return 0x69;
    case 0x20a: return 0x49;
    case 0x20b: return 0x69;
    case 0x20c: return 0x4f;
    case 0x20d: return 0x6f;
    case 0x20e: return 0x4f;
    case 0x20f: return 0x6f;
    case 0x210: return 0x52;
    case 0x211: return 0x72;
    case 0x212: return 0x52;
    case 0x213: return 0x72;
    case 0x214: return 0x55;
    case 0x215: return 0x75;
    case 0x216: return 0x55;
    case 0x217: return 0x75;
    case 0x218: return 0x53;
    case 0x219: return 0x73;
    case 0x21a: return 0x54;
    case 0x21b: return 0x74;
    case 0x21e: return 0x48;
    case 0x21f: return 0x68;
    case 0x226: return 0x41;
    case 0x227: return 0x61;
    case 0x228: return 0x45;
    case 0x229: return 0x65;
    case 0x22a: return 0x4f;
    case 0x22b: return 0x6f;
    case 0x22c: return 0x4f;
    case 0x22d: return 0x6f;
    case 0x22e: return 0x4f;
    case 0x22f: return 0x6f;
    case 0x230: return 0x4f;
    case 0x231: return 0x6f;
    case 0x232: return 0x59;
    case 0x233: return 0x79;
    case 0x2b0: return 0x0;
    case 0x2b1: return 0x0;
    case 0x2b2: return 0x0;
    case 0x2b3: return 0x0;
    case 0x2b4: return 0x0;
    case 0x2b5: return 0x0;
    case 0x2b6: return 0x0;
    case 0x2b7: return 0x0;
    case 0x2b8: return 0x0;
    case 0x2b9: return 0x0;
    case 0x2ba: return 0x0;
    case 0x2bb: return 0x0;
    case 0x2bc: return 0x0;
    case 0x2bd: return 0x0;
    case 0x2be: return 0x0;
    case 0x2bf: return 0x0;
    case 0x2c0: return 0x0;
    case 0x2c1: return 0x0;
    case 0x2c2: return 0x0;
    case 0x2c3: return 0x0;
    case 0x2c4: return 0x0;
    case 0x2c5: return 0x0;
    case 0x2c6: return 0x0;
    case 0x2c7: return 0x0;
    case 0x2c8: return 0x0;
    case 0x2c9: return 0x0;
    case 0x2ca: return 0x0;
    case 0x2cb: return 0x0;
    case 0x2cc: return 0x0;
    case 0x2cd: return 0x0;
    case 0x2ce: return 0x0;
    case 0x2cf: return 0x0;
    case 0x2d0: return 0x0;
    case 0x2d1: return 0x0;
    case 0x2d2: return 0x0;
    case 0x2d3: return 0x0;
    case 0x2d4: return 0x0;
    case 0x2d5: return 0x0;
    case 0x2d6: return 0x0;
    case 0x2d7: return 0x0;
    case 0x2d8: return 0x0;
    case 0x2d9: return 0x0;
    case 0x2da: return 0x0;
    case 0x2db: return 0x0;
    case 0x2dc: return 0x0;
    case 0x2dd: return 0x0;
    case 0x2de: return 0x0;
    case 0x2df: return 0x0;
    case 0x2e0: return 0x0;
    case 0x2e1: return 0x0;
    case 0x2e2: return 0x0;
    case 0x2e3: return 0x0;
    case 0x2e4: return 0x0;
    case 0x2e5: return 0x0;
    case 0x2e6: return 0x0;
    case 0x2e7: return 0x0;
    case 0x2e8: return 0x0;
    case 0x2e9: return 0x0;
    case 0x2ea: return 0x0;
    case 0x2eb: return 0x0;
    case 0x2ec: return 0x0;
    case 0x2ed: return 0x0;
    case 0x2ee: return 0x0;
    case 0x2ef: return 0x0;
    case 0x2f0: return 0x0;
    case 0x2f1: return 0x0;
    case 0x2f2: return 0x0;
    case 0x2f3: return 0x0;
    case 0x2f4: return 0x0;
    case 0x2f5: return 0x0;
    case 0x2f6: return 0x0;
    case 0x2f7: return 0x0;
    case 0x2f8: return 0x0;
    case 0x2f9: return 0x0;
    case 0x2fa: return 0x0;
    case 0x2fb: return 0x0;
    case 0x2fc: return 0x0;
    case 0x2fd: return 0x0;
    case 0x2fe: return 0x0;
    case 0x2ff: return 0x0;
    case 0x300: return 0x0;
    case 0x301: return 0x0;
    case 0x302: return 0x0;
    case 0x303: return 0x0;
    case 0x304: return 0x0;
    case 0x305: return 0x0;
    case 0x306: return 0x0;
    case 0x307: return 0x0;
    case 0x308: return 0x0;
    case 0x309: return 0x0;
    case 0x30a: return 0x0;
    case 0x30b: return 0x0;
    case 0x30c: return 0x0;
    case 0x30d: return 0x0;
    case 0x30e: return 0x0;
    case 0x30f: return 0x0;
    case 0x310: return 0x0;
    case 0x311: return 0x0;
    case 0x312: return 0x0;
    case 0x313: return 0x0;
    case 0x314: return 0x0;
    case 0x315: return 0x0;
    case 0x316: return 0x0;
    case 0x317: return 0x0;
    case 0x318: return 0x0;
    case 0x319: return 0x0;
    case 0x31a: return 0x0;
    case 0x31b: return 0x0;
    case 0x31c: return 0x0;
    case 0x31d: return 0x0;
    case 0x31e: return 0x0;
    case 0x31f: return 0x0;
    case 0x320: return 0x0;
    case 0x321: return 0x0;
    case 0x322: return 0x0;
    case 0x323: return 0x0;
    case 0x324: return 0x0;
    case 0x325: return 0x0;
    case 0x326: return 0x0;
    case 0x327: return 0x0;
    case 0x328: return 0x0;
    case 0x329: return 0x0;
    case 0x32a: return 0x0;
    case 0x32b: return 0x0;
    case 0x32c: return 0x0;
    case 0x32d: return 0x0;
    case 0x32e: return 0x0;
    case 0x32f: return 0x0;
    case 0x330: return 0x0;
    case 0x331: return 0x0;
    case 0x332: return 0x0;
    case 0x333: return 0x0;
    case 0x334: return 0x0;
    case 0x335: return 0x0;
    case 0x336: return 0x0;
    case 0x337: return 0x0;
    case 0x338: return 0x0;
    case 0x339: return 0x0;
    case 0x33a: return 0x0;
    case 0x33b: return 0x0;
    case 0x33c: return 0x0;
    case 0x33d: return 0x0;
    case 0x33e: return 0x0;
    case 0x33f: return 0x0;
    case 0x340: return 0x0;
    case 0x341: return 0x0;
    case 0x342: return 0x0;
    case 0x343: return 0x0;
    case 0x344: return 0x0;
    case 0x345: return 0x0;
    case 0x346: return 0x0;
    case 0x347: return 0x0;
    case 0x348: return 0x0;
    case 0x349: return 0x0;
    case 0x34a: return 0x0;
    case 0x34b: return 0x0;
    case 0x34c: return 0x0;
    case 0x34d: return 0x0;
    case 0x34e: return 0x0;
    case 0x350: return 0x0;
    case 0x351: return 0x0;
    case 0x352: return 0x0;
    case 0x353: return 0x0;
    case 0x354: return 0x0;
    case 0x355: return 0x0;
    case 0x356: return 0x0;
    case 0x357: return 0x0;
    case 0x35d: return 0x0;
    case 0x35e: return 0x0;
    case 0x35f: return 0x0;
    case 0x360: return 0x0;
    case 0x361: return 0x0;
    case 0x362: return 0x0;
    case 0x374: return 0x0;
    case 0x375: return 0x0;
    case 0x37a: return 0x0;
    case 0x37e: return 0x3b;
    case 0x384: return 0x0;
    case 0x385: return 0x0;
    case 0x386: return 0x391;
    case 0x388: return 0x395;
    case 0x389: return 0x397;
    case 0x38a: return 0x399;
    case 0x38c: return 0x39f;
    case 0x38e: return 0x3a5;
    case 0x38f: return 0x3a9;
    case 0x390: return 0x3b9;
    case 0x3aa: return 0x399;
    case 0x3ab: return 0x3a5;
    case 0x3ac: return 0x3b1;
    case 0x3ad: return 0x3b5;
    case 0x3ae: return 0x3b7;
    case 0x3af: return 0x3b9;
    case 0x3b0: return 0x3c5;
    case 0x3ca: return 0x3b9;
    case 0x3cb: return 0x3c5;
    case 0x3cc: return 0x3bf;
    case 0x3cd: return 0x3c5;
    case 0x3ce: return 0x3c9;
    case 0x3d3: return 0x3d2;
    case 0x3d4: return 0x3d2;
    case 0x400: return 0x415;
    case 0x401: return 0x415;
    case 0x403: return 0x413;
    case 0x407: return 0x406;
    case 0x40c: return 0x41a;
    case 0x40d: return 0x418;
    case 0x40e: return 0x423;
    case 0x419: return 0x418;
    case 0x439: return 0x438;
    case 0x450: return 0x435;
    case 0x451: return 0x435;
    case 0x453: return 0x433;
    case 0x457: return 0x456;
    case 0x45c: return 0x43a;
    case 0x45d: return 0x438;
    case 0x45e: return 0x443;
    case 0x476: return 0x474;
    case 0x477: return 0x475;
    case 0x483: return 0x0;
    case 0x484: return 0x0;
    case 0x485: return 0x0;
    case 0x486: return 0x0;
    case 0x487: return 0x0;
    case 0x4c1: return 0x416;
    case 0x4c2: return 0x436;
    case 0x4d0: return 0x410;
    case 0x4d1: return 0x430;
    case 0x4d2: return 0x410;
    case 0x4d3: return 0x430;
    case 0x4d6: return 0x415;
    case 0x4d7: return 0x435;
    case 0x4da: return 0x4d8;
    case 0x4db: return 0x4d9;
    case 0x4dc: return 0x416;
    case 0x4dd: return 0x436;
    case 0x4de: return 0x417;
    case 0x4df: return 0x437;
    case 0x4e2: return 0x418;
    case 0x4e3: return 0x438;
    case 0x4e4: return 0x418;
    case 0x4e5: return 0x438;
    case 0x4e6: return 0x41e;
    case 0x4e7: return 0x43e;
    case 0x4ea: return 0x4e8;
    case 0x4eb: return 0x4e9;
    case 0x4ec: return 0x42d;
    case 0x4ed: return 0x44d;
    case 0x4ee: return 0x423;
    case 0x4ef: return 0x443;
    case 0x4f0: return 0x423;
    case 0x4f1: return 0x443;
    case 0x4f2: return 0x423;
    case 0x4f3: return 0x443;
    case 0x4f4: return 0x427;
    case 0x4f5: return 0x447;
    case 0x4f8: return 0x42b;
    case 0x4f9: return 0x44b;
    case 0x559: return 0x0;
    case 0x591: return 0x0;
    case 0x592: return 0x0;
    case 0x593: return 0x0;
    case 0x594: return 0x0;
    case 0x595: return 0x0;
    case 0x596: return 0x0;
    case 0x597: return 0x0;
    case 0x598: return 0x0;
    case 0x599: return 0x0;
    case 0x59a: return 0x0;
    case 0x59b: return 0x0;
    case 0x59c: return 0x0;
    case 0x59d: return 0x0;
    case 0x59e: return 0x0;
    case 0x59f: return 0x0;
    case 0x5a0: return 0x0;
    case 0x5a1: return 0x0;
    case 0x5a3: return 0x0;
    case 0x5a4: return 0x0;
    case 0x5a5: return 0x0;
    case 0x5a6: return 0x0;
    case 0x5a7: return 0x0;
    case 0x5a8: return 0x0;
    case 0x5a9: return 0x0;
    case 0x5aa: return 0x0;
    case 0x5ab: return 0x0;
    case 0x5ac: return 0x0;
    case 0x5ad: return 0x0;
    case 0x5ae: return 0x0;
    case 0x5af: return 0x0;
    case 0x5b0: return 0x0;
    case 0x5b1: return 0x0;
    case 0x5b2: return 0x0;
    case 0x5b3: return 0x0;
    case 0x5b4: return 0x0;
    case 0x5b5: return 0x0;
    case 0x5b6: return 0x0;
    case 0x5b7: return 0x0;
    case 0x5b8: return 0x0;
    case 0x5b9: return 0x0;
    case 0x5ba: return 0x0;
    case 0x5bb: return 0x0;
    case 0x5bc: return 0x0;
    case 0x5bd: return 0x0;
    case 0x5bf: return 0x0;
    case 0x5c1: return 0x0;
    case 0x5c2: return 0x0;
    case 0x5c4: return 0x0;
    case 0x64b: return 0x0;
    case 0x64c: return 0x0;
    case 0x64d: return 0x0;
    case 0x64e: return 0x0;
    case 0x64f: return 0x0;
    case 0x650: return 0x0;
    case 0x651: return 0x0;
    case 0x652: return 0x0;
    case 0x657: return 0x0;
    case 0x658: return 0x0;
    case 0x6df: return 0x0;
    case 0x6e0: return 0x0;
    case 0x6e5: return 0x0;
    case 0x6e6: return 0x0;
    case 0x6ea: return 0x0;
    case 0x6eb: return 0x0;
    case 0x6ec: return 0x0;
    case 0x730: return 0x0;
    case 0x731: return 0x0;
    case 0x732: return 0x0;
    case 0x733: return 0x0;
    case 0x734: return 0x0;
    case 0x735: return 0x0;
    case 0x736: return 0x0;
    case 0x737: return 0x0;
    case 0x738: return 0x0;
    case 0x739: return 0x0;
    case 0x73a: return 0x0;
    case 0x73b: return 0x0;
    case 0x73c: return 0x0;
    case 0x73d: return 0x0;
    case 0x73e: return 0x0;
    case 0x73f: return 0x0;
    case 0x740: return 0x0;
    case 0x741: return 0x0;
    case 0x742: return 0x0;
    case 0x743: return 0x0;
    case 0x744: return 0x0;
    case 0x745: return 0x0;
    case 0x746: return 0x0;
    case 0x747: return 0x0;
    case 0x748: return 0x0;
    case 0x749: return 0x0;
    case 0x74a: return 0x0;
    case 0x7a6: return 0x0;
    case 0x7a7: return 0x0;
    case 0x7a8: return 0x0;
    case 0x7a9: return 0x0;
    case 0x7aa: return 0x0;
    case 0x7ab: return 0x0;
    case 0x7ac: return 0x0;
    case 0x7ad: return 0x0;
    case 0x7ae: return 0x0;
    case 0x7af: return 0x0;
    case 0x7b0: return 0x0;
    case 0x7eb: return 0x0;
    case 0x7ec: return 0x0;
    case 0x7ed: return 0x0;
    case 0x7ee: return 0x0;
    case 0x7ef: return 0x0;
    case 0x7f0: return 0x0;
    case 0x7f1: return 0x0;
    case 0x7f2: return 0x0;
    case 0x7f3: return 0x0;
    case 0x7f4: return 0x0;
    case 0x7f5: return 0x0;
    case 0x818: return 0x0;
    case 0x819: return 0x0;
    case 0x8e3: return 0x0;
    case 0x8e4: return 0x0;
    case 0x8e5: return 0x0;
    case 0x8e6: return 0x0;
    case 0x8e7: return 0x0;
    case 0x8e8: return 0x0;
    case 0x8e9: return 0x0;
    case 0x8ea: return 0x0;
    case 0x8eb: return 0x0;
    case 0x8ec: return 0x0;
    case 0x8ed: return 0x0;
    case 0x8ee: return 0x0;
    case 0x8ef: return 0x0;
    case 0x8f0: return 0x0;
    case 0x8f1: return 0x0;
    case 0x8f2: return 0x0;
    case 0x8f3: return 0x0;
    case 0x8f4: return 0x0;
    case 0x8f5: return 0x0;
    case 0x8f6: return 0x0;
    case 0x8f7: return 0x0;
    case 0x8f8: return 0x0;
    case 0x8f9: return 0x0;
    case 0x8fa: return 0x0;
    case 0x8fb: return 0x0;
    case 0x8fc: return 0x0;
    case 0x8fd: return 0x0;
    case 0x8fe: return 0x0;
    case 0x929: return 0x928;
    case 0x931: return 0x930;
    case 0x934: return 0x933;
    case 0x93c: return 0x0;
    case 0x94d: return 0x0;
    case 0x951: return 0x0;
    case 0x952: return 0x0;
    case 0x953: return 0x0;
    case 0x954: return 0x0;
    case 0x958: return 0x915;
    case 0x959: return 0x916;
    case 0x95a: return 0x917;
    case 0x95b: return 0x91c;
    case 0x95c: return 0x921;
    case 0x95d: return 0x922;
    case 0x95e: return 0x92b;
    case 0x95f: return 0x92f;
    case 0x971: return 0x0;
    case 0x9bc: return 0x0;
    case 0x9cd: return 0x0;
    case 0x9dc: return 0x9a1;
    case 0x9dd: return 0x9a2;
    case 0x9df: return 0x9af;
    case 0xa33: return 0xa32;
    case 0xa36: return 0xa38;
    case 0xa3c: return 0x0;
    case 0xa4d: return 0x0;
    case 0xa59: return 0xa16;
    case 0xa5a: return 0xa17;
    case 0xa5b: return 0xa1c;
    case 0xa5e: return 0xa2b;
    case 0xabc: return 0x0;
    case 0xacd: return 0x0;
    case 0xafd: return 0x0;
    case 0xafe: return 0x0;
    case 0xaff: return 0x0;
    case 0xb3c: return 0x0;
    case 0xb4d: return 0x0;
    case 0xb55: return 0x0;
    case 0xb5c: return 0xb21;
    case 0xb5d: return 0xb22;
    case 0xbcd: return 0x0;
    case 0xc4d: return 0x0;
    case 0xcbc: return 0x0;
    case 0xccd: return 0x0;
    case 0xd3b: return 0x0;
    case 0xd3c: return 0x0;
    case 0xd4d: return 0x0;
    case 0xdca: return 0x0;
    case 0xdda: return 0xdd9;
    case 0xddd: return 0xddc;
    case 0xe47: return 0x0;
    case 0xe48: return 0x0;
    case 0xe49: return 0x0;
    case 0xe4a: return 0x0;
    case 0xe4b: return 0x0;
    case 0xe4c: return 0x0;
    case 0xe4e: return 0x0;
    case 0xeba: return 0x0;
    case 0xec8: return 0x0;
    case 0xec9: return 0x0;
    case 0xeca: return 0x0;
    case 0xecb: return 0x0;
    case 0xecc: return 0x0;
    case 0xf18: return 0x0;
    case 0xf19: return 0x0;
    case 0xf35: return 0x0;
    case 0xf37: return 0x0;
    case 0xf39: return 0x0;
    case 0xf3e: return 0x0;
    case 0xf3f: return 0x0;
    case 0xf82: return 0x0;
    case 0xf83: return 0x0;
    case 0xf84: return 0x0;
    case 0xf86: return 0x0;
    case 0xf87: return 0x0;
    case 0xfc6: return 0x0;
    case 0x1037: return 0x0;
    case 0x1039: return 0x0;
    case 0x103a: return 0x0;
    case 0x1063: return 0x0;
    case 0x1064: return 0x0;
    case 0x1069: return 0x0;
    case 0x106a: return 0x0;
    case 0x106b: return 0x0;
    case 0x106c: return 0x0;
    case 0x106d: return 0x0;
    case 0x1087: return 0x0;
    case 0x1088: return 0x0;
    case 0x1089: return 0x0;
    case 0x108a: return 0x0;
    case 0x108b: return 0x0;
    case 0x108c: return 0x0;
    case 0x108d: return 0x0;
    case 0x108f: return 0x0;
    case 0x109a: return 0x0;
    case 0x109b: return 0x0;
    case 0x135d: return 0x0;
    case 0x135e: return 0x0;
    case 0x135f: return 0x0;
    case 0x17c9: return 0x0;
    case 0x17ca: return 0x0;
    case 0x17cb: return 0x0;
    case 0x17cc: return 0x0;
    case 0x17cd: return 0x0;
    case 0x17ce: return 0x0;
    case 0x17cf: return 0x0;
    case 0x17d0: return 0x0;
    case 0x17d1: return 0x0;
    case 0x17d2: return 0x0;
    case 0x17d3: return 0x0;
    case 0x17dd: return 0x0;
    case 0x1939: return 0x0;
    case 0x193a: return 0x0;
    case 0x193b: return 0x0;
    case 0x1a75: return 0x0;
    case 0x1a76: return 0x0;
    case 0x1a77: return 0x0;
    case 0x1a78: return 0x0;
    case 0x1a79: return 0x0;
    case 0x1a7a: return 0x0;
    case 0x1a7b: return 0x0;
    case 0x1a7c: return 0x0;
    case 0x1a7f: return 0x0;
    case 0x1ab0: return 0x0;
    case 0x1ab1: return 0x0;
    case 0x1ab2: return 0x0;
    case 0x1ab3: return 0x0;
    case 0x1ab4: return 0x0;
    case 0x1ab5: return 0x0;
    case 0x1ab6: return 0x0;
    case 0x1ab7: return 0x0;
    case 0x1ab8: return 0x0;
    case 0x1ab9: return 0x0;
    case 0x1aba: return 0x0;
    case 0x1abb: return 0x0;
    case 0x1abc: return 0x0;
    case 0x1abd: return 0x0;
    case 0x1b34: return 0x0;
    case 0x1b44: return 0x0;
    case 0x1b6b: return 0x0;
    case 0x1b6c: return 0x0;
    case 0x1b6d: return 0x0;
    case 0x1b6e: return 0x0;
    case 0x1b6f: return 0x0;
    case 0x1b70: return 0x0;
    case 0x1b71: return 0x0;
    case 0x1b72: return 0x0;
    case 0x1b73: return 0x0;
    case 0x1baa: return 0x0;
    case 0x1bab: return 0x0;
    case 0x1c36: return 0x0;
    case 0x1c37: return 0x0;
    case 0x1c78: return 0x0;
    case 0x1c79: return 0x0;
    case 0x1c7a: return 0x0;
    case 0x1c7b: return 0x0;
    case 0x1c7c: return 0x0;
    case 0x1c7d: return 0x0;
    case 0x1cd0: return 0x0;
    case 0x1cd1: return 0x0;
    case 0x1cd2: return 0x0;
    case 0x1cd3: return 0x0;
    case 0x1cd4: return 0x0;
    case 0x1cd5: return 0x0;
    case 0x1cd6: return 0x0;
    case 0x1cd7: return 0x0;
    case 0x1cd8: return 0x0;
    case 0x1cd9: return 0x0;
    case 0x1cda: return 0x0;
    case 0x1cdb: return 0x0;
    case 0x1cdc: return 0x0;
    case 0x1cdd: return 0x0;
    case 0x1cde: return 0x0;
    case 0x1cdf: return 0x0;
    case 0x1ce0: return 0x0;
    case 0x1ce1: return 0x0;
    case 0x1ce2: return 0x0;
    case 0x1ce3: return 0x0;
    case 0x1ce4: return 0x0;
    case 0x1ce5: return 0x0;
    case 0x1ce6: return 0x0;
    case 0x1ce7: return 0x0;
    case 0x1ce8: return 0x0;
    case 0x1ced: return 0x0;
    case 0x1cf4: return 0x0;
    case 0x1cf7: return 0x0;
    case 0x1cf8: return 0x0;
    case 0x1cf9: return 0x0;
    case 0x1d2c: return 0x0;
    case 0x1d2d: return 0x0;
    case 0x1d2e: return 0x0;
    case 0x1d2f: return 0x0;
    case 0x1d30: return 0x0;
    case 0x1d31: return 0x0;
    case 0x1d32: return 0x0;
    case 0x1d33: return 0x0;
    case 0x1d34: return 0x0;
    case 0x1d35: return 0x0;
    case 0x1d36: return 0x0;
    case 0x1d37: return 0x0;
    case 0x1d38: return 0x0;
    case 0x1d39: return 0x0;
    case 0x1d3a: return 0x0;
    case 0x1d3b: return 0x0;
    case 0x1d3c: return 0x0;
    case 0x1d3d: return 0x0;
    case 0x1d3e: return 0x0;
    case 0x1d3f: return 0x0;
    case 0x1d40: return 0x0;
    case 0x1d41: return 0x0;
    case 0x1d42: return 0x0;
    case 0x1d43: return 0x0;
    case 0x1d44: return 0x0;
    case 0x1d45: return 0x0;
    case 0x1d46: return 0x0;
    case 0x1d47: return 0x0;
    case 0x1d48: return 0x0;
    case 0x1d49: return 0x0;
    case 0x1d4a: return 0x0;
    case 0x1d4b: return 0x0;
    case 0x1d4c: return 0x0;
    case 0x1d4d: return 0x0;
    case 0x1d4e: return 0x0;
    case 0x1d4f: return 0x0;
    case 0x1d50: return 0x0;
    case 0x1d51: return 0x0;
    case 0x1d52: return 0x0;
    case 0x1d53: return 0x0;
    case 0x1d54: return 0x0;
    case 0x1d55: return 0x0;
    case 0x1d56: return 0x0;
    case 0x1d57: return 0x0;
    case 0x1d58: return 0x0;
    case 0x1d59: return 0x0;
    case 0x1d5a: return 0x0;
    case 0x1d5b: return 0x0;
    case 0x1d5c: return 0x0;
    case 0x1d5d: return 0x0;
    case 0x1d5e: return 0x0;
    case 0x1d5f: return 0x0;
    case 0x1d60: return 0x0;
    case 0x1d61: return 0x0;
    case 0x1d62: return 0x0;
    case 0x1d63: return 0x0;
    case 0x1d64: return 0x0;
    case 0x1d65: return 0x0;
    case 0x1d66: return 0x0;
    case 0x1d67: return 0x0;
    case 0x1d68: return 0x0;
    case 0x1d69: return 0x0;
    case 0x1d6a: return 0x0;
    case 0x1dc4: return 0x0;
    case 0x1dc5: return 0x0;
    case 0x1dc6: return 0x0;
    case 0x1dc7: return 0x0;
    case 0x1dc8: return 0x0;
    case 0x1dc9: return 0x0;
    case 0x1dca: return 0x0;
    case 0x1dcb: return 0x0;
    case 0x1dcc: return 0x0;
    case 0x1dcd: return 0x0;
    case 0x1dce: return 0x0;
    case 0x1dcf: return 0x0;
    case 0x1df5: return 0x0;
    case 0x1df6: return 0x0;
    case 0x1df7: return 0x0;
    case 0x1df8: return 0x0;
    case 0x1df9: return 0x0;
    case 0x1dfd: return 0x0;
    case 0x1dfe: return 0x0;
    case 0x1dff: return 0x0;
    case 0x1e00: return 0x41;
    case 0x1e01: return 0x61;
    case 0x1e02: return 0x42;
    case 0x1e03: return 0x62;
    case 0x1e04: return 0x42;
    case 0x1e05: return 0x62;
    case 0x1e06: return 0x42;
    case 0x1e07: return 0x62;
    case 0x1e08: return 0x43;
    case 0x1e09: return 0x63;
    case 0x1e0a: return 0x44;
    case 0x1e0b: return 0x64;
    case 0x1e0c: return 0x44;
    case 0x1e0d: return 0x64;
    case 0x1e0e: return 0x44;
    case 0x1e0f: return 0x64;
    case 0x1e10: return 0x44;
    case 0x1e11: return 0x64;
    case 0x1e12: return 0x44;
    case 0x1e13: return 0x64;
    case 0x1e14: return 0x45;
    case 0x1e15: return 0x65;
    case 0x1e16: return 0x45;
    case 0x1e17: return 0x65;
    case 0x1e18: return 0x45;
    case 0x1e19: return 0x65;
    case 0x1e1a: return 0x45;
    case 0x1e1b: return 0x65;
    case 0x1e1c: return 0x45;
    case 0x1e1d: return 0x65;
    case 0x1e1e: return 0x46;
    case 0x1e1f: return 0x66;
    case 0x1e20: return 0x47;
    case 0x1e21: return 0x67;
    case 0x1e22: return 0x48;
    case 0x1e23: return 0x68;
    case 0x1e24: return 0x48;
    case 0x1e25: return 0x68;
    case 0x1e26: return 0x48;
    case 0x1e27: return 0x68;
    case 0x1e28: return 0x48;
    case 0x1e29: return 0x68;
    case 0x1e2a: return 0x48;
    case 0x1e2b: return 0x68;
    case 0x1e2c: return 0x49;
    case 0x1e2d: return 0x69;
    case 0x1e2e: return 0x49;
    case 0x1e2f: return 0x69;
    case 0x1e30: return 0x4b;
    case 0x1e31: return 0x6b;
    case 0x1e32: return 0x4b;
    case 0x1e33: return 0x6b;
    case 0x1e34: return 0x4b;
    case 0x1e35: return 0x6b;
    case 0x1e36: return 0x4c;
    case 0x1e37: return 0x6c;
    case 0x1e38: return 0x4c;
    case 0x1e39: return 0x6c;
    case 0x1e3a: return 0x4c;
    case 0x1e3b: return 0x6c;
    case 0x1e3c: return 0x4c;
    case 0x1e3d: return 0x6c;
    case 0x1e3e: return 0x4d;
    case 0x1e3f: return 0x6d;
    case 0x1e40: return 0x4d;
    case 0x1e41: return 0x6d;
    case 0x1e42: return 0x4d;
    case 0x1e43: return 0x6d;
    case 0x1e44: return 0x4e;
    case 0x1e45: return 0x6e;
    case 0x1e46: return 0x4e;
    case 0x1e47: return 0x6e;
    case 0x1e48: return 0x4e;
    case 0x1e49: return 0x6e;
    case 0x1e4a: return 0x4e;
    case 0x1e4b: return 0x6e;
    case 0x1e4c: return 0x4f;
    case 0x1e4d: return 0x6f;
    case 0x1e4e: return 0x4f;
    case 0x1e4f: return 0x6f;
    case 0x1e50: return 0x4f;
    case 0x1e51: return 0x6f;
    case 0x1e52: return 0x4f;
    case 0x1e53: return 0x6f;
    case 0x1e54: return 0x50;
    case 0x1e55: return 0x70;
    case 0x1e56: return 0x50;
    case 0x1e57: return 0x70;
    case 0x1e58: return 0x52;
    case 0x1e59: return 0x72;
    case 0x1e5a: return 0x52;
    case 0x1e5b: return 0x72;
    case 0x1e5c: return 0x52;
    case 0x1e5d: return 0x72;
    case 0x1e5e: return 0x52;
    case 0x1e5f: return 0x72;
    case 0x1e60: return 0x53;
    case 0x1e61: return 0x73;
    case 0x1e62: return 0x53;
    case 0x1e63: return 0x73;
    case 0x1e64: return 0x53;
    case 0x1e65: return 0x73;
    case 0x1e66: return 0x53;
    case 0x1e67: return 0x73;
    case 0x1e68: return 0x53;
    case 0x1e69: return 0x73;
    case 0x1e6a: return 0x54;
    case 0x1e6b: return 0x74;
    case 0x1e6c: return 0x54;
    case 0x1e6d: return 0x74;
    case 0x1e6e: return 0x54;
    case 0x1e6f: return 0x74;
    case 0x1e70: return 0x54;
    case 0x1e71: return 0x74;
    case 0x1e72: return 0x55;
    case 0x1e73: return 0x75;
    case 0x1e74: return 0x55;
    case 0x1e75: return 0x75;
    case 0x1e76: return 0x55;
    case 0x1e77: return 0x75;
    case 0x1e78: return 0x55;
    case 0x1e79: return 0x75;
    case 0x1e7a: return 0x55;
    case 0x1e7b: return 0x75;
    case 0x1e7c: return 0x56;
    case 0x1e7d: return 0x76;
    case 0x1e7e: return 0x56;
    case 0x1e7f: return 0x76;
    case 0x1e80: return 0x57;
    case 0x1e81: return 0x77;
    case 0x1e82: return 0x57;
    case 0x1e83: return 0x77;
    case 0x1e84: return 0x57;
    case 0x1e85: return 0x77;
    case 0x1e86: return 0x57;
    case 0x1e87: return 0x77;
    case 0x1e88: return 0x57;
    case 0x1e89: return 0x77;
    case 0x1e8a: return 0x58;
    case 0x1e8b: return 0x78;
    case 0x1e8c: return 0x58;
    case 0x1e8d: return 0x78;
    case 0x1e8e: return 0x59;
    case 0x1e8f: return 0x79;
    case 0x1e90: return 0x5a;
    case 0x1e91: return 0x7a;
    case 0x1e92: return 0x5a;
    case 0x1e93: return 0x7a;
    case 0x1e94: return 0x5a;
    case 0x1e95: return 0x7a;
    case 0x1e96: return 0x68;
    case 0x1e97: return 0x74;
    case 0x1e98: return 0x77;
    case 0x1e99: return 0x79;
    case 0x1e9b: return 0x17f;
    case 0x1ea0: return 0x41;
    case 0x1ea1: return 0x61;
    case 0x1ea2: return 0x41;
    case 0x1ea3: return 0x61;
    case 0x1ea4: return 0x41;
    case 0x1ea5: return 0x61;
    case 0x1ea6: return 0x41;
    case 0x1ea7: return 0x61;
    case 0x1ea8: return 0x41;
    case 0x1ea9: return 0x61;
    case 0x1eaa: return 0x41;
    case 0x1eab: return 0x61;
    case 0x1eac: return 0x41;
    case 0x1ead: return 0x61;
    case 0x1eae: return 0x41;
    case 0x1eaf: return 0x61;
    case 0x1eb0: return 0x41;
    case 0x1eb1: return 0x61;
    case 0x1eb2: return 0x41;
    case 0x1eb3: return 0x61;
    case 0x1eb4: return 0x41;
    case 0x1eb5: return 0x61;
    case 0x1eb6: return 0x41;
    case 0x1eb7: return 0x61;
    case 0x1eb8: return 0x45;
    case 0x1eb9: return 0x65;
    case 0x1eba: return 0x45;
    case 0x1ebb: return 0x65;
    case 0x1ebc: return 0x45;
    case 0x1ebd: return 0x65;
    case 0x1ebe: return 0x45;
    case 0x1ebf: return 0x65;
    case 0x1ec0: return 0x45;
    case 0x1ec1: return 0x65;
    case 0x1ec2: return 0x45;
    case 0x1ec3: return 0x65;
    case 0x1ec4: return 0x45;
    case 0x1ec5: return 0x65;
    case 0x1ec6: return 0x45;
    case 0x1ec7: return 0x65;
    case 0x1ec8: return 0x49;
    case 0x1ec9: return 0x69;
    case 0x1eca: return 0x49;
    case 0x1ecb: return 0x69;
    case 0x1ecc: return 0x4f;
    case 0x1ecd: return 0x6f;
    case 0x1ece: return 0x4f;
    case 0x1ecf: return 0x6f;
    case 0x1ed0: return 0x4f;
    case 0x1ed1: return 0x6f;
    case 0x1ed2: return 0x4f;
    case 0x1ed3: return 0x6f;
    case 0x1ed4: return 0x4f;
    case 0x1ed5: return 0x6f;
    case 0x1ed6: return 0x4f;
    case 0x1ed7: return 0x6f;
    case 0x1ed8: return 0x4f;
    case 0x1ed9: return 0x6f;
    case 0x1eda: return 0x4f;
    case 0x1edb: return 0x6f;
    case 0x1edc: return 0x4f;
    case 0x1edd: return 0x6f;
    case 0x1ede: return 0x4f;
    case 0x1edf: return 0x6f;
    case 0x1ee0: return 0x4f;
    case 0x1ee1: return 0x6f;
    case 0x1ee2: return 0x4f;
    case 0x1ee3: return 0x6f;
    case 0x1ee4: return 0x55;
    case 0x1ee5: return 0x75;
    case 0x1ee6: return 0x55;
    case 0x1ee7: return 0x75;
    case 0x1ee8: return 0x55;
    case 0x1ee9: return 0x75;
    case 0x1eea: return 0x55;
    case 0x1eeb: return 0x75;
    case 0x1eec: return 0x55;
    case 0x1eed: return 0x75;
    case 0x1eee: return 0x55;
    case 0x1eef: return 0x75;
    case 0x1ef0: return 0x55;
    case 0x1ef1: return 0x75;
    case 0x1ef2: return 0x59;
    case 0x1ef3: return 0x79;
    case 0x1ef4: return 0x59;
    case 0x1ef5: return 0x79;
    case 0x1ef6: return 0x59;
    case 0x1ef7: return 0x79;
    case 0x1ef8: return 0x59;
    case 0x1ef9: return 0x79;
    case 0x1f00: return 0x3b1;
    case 0x1f01: return 0x3b1;
    case 0x1f02: return 0x3b1;
    case 0x1f03: return 0x3b1;
    case 0x1f04: return 0x3b1;
    case 0x1f05: return 0x3b1;
    case 0x1f06: return 0x3b1;
    case 0x1f07: return 0x3b1;
    case 0x1f08: return 0x391;
    case 0x1f09: return 0x391;
    case 0x1f0a: return 0x391;
    case 0x1f0b: return 0x391;
    case 0x1f0c: return 0x391;
    case 0x1f0d: return 0x391;
    case 0x1f0e: return 0x391;
    case 0x1f0f: return 0x391;
    case 0x1f10: return 0x3b5;
    case 0x1f11: return 0x3b5;
    case 0x1f12: return 0x3b5;
    case 0x1f13: return 0x3b5;
    case 0x1f14: return 0x3b5;
    case 0x1f15: return 0x3b5;
    case 0x1f18: return 0x395;
    case 0x1f19: return 0x395;
    case 0x1f1a: return 0x395;
    case 0x1f1b: return 0x395;
    case 0x1f1c: return 0x395;
    case 0x1f1d: return 0x395;
    case 0x1f20: return 0x3b7;
    case 0x1f21: return 0x3b7;
    case 0x1f22: return 0x3b7;
    case 0x1f23: return 0x3b7;
    case 0x1f24: return 0x3b7;
    case 0x1f25: return 0x3b7;
    case 0x1f26: return 0x3b7;
    case 0x1f27: return 0x3b7;
    case 0x1f28: return 0x397;
    case 0x1f29: return 0x397;
    case 0x1f2a: return 0x397;
    case 0x1f2b: return 0x397;
    case 0x1f2c: return 0x397;
    case 0x1f2d: return 0x397;
    case 0x1f2e: return 0x397;
    case 0x1f2f: return 0x397;
    case 0x1f30: return 0x3b9;
    case 0x1f31: return 0x3b9;
    case 0x1f32: return 0x3b9;
    case 0x1f33: return 0x3b9;
    case 0x1f34: return 0x3b9;
    case 0x1f35: return 0x3b9;
    case 0x1f36: return 0x3b9;
    case 0x1f37: return 0x3b9;
    case 0x1f38: return 0x399;
    case 0x1f39: return 0x399;
    case 0x1f3a: return 0x399;
    case 0x1f3b: return 0x399;
    case 0x1f3c: return 0x399;
    case 0x1f3d: return 0x399;
    case 0x1f3e: return 0x399;
    case 0x1f3f: return 0x399;
    case 0x1f40: return 0x3bf;
    case 0x1f41: return 0x3bf;
    case 0x1f42: return 0x3bf;
    case 0x1f43: return 0x3bf;
    case 0x1f44: return 0x3bf;
    case 0x1f45: return 0x3bf;
    case 0x1f48: return 0x39f;
    case 0x1f49: return 0x39f;
    case 0x1f4a: return 0x39f;
    case 0x1f4b: return 0x39f;
    case 0x1f4c: return 0x39f;
    case 0x1f4d: return 0x39f;
    case 0x1f50: return 0x3c5;
    case 0x1f51: return 0x3c5;
    case 0x1f52: return 0x3c5;
    case 0x1f53: return 0x3c5;
    case 0x1f54: return 0x3c5;
    case 0x1f55: return 0x3c5;
    case 0x1f56: return 0x3c5;
    case 0x1f57: return 0x3c5;
    case 0x1f59: return 0x3a5;
    case 0x1f5b: return 0x3a5;
    case 0x1f5d: return 0x3a5;
    case 0x1f5f: return 0x3a5;
    case 0x1f60: return 0x3c9;
    case 0x1f61: return 0x3c9;
    case 0x1f62: return 0x3c9;
    case 0x1f63: return 0x3c9;
    case 0x1f64: return 0x3c9;
    case 0x1f65: return 0x3c9;
    case 0x1f66: return 0x3c9;
    case 0x1f67: return 0x3c9;
    case 0x1f68: return 0x3a9;
    case 0x1f69: return 0x3a9;
    case 0x1f6a: return 0x3a9;
    case 0x1f6b: return 0x3a9;
    case 0x1f6c: return 0x3a9;
    case 0x1f6d: return 0x3a9;
    case 0x1f6e: return 0x3a9;
    case 0x1f6f: return 0x3a9;
    case 0x1f70: return 0x3b1;
    case 0x1f71: return 0x3b1;
    case 0x1f72: return 0x3b5;
    case 0x1f73: return 0x3b5;
    case 0x1f74: return 0x3b7;
    case 0x1f75: return 0x3b7;
    case 0x1f76: return 0x3b9;
    case 0x1f77: return 0x3b9;
    case 0x1f78: return 0x3bf;
    case 0x1f79: return 0x3bf;
    case 0x1f7a: return 0x3c5;
    case 0x1f7b: return 0x3c5;
    case 0x1f7c: return 0x3c9;
    case 0x1f7d: return 0x3c9;
    case 0x1f80: return 0x3b1;
    case 0x1f81: return 0x3b1;
    case 0x1f82: return 0x3b1;
    case 0x1f83: return 0x3b1;
    case 0x1f84: return 0x3b1;
    case 0x1f85: return 0x3b1;
    case 0x1f86: return 0x3b1;
    case 0x1f87: return 0x3b1;
    case 0x1f88: return 0x391;
    case 0x1f89: return 0x391;
    case 0x1f8a: return 0x391;
    case 0x1f8b: return 0x391;
    case 0x1f8c: return 0x391;
    case 0x1f8d: return 0x391;
    case 0x1f8e: return 0x391;
    case 0x1f8f: return 0x391;
    case 0x1f90: return 0x3b7;
    case 0x1f91: return 0x3b7;
    case 0x1f92: return 0x3b7;
    case 0x1f93: return 0x3b7;
    case 0x1f94: return 0x3b7;
    case 0x1f95: return 0x3b7;
    case 0x1f96: return 0x3b7;
    case 0x1f97: return 0x3b7;
    case 0x1f98: return 0x397;
    case 0x1f99: return 0x397;
    case 0x1f9a: return 0x397;
    case 0x1f9b: return 0x397;
    case 0x1f9c: return 0x397;
    case 0x1f9d: return 0x397;
    case 0x1f9e: return 0x397;
    case 0x1f9f: return 0x397;
    case 0x1fa0: return 0x3c9;
    case 0x1fa1: return 0x3c9;
    case 0x1fa2: return 0x3c9;
    case 0x1fa3: return 0x3c9;
    case 0x1fa4: return 0x3c9;
    case 0x1fa5: return 0x3c9;
    case 0x1fa6: return 0x3c9;
    case 0x1fa7: return 0x3c9;
    case 0x1fa8: return 0x3a9;
    case 0x1fa9: return 0x3a9;
    case 0x1faa: return 0x3a9;
    case 0x1fab: return 0x3a9;
    case 0x1fac: return 0x3a9;
    case 0x1fad: return 0x3a9;
    case 0x1fae: return 0x3a9;
    case 0x1faf: return 0x3a9;
    case 0x1fb0: return 0x3b1;
    case 0x1fb1: return 0x3b1;
    case 0x1fb2: return 0x3b1;
    case 0x1fb3: return 0x3b1;
    case 0x1fb4: return 0x3b1;
    case 0x1fb6: return 0x3b1;
    case 0x1fb7: return 0x3b1;
    case 0x1fb8: return 0x391;
    case 0x1fb9: return 0x391;
    case 0x1fba: return 0x391;
    case 0x1fbb: return 0x391;
    case 0x1fbc: return 0x391;
    case 0x1fbd: return 0x0;
    case 0x1fbe: return 0x3b9;
    case 0x1fbf: return 0x0;
    case 0x1fc0: return 0x0;
    case 0x1fc1: return 0x0;
    case 0x1fc2: return 0x3b7;
    case 0x1fc3: return 0x3b7;
    case 0x1fc4: return 0x3b7;
    case 0x1fc6: return 0x3b7;
    case 0x1fc7: return 0x3b7;
    case 0x1fc8: return 0x395;
    case 0x1fc9: return 0x395;
    case 0x1fca: return 0x397;
    case 0x1fcb: return 0x397;
    case 0x1fcc: return 0x397;
    case 0x1fcd: return 0x0;
    case 0x1fce: return 0x0;
    case 0x1fcf: return 0x0;
    case 0x1fd0: return 0x3b9;
    case 0x1fd1: return 0x3b9;
    case 0x1fd2: return 0x3b9;
    case 0x1fd3: return 0x3b9;
    case 0x1fd6: return 0x3b9;
    case 0x1fd7: return 0x3b9;
    case 0x1fd8: return 0x399;
    case 0x1fd9: return 0x399;
    case 0x1fda: return 0x399;
    case 0x1fdb: return 0x399;
    case 0x1fdd: return 0x0;
    case 0x1fde: return 0x0;
    case 0x1fdf: return 0x0;
    case 0x1fe0: return 0x3c5;
    case 0x1fe1: return 0x3c5;
    case 0x1fe2: return 0x3c5;
    case 0x1fe3: return 0x3c5;
    case 0x1fe4: return 0x3c1;
    case 0x1fe5: return 0x3c1;
    case 0x1fe6: return 0x3c5;
    case 0x1fe7: return 0x3c5;
    case 0x1fe8: return 0x3a5;
    case 0x1fe9: return 0x3a5;
    case 0x1fea: return 0x3a5;
    case 0x1feb: return 0x3a5;
    case 0x1fec: return 0x3a1;
    case 0x1fed: return 0x0;
    case 0x1fee: return 0x0;
    case 0x1fef: return 0x0;
    case 0x1ff2: return 0x3c9;
    case 0x1ff3: return 0x3c9;
    case 0x1ff4: return 0x3c9;
    case 0x1ff6: return 0x3c9;
    case 0x1ff7: return 0x3c9;
    case 0x1ff8: return 0x39f;
    case 0x1ff9: return 0x39f;
    case 0x1ffa: return 0x3a9;
    case 0x1ffb: return 0x3a9;
    case 0x1ffc: return 0x3a9;
    case 0x1ffd: return 0x0;
    case 0x1ffe: return 0x0;
    case 0x2000: return 0x2002;
    case 0x2001: return 0x2003;
    case 0x2126: return 0x3a9;
    case 0x212a: return 0x4b;
    case 0x212b: return 0x41;
    case 0x219a: return 0x2190;
    case 0x219b: return 0x2192;
    case 0x21ae: return 0x2194;
    case 0x21cd: return 0x21d0;
    case 0x21ce: return 0x21d4;
    case 0x21cf: return 0x21d2;
    case 0x2204: return 0x2203;
    case 0x2209: return 0x2208;
    case 0x220c: return 0x220b;
    case 0x2224: return 0x2223;
    case 0x2226: return 0x2225;
    case 0x2241: return 0x223c;
    case 0x2244: return 0x2243;
    case 0x2247: return 0x2245;
    case 0x2249: return 0x2248;
    case 0x2260: return 0x3d;
    case 0x2262: return 0x2261;
    case 0x226d: return 0x224d;
    case 0x226e: return 0x3c;
    case 0x226f: return 0x3e;
    case 0x2270: return 0x2264;
    case 0x2271: return 0x2265;
    case 0x2274: return 0x2272;
    case 0x2275: return 0x2273;
    case 0x2278: return 0x2276;
    case 0x2279: return 0x2277;
    case 0x2280: return 0x227a;
    case 0x2281: return 0x227b;
    case 0x2284: return 0x2282;
    case 0x2285: return 0x2283;
    case 0x2288: return 0x2286;
    case 0x2289: return 0x2287;
    case 0x22ac: return 0x22a2;
    case 0x22ad: return 0x22a8;
    case 0x22ae: return 0x22a9;
    case 0x22af: return 0x22ab;
    case 0x22e0: return 0x227c;
    case 0x22e1: return 0x227d;
    case 0x22e2: return 0x2291;
    case 0x22e3: return 0x2292;
    case 0x22ea: return 0x22b2;
    case 0x22eb: return 0x22b3;
    case 0x22ec: return 0x22b4;
    case 0x22ed: return 0x22b5;
    case 0x2329: return 0x3008;
    case 0x232a: return 0x3009;
    case 0x2adc: return 0x2add;
    case 0x2cef: return 0x0;
    case 0x2cf0: return 0x0;
    case 0x2cf1: return 0x0;
    case 0x2e2f: return 0x0;
    case 0x302a: return 0x0;
    case 0x302b: return 0x0;
    case 0x302c: return 0x0;
    case 0x302d: return 0x0;
    case 0x302e: return 0x0;
    case 0x302f: return 0x0;
    case 0x304c: return 0x304b;
    case 0x304e: return 0x304d;
    case 0x3050: return 0x304f;
    case 0x3052: return 0x3051;
    case 0x3054: return 0x3053;
    case 0x3056: return 0x3055;
    case 0x3058: return 0x3057;
    case 0x305a: return 0x3059;
    case 0x305c: return 0x305b;
    case 0x305e: return 0x305d;
    case 0x3060: return 0x305f;
    case 0x3062: return 0x3061;
    case 0x3065: return 0x3064;
    case 0x3067: return 0x3066;
    case 0x3069: return 0x3068;
    case 0x3070: return 0x306f;
    case 0x3071: return 0x306f;
    case 0x3073: return 0x3072;
    case 0x3074: return 0x3072;
    case 0x3076: return 0x3075;
    case 0x3077: return 0x3075;
    case 0x3079: return 0x3078;
    case 0x307a: return 0x3078;
    case 0x307c: return 0x307b;
    case 0x307d: return 0x307b;
    case 0x3094: return 0x3046;
    case 0x3099: return 0x0;
    case 0x309a: return 0x0;
    case 0x309b: return 0x0;
    case 0x309c: return 0x0;
    case 0x309e: return 0x309d;
    case 0x30ac: return 0x30ab;
    case 0x30ae: return 0x30ad;
    case 0x30b0: return 0x30af;
    case 0x30b2: return 0x30b1;
    case 0x30b4: return 0x30b3;
    case 0x30b6: return 0x30b5;
    case 0x30b8: return 0x30b7;
    case 0x30ba: return 0x30b9;
    case 0x30bc: return 0x30bb;
    case 0x30be: return 0x30bd;
    case 0x30c0: return 0x30bf;
    case 0x30c2: return 0x30c1;
    case 0x30c5: return 0x30c4;
    case 0x30c7: return 0x30c6;
    case 0x30c9: return 0x30c8;
    case 0x30d0: return 0x30cf;
    case 0x30d1: return 0x30cf;
    case 0x30d3: return 0x30d2;
    case 0x30d4: return 0x30d2;
    case 0x30d6: return 0x30d5;
    case 0x30d7: return 0x30d5;
    case 0x30d9: return 0x30d8;
    case 0x30da: return 0x30d8;
    case 0x30dc: return 0x30db;
    case 0x30dd: return 0x30db;
    case 0x30f4: return 0x30a6;
    case 0x30f7: return 0x30ef;
    case 0x30f8: return 0x30f0;
    case 0x30f9: return 0x30f1;
    case 0x30fa: return 0x30f2;
    case 0x30fc: return 0x0;
    case 0x30fe: return 0x30fd;
    case 0xa66f: return 0x0;
    case 0xa67c: return 0x0;
    case 0xa67d: return 0x0;
    case 0xa67f: return 0x0;
    case 0xa69c: return 0x0;
    case 0xa69d: return 0x0;
    case 0xa6f0: return 0x0;
    case 0xa6f1: return 0x0;
    case 0xa700: return 0x0;
    case 0xa701: return 0x0;
    case 0xa702: return 0x0;
    case 0xa703: return 0x0;
    case 0xa704: return 0x0;
    case 0xa705: return 0x0;
    case 0xa706: return 0x0;
    case 0xa707: return 0x0;
    case 0xa708: return 0x0;
    case 0xa709: return 0x0;
    case 0xa70a: return 0x0;
    case 0xa70b: return 0x0;
    case 0xa70c: return 0x0;
    case 0xa70d: return 0x0;
    case 0xa70e: return 0x0;
    case 0xa70f: return 0x0;
    case 0xa710: return 0x0;
    case 0xa711: return 0x0;
    case 0xa712: return 0x0;
    case 0xa713: return 0x0;
    case 0xa714: return 0x0;
    case 0xa715: return 0x0;
    case 0xa716: return 0x0;
    case 0xa717: return 0x0;
    case 0xa718: return 0x0;
    case 0xa719: return 0x0;
    case 0xa71a: return 0x0;
    case 0xa71b: return 0x0;
    case 0xa71c: return 0x0;
    case 0xa71d: return 0x0;
    case 0xa71e: return 0x0;
    case 0xa71f: return 0x0;
    case 0xa720: return 0x0;
    case 0xa721: return 0x0;
    case 0xa788: return 0x0;
    case 0xa789: return 0x0;
    case 0xa78a: return 0x0;
    case 0xa7f8: return 0x0;
    case 0xa7f9: return 0x0;
    case 0xa8c4: return 0x0;
    case 0xa8e0: return 0x0;
    case 0xa8e1: return 0x0;
    case 0xa8e2: return 0x0;
    case 0xa8e3: return 0x0;
    case 0xa8e4: return 0x0;
    case 0xa8e5: return 0x0;
    case 0xa8e6: return 0x0;
    case 0xa8e7: return 0x0;
    case 0xa8e8: return 0x0;
    case 0xa8e9: return 0x0;
    case 0xa8ea: return 0x0;
    case 0xa8eb: return 0x0;
    case 0xa8ec: return 0x0;
    case 0xa8ed: return 0x0;
    case 0xa8ee: return 0x0;
    case 0xa8ef: return 0x0;
    case 0xa8f0: return 0x0;
    case 0xa8f1: return 0x0;
    case 0xa92b: return 0x0;
    case 0xa92c: return 0x0;
    case 0xa92d: return 0x0;
    case 0xa92e: return 0x0;
    case 0xa953: return 0x0;
    case 0xa9b3: return 0x0;
    case 0xa9c0: return 0x0;
    case 0xa9e5: return 0x0;
    case 0xaa7b: return 0x0;
    case 0xaa7c: return 0x0;
    case 0xaa7d: return 0x0;
    case 0xaabf: return 0x0;
    case 0xaac0: return 0x0;
    case 0xaac1: return 0x0;
    case 0xaac2: return 0x0;
    case 0xaaf6: return 0x0;
    case 0xab5b: return 0x0;
    case 0xab5c: return 0x0;
    case 0xab5d: return 0x0;
    case 0xab5e: return 0x0;
    case 0xab5f: return 0x0;
    case 0xab69: return 0x0;
    case 0xab6a: return 0x0;
    case 0xab6b: return 0x0;
    case 0xabec: return 0x0;
    case 0xabed: return 0x0;
    case 0xf900: return 0x8c48;
    case 0xf901: return 0x66f4;
    case 0xf902: return 0x8eca;
    case 0xf903: return 0x8cc8;
    case 0xf904: return 0x6ed1;
    case 0xf905: return 0x4e32;
    case 0xf906: return 0x53e5;
    case 0xf907: return 0x9f9c;
    case 0xf908: return 0x9f9c;
    case 0xf909: return 0x5951;
    case 0xf90a: return 0x91d1;
    case 0xf90b: return 0x5587;
    case 0xf90c: return 0x5948;
    case 0xf90d: return 0x61f6;
    case 0xf90e: return 0x7669;
    case 0xf90f: return 0x7f85;
    case 0xf910: return 0x863f;
    case 0xf911: return 0x87ba;
    case 0xf912: return 0x88f8;
    case 0xf913: return 0x908f;
    case 0xf914: return 0x6a02;
    case 0xf915: return 0x6d1b;
    case 0xf916: return 0x70d9;
    case 0xf917: return 0x73de;
    case 0xf918: return 0x843d;
    case 0xf919: return 0x916a;
    case 0xf91a: return 0x99f1;
    case 0xf91b: return 0x4e82;
    case 0xf91c: return 0x5375;
    case 0xf91d: return 0x6b04;
    case 0xf91e: return 0x721b;
    case 0xf91f: return 0x862d;
    case 0xf920: return 0x9e1e;
    case 0xf921: return 0x5d50;
    case 0xf922: return 0x6feb;
    case 0xf923: return 0x85cd;
    case 0xf924: return 0x8964;
    case 0xf925: return 0x62c9;
    case 0xf926: return 0x81d8;
    case 0xf927: return 0x881f;
    case 0xf928: return 0x5eca;
    case 0xf929: return 0x6717;
    case 0xf92a: return 0x6d6a;
    case 0xf92b: return 0x72fc;
    case 0xf92c: return 0x90ce;
    case 0xf92d: return 0x4f86;
    case 0xf92e: return 0x51b7;
    case 0xf92f: return 0x52de;
    case 0xf930: return 0x64c4;
    case 0xf931: return 0x6ad3;
    case 0xf932: return 0x7210;
    case 0xf933: return 0x76e7;
    case 0xf934: return 0x8001;
    case 0xf935: return 0x8606;
    case 0xf936: return 0x865c;
    case 0xf937: return 0x8def;
    case 0xf938: return 0x9732;
    case 0xf939: return 0x9b6f;
    case 0xf93a: return 0x9dfa;
    case 0xf93b: return 0x788c;
    case 0xf93c: return 0x797f;
    case 0xf93d: return 0x7da0;
    case 0xf93e: return 0x83c9;
    case 0xf93f: return 0x9304;
    case 0xf940: return 0x9e7f;
    case 0xf941: return 0x8ad6;
    case 0xf942: return 0x58df;
    case 0xf943: return 0x5f04;
    case 0xf944: return 0x7c60;
    case 0xf945: return 0x807e;
    case 0xf946: return 0x7262;
    case 0xf947: return 0x78ca;
    case 0xf948: return 0x8cc2;
    case 0xf949: return 0x96f7;
    case 0xf94a: return 0x58d8;
    case 0xf94b: return 0x5c62;
    case 0xf94c: return 0x6a13;
    case 0xf94d: return 0x6dda;
    case 0xf94e: return 0x6f0f;
    case 0xf94f: return 0x7d2f;
    case 0xf950: return 0x7e37;
    case 0xf951: return 0x964b;
    case 0xf952: return 0x52d2;
    case 0xf953: return 0x808b;
    case 0xf954: return 0x51dc;
    case 0xf955: return 0x51cc;
    case 0xf956: return 0x7a1c;
    case 0xf957: return 0x7dbe;
    case 0xf958: return 0x83f1;
    case 0xf959: return 0x9675;
    case 0xf95a: return 0x8b80;
    case 0xf95b: return 0x62cf;
    case 0xf95c: return 0x6a02;
    case 0xf95d: return 0x8afe;
    case 0xf95e: return 0x4e39;
    case 0xf95f: return 0x5be7;
    case 0xf960: return 0x6012;
    case 0xf961: return 0x7387;
    case 0xf962: return 0x7570;
    case 0xf963: return 0x5317;
    case 0xf964: return 0x78fb;
    case 0xf965: return 0x4fbf;
    case 0xf966: return 0x5fa9;
    case 0xf967: return 0x4e0d;
    case 0xf968: return 0x6ccc;
    case 0xf969: return 0x6578;
    case 0xf96a: return 0x7d22;
    case 0xf96b: return 0x53c3;
    case 0xf96c: return 0x585e;
    case 0xf96d: return 0x7701;
    case 0xf96e: return 0x8449;
    case 0xf96f: return 0x8aaa;
    case 0xf970: return 0x6bba;
    case 0xf971: return 0x8fb0;
    case 0xf972: return 0x6c88;
    case 0xf973: return 0x62fe;
    case 0xf974: return 0x82e5;
    case 0xf975: return 0x63a0;
    case 0xf976: return 0x7565;
    case 0xf977: return 0x4eae;
    case 0xf978: return 0x5169;
    case 0xf979: return 0x51c9;
    case 0xf97a: return 0x6881;
    case 0xf97b: return 0x7ce7;
    case 0xf97c: return 0x826f;
    case 0xf97d: return 0x8ad2;
    case 0xf97e: return 0x91cf;
    case 0xf97f: return 0x52f5;
    case 0xf980: return 0x5442;
    case 0xf981: return 0x5973;
    case 0xf982: return 0x5eec;
    case 0xf983: return 0x65c5;
    case 0xf984: return 0x6ffe;
    case 0xf985: return 0x792a;
    case 0xf986: return 0x95ad;
    case 0xf987: return 0x9a6a;
    case 0xf988: return 0x9e97;
    case 0xf989: return 0x9ece;
    case 0xf98a: return 0x529b;
    case 0xf98b: return 0x66c6;
    case 0xf98c: return 0x6b77;
    case 0xf98d: return 0x8f62;
    case 0xf98e: return 0x5e74;
    case 0xf98f: return 0x6190;
    case 0xf990: return 0x6200;
    case 0xf991: return 0x649a;
    case 0xf992: return 0x6f23;
    case 0xf993: return 0x7149;
    case 0xf994: return 0x7489;
    case 0xf995: return 0x79ca;
    case 0xf996: return 0x7df4;
    case 0xf997: return 0x806f;
    case 0xf998: return 0x8f26;
    case 0xf999: return 0x84ee;
    case 0xf99a: return 0x9023;
    case 0xf99b: return 0x934a;
    case 0xf99c: return 0x5217;
    case 0xf99d: return 0x52a3;
    case 0xf99e: return 0x54bd;
    case 0xf99f: return 0x70c8;
    case 0xf9a0: return 0x88c2;
    case 0xf9a1: return 0x8aaa;
    case 0xf9a2: return 0x5ec9;
    case 0xf9a3: return 0x5ff5;
    case 0xf9a4: return 0x637b;
    case 0xf9a5: return 0x6bae;
    case 0xf9a6: return 0x7c3e;
    case 0xf9a7: return 0x7375;
    case 0xf9a8: return 0x4ee4;
    case 0xf9a9: return 0x56f9;
    case 0xf9aa: return 0x5be7;
    case 0xf9ab: return 0x5dba;
    case 0xf9ac: return 0x601c;
    case 0xf9ad: return 0x73b2;
    case 0xf9ae: return 0x7469;
    case 0xf9af: return 0x7f9a;
    case 0xf9b0: return 0x8046;
    case 0xf9b1: return 0x9234;
    case 0xf9b2: return 0x96f6;
    case 0xf9b3: return 0x9748;
    case 0xf9b4: return 0x9818;
    case 0xf9b5: return 0x4f8b;
    case 0xf9b6: return 0x79ae;
    case 0xf9b7: return 0x91b4;
    case 0xf9b8: return 0x96b8;
    case 0xf9b9: return 0x60e1;
    case 0xf9ba: return 0x4e86;
    case 0xf9bb: return 0x50da;
    case 0xf9bc: return 0x5bee;
    case 0xf9bd: return 0x5c3f;
    case 0xf9be: return 0x6599;
    case 0xf9bf: return 0x6a02;
    case 0xf9c0: return 0x71ce;
    case 0xf9c1: return 0x7642;
    case 0xf9c2: return 0x84fc;
    case 0xf9c3: return 0x907c;
    case 0xf9c4: return 0x9f8d;
    case 0xf9c5: return 0x6688;
    case 0xf9c6: return 0x962e;
    case 0xf9c7: return 0x5289;
    case 0xf9c8: return 0x677b;
    case 0xf9c9: return 0x67f3;
    case 0xf9ca: return 0x6d41;
    case 0xf9cb: return 0x6e9c;
    case 0xf9cc: return 0x7409;
    case 0xf9cd: return 0x7559;
    case 0xf9ce: return 0x786b;
    case 0xf9cf: return 0x7d10;
    case 0xf9d0: return 0x985e;
    case 0xf9d1: return 0x516d;
    case 0xf9d2: return 0x622e;
    case 0xf9d3: return 0x9678;
    case 0xf9d4: return 0x502b;
    case 0xf9d5: return 0x5d19;
    case 0xf9d6: return 0x6dea;
    case 0xf9d7: return 0x8f2a;
    case 0xf9d8: return 0x5f8b;
    case 0xf9d9: return 0x6144;
    case 0xf9da: return 0x6817;
    case 0xf9db: return 0x7387;
    case 0xf9dc: return 0x9686;
    case 0xf9dd: return 0x5229;
    case 0xf9de: return 0x540f;
    case 0xf9df: return 0x5c65;
    case 0xf9e0: return 0x6613;
    case 0xf9e1: return 0x674e;
    case 0xf9e2: return 0x68a8;
    case 0xf9e3: return 0x6ce5;
    case 0xf9e4: return 0x7406;
    case 0xf9e5: return 0x75e2;
    case 0xf9e6: return 0x7f79;
    case 0xf9e7: return 0x88cf;
    case 0xf9e8: return 0x88e1;
    case 0xf9e9: return 0x91cc;
    case 0xf9ea: return 0x96e2;
    case 0xf9eb: return 0x533f;
    case 0xf9ec: return 0x6eba;
    case 0xf9ed: return 0x541d;
    case 0xf9ee: return 0x71d0;
    case 0xf9ef: return 0x7498;
    case 0xf9f0: return 0x85fa;
    case 0xf9f1: return 0x96a3;
    case 0xf9f2: return 0x9c57;
    case 0xf9f3: return 0x9e9f;
    case 0xf9f4: return 0x6797;
    case 0xf9f5: return 0x6dcb;
    case 0xf9f6: return 0x81e8;
    case 0xf9f7: return 0x7acb;
    case 0xf9f8: return 0x7b20;
    case 0xf9f9: return 0x7c92;
    case 0xf9fa: return 0x72c0;
    case 0xf9fb: return 0x7099;
    case 0xf9fc: return 0x8b58;
    case 0xf9fd: return 0x4ec0;
    case 0xf9fe: return 0x8336;
    case 0xf9ff: return 0x523a;
    case 0xfa00: return 0x5207;
    case 0xfa01: return 0x5ea6;
    case 0xfa02: return 0x62d3;
    case 0xfa03: return 0x7cd6;
    case 0xfa04: return 0x5b85;
    case 0xfa05: return 0x6d1e;
    case 0xfa06: return 0x66b4;
    case 0xfa07: return 0x8f3b;
    case 0xfa08: return 0x884c;
    case 0xfa09: return 0x964d;
    case 0xfa0a: return 0x898b;
    case 0xfa0b: return 0x5ed3;
    case 0xfa0c: return 0x5140;
    case 0xfa0d: return 0x55c0;
    case 0xfa10: return 0x585a;
    case 0xfa12: return 0x6674;
    case 0xfa15: return 0x51de;
    case 0xfa16: return 0x732a;
    case 0xfa17: return 0x76ca;
    case 0xfa18: return 0x793c;
    case 0xfa19: return 0x795e;
    case 0xfa1a: return 0x7965;
    case 0xfa1b: return 0x798f;
    case 0xfa1c: return 0x9756;
    case 0xfa1d: return 0x7cbe;
    case 0xfa1e: return 0x7fbd;
    case 0xfa20: return 0x8612;
    case 0xfa22: return 0x8af8;
    case 0xfa25: return 0x9038;
    case 0xfa26: return 0x90fd;
    case 0xfa2a: return 0x98ef;
    case 0xfa2b: return 0x98fc;
    case 0xfa2c: return 0x9928;
    case 0xfa2d: return 0x9db4;
    case 0xfa2e: return 0x90de;
    case 0xfa2f: return 0x96b7;
    case 0xfa30: return 0x4fae;
    case 0xfa31: return 0x50e7;
    case 0xfa32: return 0x514d;
    case 0xfa33: return 0x52c9;
    case 0xfa34: return 0x52e4;
    case 0xfa35: return 0x5351;
    case 0xfa36: return 0x559d;
    case 0xfa37: return 0x5606;
    case 0xfa38: return 0x5668;
    case 0xfa39: return 0x5840;
    case 0xfa3a: return 0x58a8;
    case 0xfa3b: return 0x5c64;
    case 0xfa3c: return 0x5c6e;
    case 0xfa3d: return 0x6094;
    case 0xfa3e: return 0x6168;
    case 0xfa3f: return 0x618e;
    case 0xfa40: return 0x61f2;
    case 0xfa41: return 0x654f;
    case 0xfa42: return 0x65e2;
    case 0xfa43: return 0x6691;
    case 0xfa44: return 0x6885;
    case 0xfa45: return 0x6d77;
    case 0xfa46: return 0x6e1a;
    case 0xfa47: return 0x6f22;
    case 0xfa48: return 0x716e;
    case 0xfa49: return 0x722b;
    case 0xfa4a: return 0x7422;
    case 0xfa4b: return 0x7891;
    case 0xfa4c: return 0x793e;
    case 0xfa4d: return 0x7949;
    case 0xfa4e: return 0x7948;
    case 0xfa4f: return 0x7950;
    case 0xfa50: return 0x7956;
    case 0xfa51: return 0x795d;
    case 0xfa52: return 0x798d;
    case 0xfa53: return 0x798e;
    case 0xfa54: return 0x7a40;
    case 0xfa55: return 0x7a81;
    case 0xfa56: return 0x7bc0;
    case 0xfa57: return 0x7df4;
    case 0xfa58: return 0x7e09;
    case 0xfa59: return 0x7e41;
    case 0xfa5a: return 0x7f72;
    case 0xfa5b: return 0x8005;
    case 0xfa5c: return 0x81ed;
    case 0xfa5d: return 0x8279;
    case 0xfa5e: return 0x8279;
    case 0xfa5f: return 0x8457;
    case 0xfa60: return 0x8910;
    case 0xfa61: return 0x8996;
    case 0xfa62: return 0x8b01;
    case 0xfa63: return 0x8b39;
    case 0xfa64: return 0x8cd3;
    case 0xfa65: return 0x8d08;
    case 0xfa66: return 0x8fb6;
    case 0xfa67: return 0x9038;
    case 0xfa68: return 0x96e3;
    case 0xfa69: return 0x97ff;
    case 0xfa6a: return 0x983b;
    case 0xfa6b: return 0x6075;
    case 0xfa6c: return 0x242ee;
    case 0xfa6d: return 0x8218;
    case 0xfa70: return 0x4e26;
    case 0xfa71: return 0x51b5;
    case 0xfa72: return 0x5168;
    case 0xfa73: return 0x4f80;
    case 0xfa74: return 0x5145;
    case 0xfa75: return 0x5180;
    case 0xfa76: return 0x52c7;
    case 0xfa77: return 0x52fa;
    case 0xfa78: return 0x559d;
    case 0xfa79: return 0x5555;
    case 0xfa7a: return 0x5599;
    case 0xfa7b: return 0x55e2;
    case 0xfa7c: return 0x585a;
    case 0xfa7d: return 0x58b3;
    case 0xfa7e: return 0x5944;
    case 0xfa7f: return 0x5954;
    case 0xfa80: return 0x5a62;
    case 0xfa81: return 0x5b28;
    case 0xfa82: return 0x5ed2;
    case 0xfa83: return 0x5ed9;
    case 0xfa84: return 0x5f69;
    case 0xfa85: return 0x5fad;
    case 0xfa86: return 0x60d8;
    case 0xfa87: return 0x614e;
    case 0xfa88: return 0x6108;
    case 0xfa89: return 0x618e;
    case 0xfa8a: return 0x6160;
    case 0xfa8b: return 0x61f2;
    case 0xfa8c: return 0x6234;
    case 0xfa8d: return 0x63c4;
    case 0xfa8e: return 0x641c;
    case 0xfa8f: return 0x6452;
    case 0xfa90: return 0x6556;
    case 0xfa91: return 0x6674;
    case 0xfa92: return 0x6717;
    case 0xfa93: return 0x671b;
    case 0xfa94: return 0x6756;
    case 0xfa95: return 0x6b79;
    case 0xfa96: return 0x6bba;
    case 0xfa97: return 0x6d41;
    case 0xfa98: return 0x6edb;
    case 0xfa99: return 0x6ecb;
    case 0xfa9a: return 0x6f22;
    case 0xfa9b: return 0x701e;
    case 0xfa9c: return 0x716e;
    case 0xfa9d: return 0x77a7;
    case 0xfa9e: return 0x7235;
    case 0xfa9f: return 0x72af;
    case 0xfaa0: return 0x732a;
    case 0xfaa1: return 0x7471;
    case 0xfaa2: return 0x7506;
    case 0xfaa3: return 0x753b;
    case 0xfaa4: return 0x761d;
    case 0xfaa5: return 0x761f;
    case 0xfaa6: return 0x76ca;
    case 0xfaa7: return 0x76db;
    case 0xfaa8: return 0x76f4;
    case 0xfaa9: return 0x774a;
    case 0xfaaa: return 0x7740;
    case 0xfaab: return 0x78cc;
    case 0xfaac: return 0x7ab1;
    case 0xfaad: return 0x7bc0;
    case 0xfaae: return 0x7c7b;
    case 0xfaaf: return 0x7d5b;
    case 0xfab0: return 0x7df4;
    case 0xfab1: return 0x7f3e;
    case 0xfab2: return 0x8005;
    case 0xfab3: return 0x8352;
    case 0xfab4: return 0x83ef;
    case 0xfab5: return 0x8779;
    case 0xfab6: return 0x8941;
    case 0xfab7: return 0x8986;
    case 0xfab8: return 0x8996;
    case 0xfab9: return 0x8abf;
    case 0xfaba: return 0x8af8;
    case 0xfabb: return 0x8acb;
    case 0xfabc: return 0x8b01;
    case 0xfabd: return 0x8afe;
    case 0xfabe: return 0x8aed;
    case 0xfabf: return 0x8b39;
    case 0xfac0: return 0x8b8a;
    case 0xfac1: return 0x8d08;
    case 0xfac2: return 0x8f38;
    case 0xfac3: return 0x9072;
    case 0xfac4: return 0x9199;
    case 0xfac5: return 0x9276;
    case 0xfac6: return 0x967c;
    case 0xfac7: return 0x96e3;
    case 0xfac8: return 0x9756;
    case 0xfac9: return 0x97db;
    case 0xfaca: return 0x97ff;
    case 0xfacb: return 0x980b;
    case 0xfacc: return 0x983b;
    case 0xfacd: return 0x9b12;
    case 0xface: return 0x9f9c;
    case 0xfacf: return 0x2284a;
    case 0xfad0: return 0x22844;
    case 0xfad1: return 0x233d5;
    case 0xfad2: return 0x3b9d;
    case 0xfad3: return 0x4018;
    case 0xfad4: return 0x4039;
    case 0xfad5: return 0x25249;
    case 0xfad6: return 0x25cd0;
    case 0xfad7: return 0x27ed3;
    case 0xfad8: return 0x9f43;
    case 0xfad9: return 0x9f8e;
    case 0xfb1d: return 0x5d9;
    case 0xfb1e: return 0x0;
    case 0xfb1f: return 0x5f2;
    case 0xfb2a: return 0x5e9;
    case 0xfb2b: return 0x5e9;
    case 0xfb2c: return 0x5e9;
    case 0xfb2d: return 0x5e9;
    case 0xfb2e: return 0x5d0;
    case 0xfb2f: return 0x5d0;
    case 0xfb30: return 0x5d0;
    case 0xfb31: return 0x5d1;
    case 0xfb32: return 0x5d2;
    case 0xfb33: return 0x5d3;
    case 0xfb34: return 0x5d4;
    case 0xfb35: return 0x5d5;
    case 0xfb36: return 0x5d6;
    case 0xfb38: return 0x5d8;
    case 0xfb39: return 0x5d9;
    case 0xfb3a: return 0x5da;
    case 0xfb3b: return 0x5db;
    case 0xfb3c: return 0x5dc;
    case 0xfb3e: return 0x5de;
    case 0xfb40: return 0x5e0;
    case 0xfb41: return 0x5e1;
    case 0xfb43: return 0x5e3;
    case 0xfb44: return 0x5e4;
    case 0xfb46: return 0x5e6;
    case 0xfb47: return 0x5e7;
    case 0xfb48: return 0x5e8;
    case 0xfb49: return 0x5e9;
    case 0xfb4a: return 0x5ea;
    case 0xfb4b: return 0x5d5;
    case 0xfb4c: return 0x5d1;
    case 0xfb4d: return 0x5db;
    case 0xfb4e: return 0x5e4;
    case 0xfe20: return 0x0;
    case 0xfe21: return 0x0;
    case 0xfe22: return 0x0;
    case 0xfe23: return 0x0;
    case 0xfe24: return 0x0;
    case 0xfe25: return 0x0;
    case 0xfe26: return 0x0;
    case 0xfe27: return 0x0;
    case 0xfe28: return 0x0;
    case 0xfe29: return 0x0;
    case 0xfe2a: return 0x0;
    case 0xfe2b: return 0x0;
    case 0xfe2c: return 0x0;
    case 0xfe2d: return 0x0;
    case 0xfe2e: return 0x0;
    case 0xfe2f: return 0x0;
    case 0xff3e: return 0x0;
    case 0xff40: return 0x0;
    case 0xff70: return 0x0;
    case 0xff9e: return 0x0;
    case 0xff9f: return 0x0;
    case 0xffe3: return 0x0;
    case 0x102e0: return 0x0;
    case 0x10ae5: return 0x0;
    case 0x10ae6: return 0x0;
    case 0x10d22: return 0x0;
    case 0x10d23: return 0x0;
    case 0x10d24: return 0x0;
    case 0x10d25: return 0x0;
    case 0x10d26: return 0x0;
    case 0x10d27: return 0x0;
    case 0x10f46: return 0x0;
    case 0x10f47: return 0x0;
    case 0x10f48: return 0x0;
    case 0x10f49: return 0x0;
    case 0x10f4a: return 0x0;
    case 0x10f4b: return 0x0;
    case 0x10f4c: return 0x0;
    case 0x10f4d: return 0x0;
    case 0x10f4e: return 0x0;
    case 0x10f4f: return 0x0;
    case 0x10f50: return 0x0;
    case 0x1109a: return 0x11099;
    case 0x1109c: return 0x1109b;
    case 0x110ab: return 0x110a5;
    case 0x110b9: return 0x0;
    case 0x110ba: return 0x0;
    case 0x11133: return 0x0;
    case 0x11134: return 0x0;
    case 0x11173: return 0x0;
    case 0x111c0: return 0x0;
    case 0x111ca: return 0x0;
    case 0x111cb: return 0x0;
    case 0x111cc: return 0x0;
    case 0x11235: return 0x0;
    case 0x11236: return 0x0;
    case 0x112e9: return 0x0;
    case 0x112ea: return 0x0;
    case 0x1133c: return 0x0;
    case 0x1134d: return 0x0;
    case 0x11366: return 0x0;
    case 0x11367: return 0x0;
    case 0x11368: return 0x0;
    case 0x11369: return 0x0;
    case 0x1136a: return 0x0;
    case 0x1136b: return 0x0;
    case 0x1136c: return 0x0;
    case 0x11370: return 0x0;
    case 0x11371: return 0x0;
    case 0x11372: return 0x0;
    case 0x11373: return 0x0;
    case 0x11374: return 0x0;
    case 0x11442: return 0x0;
    case 0x11446: return 0x0;
    case 0x114c2: return 0x0;
    case 0x114c3: return 0x0;
    case 0x115bf: return 0x0;
    case 0x115c0: return 0x0;
    case 0x1163f: return 0x0;
    case 0x116b6: return 0x0;
    case 0x116b7: return 0x0;
    case 0x1172b: return 0x0;
    case 0x11839: return 0x0;
    case 0x1183a: return 0x0;
    case 0x1193d: return 0x0;
    case 0x1193e: return 0x0;
    case 0x11943: return 0x0;
    case 0x119e0: return 0x0;
    case 0x11a34: return 0x0;
    case 0x11a47: return 0x0;
    case 0x11a99: return 0x0;
    case 0x11c3f: return 0x0;
    case 0x11d42: return 0x0;
    case 0x11d44: return 0x0;
    case 0x11d45: return 0x0;
    case 0x11d97: return 0x0;
    case 0x16af0: return 0x0;
    case 0x16af1: return 0x0;
    case 0x16af2: return 0x0;
    case 0x16af3: return 0x0;
    case 0x16af4: return 0x0;
    case 0x16b30: return 0x0;
    case 0x16b31: return 0x0;
    case 0x16b32: return 0x0;
    case 0x16b33: return 0x0;
    case 0x16b34: return 0x0;
    case 0x16b35: return 0x0;
    case 0x16b36: return 0x0;
    case 0x16f8f: return 0x0;
    case 0x16f90: return 0x0;
    case 0x16f91: return 0x0;
    case 0x16f92: return 0x0;
    case 0x16f93: return 0x0;
    case 0x16f94: return 0x0;
    case 0x16f95: return 0x0;
    case 0x16f96: return 0x0;
    case 0x16f97: return 0x0;
    case 0x16f98: return 0x0;
    case 0x16f99: return 0x0;
    case 0x16f9a: return 0x0;
    case 0x16f9b: return 0x0;
    case 0x16f9c: return 0x0;
    case 0x16f9d: return 0x0;
    case 0x16f9e: return 0x0;
    case 0x16f9f: return 0x0;
    case 0x16ff0: return 0x0;
    case 0x16ff1: return 0x0;
    case 0x1d167: return 0x0;
    case 0x1d168: return 0x0;
    case 0x1d169: return 0x0;
    case 0x1d16d: return 0x0;
    case 0x1d16e: return 0x0;
    case 0x1d16f: return 0x0;
    case 0x1d170: return 0x0;
    case 0x1d171: return 0x0;
    case 0x1d172: return 0x0;
    case 0x1d17b: return 0x0;
    case 0x1d17c: return 0x0;
    case 0x1d17d: return 0x0;
    case 0x1d17e: return 0x0;
    case 0x1d17f: return 0x0;
    case 0x1d180: return 0x0;
    case 0x1d181: return 0x0;
    case 0x1d182: return 0x0;
    case 0x1d185: return 0x0;
    case 0x1d186: return 0x0;
    case 0x1d187: return 0x0;
    case 0x1d188: return 0x0;
    case 0x1d189: return 0x0;
    case 0x1d18a: return 0x0;
    case 0x1d18b: return 0x0;
    case 0x1d1aa: return 0x0;
    case 0x1d1ab: return 0x0;
    case 0x1d1ac: return 0x0;
    case 0x1d1ad: return 0x0;
    case 0x1e130: return 0x0;
    case 0x1e131: return 0x0;
    case 0x1e132: return 0x0;
    case 0x1e133: return 0x0;
    case 0x1e134: return 0x0;
    case 0x1e135: return 0x0;
    case 0x1e136: return 0x0;
    case 0x1e2ec: return 0x0;
    case 0x1e2ed: return 0x0;
    case 0x1e2ee: return 0x0;
    case 0x1e2ef: return 0x0;
    case 0x1e8d0: return 0x0;
    case 0x1e8d1: return 0x0;
    case 0x1e8d2: return 0x0;
    case 0x1e8d3: return 0x0;
    case 0x1e8d4: return 0x0;
    case 0x1e8d5: return 0x0;
    case 0x1e8d6: return 0x0;
    case 0x1e944: return 0x0;
    case 0x1e945: return 0x0;
    case 0x1e946: return 0x0;
    case 0x1e948: return 0x0;
    case 0x1e949: return 0x0;
    case 0x1e94a: return 0x0;
    case 0x2f800: return 0x4e3d;
    case 0x2f801: return 0x4e38;
    case 0x2f802: return 0x4e41;
    case 0x2f803: return 0x20122;
    case 0x2f804: return 0x4f60;
    case 0x2f805: return 0x4fae;
    case 0x2f806: return 0x4fbb;
    case 0x2f807: return 0x5002;
    case 0x2f808: return 0x507a;
    case 0x2f809: return 0x5099;
    case 0x2f80a: return 0x50e7;
    case 0x2f80b: return 0x50cf;
    case 0x2f80c: return 0x349e;
    case 0x2f80d: return 0x2063a;
    case 0x2f80e: return 0x514d;
    case 0x2f80f: return 0x5154;
    case 0x2f810: return 0x5164;
    case 0x2f811: return 0x5177;
    case 0x2f812: return 0x2051c;
    case 0x2f813: return 0x34b9;
    case 0x2f814: return 0x5167;
    case 0x2f815: return 0x518d;
    case 0x2f816: return 0x2054b;
    case 0x2f817: return 0x5197;
    case 0x2f818: return 0x51a4;
    case 0x2f819: return 0x4ecc;
    case 0x2f81a: return 0x51ac;
    case 0x2f81b: return 0x51b5;
    case 0x2f81c: return 0x291df;
    case 0x2f81d: return 0x51f5;
    case 0x2f81e: return 0x5203;
    case 0x2f81f: return 0x34df;
    case 0x2f820: return 0x523b;
    case 0x2f821: return 0x5246;
    case 0x2f822: return 0x5272;
    case 0x2f823: return 0x5277;
    case 0x2f824: return 0x3515;
    case 0x2f825: return 0x52c7;
    case 0x2f826: return 0x52c9;
    case 0x2f827: return 0x52e4;
    case 0x2f828: return 0x52fa;
    case 0x2f829: return 0x5305;
    case 0x2f82a: return 0x5306;
    case 0x2f82b: return 0x5317;
    case 0x2f82c: return 0x5349;
    case 0x2f82d: return 0x5351;
    case 0x2f82e: return 0x535a;
    case 0x2f82f: return 0x5373;
    case 0x2f830: return 0x537d;
    case 0x2f831: return 0x537f;
    case 0x2f832: return 0x537f;
    case 0x2f833: return 0x537f;
    case 0x2f834: return 0x20a2c;
    case 0x2f835: return 0x7070;
    case 0x2f836: return 0x53ca;
    case 0x2f837: return 0x53df;
    case 0x2f838: return 0x20b63;
    case 0x2f839: return 0x53eb;
    case 0x2f83a: return 0x53f1;
    case 0x2f83b: return 0x5406;
    case 0x2f83c: return 0x549e;
    case 0x2f83d: return 0x5438;
    case 0x2f83e: return 0x5448;
    case 0x2f83f: return 0x5468;
    case 0x2f840: return 0x54a2;
    case 0x2f841: return 0x54f6;
    case 0x2f842: return 0x5510;
    case 0x2f843: return 0x5553;
    case 0x2f844: return 0x5563;
    case 0x2f845: return 0x5584;
    case 0x2f846: return 0x5584;
    case 0x2f847: return 0x5599;
    case 0x2f848: return 0x55ab;
    case 0x2f849: return 0x55b3;
    case 0x2f84a: return 0x55c2;
    case 0x2f84b: return 0x5716;
    case 0x2f84c: return 0x5606;
    case 0x2f84d: return 0x5717;
    case 0x2f84e: return 0x5651;
    case 0x2f84f: return 0x5674;
    case 0x2f850: return 0x5207;
    case 0x2f851: return 0x58ee;
    case 0x2f852: return 0x57ce;
    case 0x2f853: return 0x57f4;
    case 0x2f854: return 0x580d;
    case 0x2f855: return 0x578b;
    case 0x2f856: return 0x5832;
    case 0x2f857: return 0x5831;
    case 0x2f858: return 0x58ac;
    case 0x2f859: return 0x214e4;
    case 0x2f85a: return 0x58f2;
    case 0x2f85b: return 0x58f7;
    case 0x2f85c: return 0x5906;
    case 0x2f85d: return 0x591a;
    case 0x2f85e: return 0x5922;
    case 0x2f85f: return 0x5962;
    case 0x2f860: return 0x216a8;
    case 0x2f861: return 0x216ea;
    case 0x2f862: return 0x59ec;
    case 0x2f863: return 0x5a1b;
    case 0x2f864: return 0x5a27;
    case 0x2f865: return 0x59d8;
    case 0x2f866: return 0x5a66;
    case 0x2f867: return 0x36ee;
    case 0x2f868: return 0x36fc;
    case 0x2f869: return 0x5b08;
    case 0x2f86a: return 0x5b3e;
    case 0x2f86b: return 0x5b3e;
    case 0x2f86c: return 0x219c8;
    case 0x2f86d: return 0x5bc3;
    case 0x2f86e: return 0x5bd8;
    case 0x2f86f: return 0x5be7;
    case 0x2f870: return 0x5bf3;
    case 0x2f871: return 0x21b18;
    case 0x2f872: return 0x5bff;
    case 0x2f873: return 0x5c06;
    case 0x2f874: return 0x5f53;
    case 0x2f875: return 0x5c22;
    case 0x2f876: return 0x3781;
    case 0x2f877: return 0x5c60;
    case 0x2f878: return 0x5c6e;
    case 0x2f879: return 0x5cc0;
    case 0x2f87a: return 0x5c8d;
    case 0x2f87b: return 0x21de4;
    case 0x2f87c: return 0x5d43;
    case 0x2f87d: return 0x21de6;
    case 0x2f87e: return 0x5d6e;
    case 0x2f87f: return 0x5d6b;
    case 0x2f880: return 0x5d7c;
    case 0x2f881: return 0x5de1;
    case 0x2f882: return 0x5de2;
    case 0x2f883: return 0x382f;
    case 0x2f884: return 0x5dfd;
    case 0x2f885: return 0x5e28;
    case 0x2f886: return 0x5e3d;
    case 0x2f887: return 0x5e69;
    case 0x2f888: return 0x3862;
    case 0x2f889: return 0x22183;
    case 0x2f88a: return 0x387c;
    case 0x2f88b: return 0x5eb0;
    case 0x2f88c: return 0x5eb3;
    case 0x2f88d: return 0x5eb6;
    case 0x2f88e: return 0x5eca;
    case 0x2f88f: return 0x2a392;
    case 0x2f890: return 0x5efe;
    case 0x2f891: return 0x22331;
    case 0x2f892: return 0x22331;
    case 0x2f893: return 0x8201;
    case 0x2f894: return 0x5f22;
    case 0x2f895: return 0x5f22;
    case 0x2f896: return 0x38c7;
    case 0x2f897: return 0x232b8;
    case 0x2f898: return 0x261da;
    case 0x2f899: return 0x5f62;
    case 0x2f89a: return 0x5f6b;
    case 0x2f89b: return 0x38e3;
    case 0x2f89c: return 0x5f9a;
    case 0x2f89d: return 0x5fcd;
    case 0x2f89e: return 0x5fd7;
    case 0x2f89f: return 0x5ff9;
    case 0x2f8a0: return 0x6081;
    case 0x2f8a1: return 0x393a;
    case 0x2f8a2: return 0x391c;
    case 0x2f8a3: return 0x6094;
    case 0x2f8a4: return 0x226d4;
    case 0x2f8a5: return 0x60c7;
    case 0x2f8a6: return 0x6148;
    case 0x2f8a7: return 0x614c;
    case 0x2f8a8: return 0x614e;
    case 0x2f8a9: return 0x614c;
    case 0x2f8aa: return 0x617a;
    case 0x2f8ab: return 0x618e;
    case 0x2f8ac: return 0x61b2;
    case 0x2f8ad: return 0x61a4;
    case 0x2f8ae: return 0x61af;
    case 0x2f8af: return 0x61de;
    case 0x2f8b0: return 0x61f2;
    case 0x2f8b1: return 0x61f6;
    case 0x2f8b2: return 0x6210;
    case 0x2f8b3: return 0x621b;
    case 0x2f8b4: return 0x625d;
    case 0x2f8b5: return 0x62b1;
    case 0x2f8b6: return 0x62d4;
    case 0x2f8b7: return 0x6350;
    case 0x2f8b8: return 0x22b0c;
    case 0x2f8b9: return 0x633d;
    case 0x2f8ba: return 0x62fc;
    case 0x2f8bb: return 0x6368;
    case 0x2f8bc: return 0x6383;
    case 0x2f8bd: return 0x63e4;
    case 0x2f8be: return 0x22bf1;
    case 0x2f8bf: return 0x6422;
    case 0x2f8c0: return 0x63c5;
    case 0x2f8c1: return 0x63a9;
    case 0x2f8c2: return 0x3a2e;
    case 0x2f8c3: return 0x6469;
    case 0x2f8c4: return 0x647e;
    case 0x2f8c5: return 0x649d;
    case 0x2f8c6: return 0x6477;
    case 0x2f8c7: return 0x3a6c;
    case 0x2f8c8: return 0x654f;
    case 0x2f8c9: return 0x656c;
    case 0x2f8ca: return 0x2300a;
    case 0x2f8cb: return 0x65e3;
    case 0x2f8cc: return 0x66f8;
    case 0x2f8cd: return 0x6649;
    case 0x2f8ce: return 0x3b19;
    case 0x2f8cf: return 0x6691;
    case 0x2f8d0: return 0x3b08;
    case 0x2f8d1: return 0x3ae4;
    case 0x2f8d2: return 0x5192;
    case 0x2f8d3: return 0x5195;
    case 0x2f8d4: return 0x6700;
    case 0x2f8d5: return 0x669c;
    case 0x2f8d6: return 0x80ad;
    case 0x2f8d7: return 0x43d9;
    case 0x2f8d8: return 0x6717;
    case 0x2f8d9: return 0x671b;
    case 0x2f8da: return 0x6721;
    case 0x2f8db: return 0x675e;
    case 0x2f8dc: return 0x6753;
    case 0x2f8dd: return 0x233c3;
    case 0x2f8de: return 0x3b49;
    case 0x2f8df: return 0x67fa;
    case 0x2f8e0: return 0x6785;
    case 0x2f8e1: return 0x6852;
    case 0x2f8e2: return 0x6885;
    case 0x2f8e3: return 0x2346d;
    case 0x2f8e4: return 0x688e;
    case 0x2f8e5: return 0x681f;
    case 0x2f8e6: return 0x6914;
    case 0x2f8e7: return 0x3b9d;
    case 0x2f8e8: return 0x6942;
    case 0x2f8e9: return 0x69a3;
    case 0x2f8ea: return 0x69ea;
    case 0x2f8eb: return 0x6aa8;
    case 0x2f8ec: return 0x236a3;
    case 0x2f8ed: return 0x6adb;
    case 0x2f8ee: return 0x3c18;
    case 0x2f8ef: return 0x6b21;
    case 0x2f8f0: return 0x238a7;
    case 0x2f8f1: return 0x6b54;
    case 0x2f8f2: return 0x3c4e;
    case 0x2f8f3: return 0x6b72;
    case 0x2f8f4: return 0x6b9f;
    case 0x2f8f5: return 0x6bba;
    case 0x2f8f6: return 0x6bbb;
    case 0x2f8f7: return 0x23a8d;
    case 0x2f8f8: return 0x21d0b;
    case 0x2f8f9: return 0x23afa;
    case 0x2f8fa: return 0x6c4e;
    case 0x2f8fb: return 0x23cbc;
    case 0x2f8fc: return 0x6cbf;
    case 0x2f8fd: return 0x6ccd;
    case 0x2f8fe: return 0x6c67;
    case 0x2f8ff: return 0x6d16;
    case 0x2f900: return 0x6d3e;
    case 0x2f901: return 0x6d77;
    case 0x2f902: return 0x6d41;
    case 0x2f903: return 0x6d69;
    case 0x2f904: return 0x6d78;
    case 0x2f905: return 0x6d85;
    case 0x2f906: return 0x23d1e;
    case 0x2f907: return 0x6d34;
    case 0x2f908: return 0x6e2f;
    case 0x2f909: return 0x6e6e;
    case 0x2f90a: return 0x3d33;
    case 0x2f90b: return 0x6ecb;
    case 0x2f90c: return 0x6ec7;
    case 0x2f90d: return 0x23ed1;
    case 0x2f90e: return 0x6df9;
    case 0x2f90f: return 0x6f6e;
    case 0x2f910: return 0x23f5e;
    case 0x2f911: return 0x23f8e;
    case 0x2f912: return 0x6fc6;
    case 0x2f913: return 0x7039;
    case 0x2f914: return 0x701e;
    case 0x2f915: return 0x701b;
    case 0x2f916: return 0x3d96;
    case 0x2f917: return 0x704a;
    case 0x2f918: return 0x707d;
    case 0x2f919: return 0x7077;
    case 0x2f91a: return 0x70ad;
    case 0x2f91b: return 0x20525;
    case 0x2f91c: return 0x7145;
    case 0x2f91d: return 0x24263;
    case 0x2f91e: return 0x719c;
    case 0x2f91f: return 0x243ab;
    case 0x2f920: return 0x7228;
    case 0x2f921: return 0x7235;
    case 0x2f922: return 0x7250;
    case 0x2f923: return 0x24608;
    case 0x2f924: return 0x7280;
    case 0x2f925: return 0x7295;
    case 0x2f926: return 0x24735;
    case 0x2f927: return 0x24814;
    case 0x2f928: return 0x737a;
    case 0x2f929: return 0x738b;
    case 0x2f92a: return 0x3eac;
    case 0x2f92b: return 0x73a5;
    case 0x2f92c: return 0x3eb8;
    case 0x2f92d: return 0x3eb8;
    case 0x2f92e: return 0x7447;
    case 0x2f92f: return 0x745c;
    case 0x2f930: return 0x7471;
    case 0x2f931: return 0x7485;
    case 0x2f932: return 0x74ca;
    case 0x2f933: return 0x3f1b;
    case 0x2f934: return 0x7524;
    case 0x2f935: return 0x24c36;
    case 0x2f936: return 0x753e;
    case 0x2f937: return 0x24c92;
    case 0x2f938: return 0x7570;
    case 0x2f939: return 0x2219f;
    case 0x2f93a: return 0x7610;
    case 0x2f93b: return 0x24fa1;
    case 0x2f93c: return 0x24fb8;
    case 0x2f93d: return 0x25044;
    case 0x2f93e: return 0x3ffc;
    case 0x2f93f: return 0x4008;
    case 0x2f940: return 0x76f4;
    case 0x2f941: return 0x250f3;
    case 0x2f942: return 0x250f2;
    case 0x2f943: return 0x25119;
    case 0x2f944: return 0x25133;
    case 0x2f945: return 0x771e;
    case 0x2f946: return 0x771f;
    case 0x2f947: return 0x771f;
    case 0x2f948: return 0x774a;
    case 0x2f949: return 0x4039;
    case 0x2f94a: return 0x778b;
    case 0x2f94b: return 0x4046;
    case 0x2f94c: return 0x4096;
    case 0x2f94d: return 0x2541d;
    case 0x2f94e: return 0x784e;
    case 0x2f94f: return 0x788c;
    case 0x2f950: return 0x78cc;
    case 0x2f951: return 0x40e3;
    case 0x2f952: return 0x25626;
    case 0x2f953: return 0x7956;
    case 0x2f954: return 0x2569a;
    case 0x2f955: return 0x256c5;
    case 0x2f956: return 0x798f;
    case 0x2f957: return 0x79eb;
    case 0x2f958: return 0x412f;
    case 0x2f959: return 0x7a40;
    case 0x2f95a: return 0x7a4a;
    case 0x2f95b: return 0x7a4f;
    case 0x2f95c: return 0x2597c;
    case 0x2f95d: return 0x25aa7;
    case 0x2f95e: return 0x25aa7;
    case 0x2f95f: return 0x7aee;
    case 0x2f960: return 0x4202;
    case 0x2f961: return 0x25bab;
    case 0x2f962: return 0x7bc6;
    case 0x2f963: return 0x7bc9;
    case 0x2f964: return 0x4227;
    case 0x2f965: return 0x25c80;
    case 0x2f966: return 0x7cd2;
    case 0x2f967: return 0x42a0;
    case 0x2f968: return 0x7ce8;
    case 0x2f969: return 0x7ce3;
    case 0x2f96a: return 0x7d00;
    case 0x2f96b: return 0x25f86;
    case 0x2f96c: return 0x7d63;
    case 0x2f96d: return 0x4301;
    case 0x2f96e: return 0x7dc7;
    case 0x2f96f: return 0x7e02;
    case 0x2f970: return 0x7e45;
    case 0x2f971: return 0x4334;
    case 0x2f972: return 0x26228;
    case 0x2f973: return 0x26247;
    case 0x2f974: return 0x4359;
    case 0x2f975: return 0x262d9;
    case 0x2f976: return 0x7f7a;
    case 0x2f977: return 0x2633e;
    case 0x2f978: return 0x7f95;
    case 0x2f979: return 0x7ffa;
    case 0x2f97a: return 0x8005;
    case 0x2f97b: return 0x264da;
    case 0x2f97c: return 0x26523;
    case 0x2f97d: return 0x8060;
    case 0x2f97e: return 0x265a8;
    case 0x2f97f: return 0x8070;
    case 0x2f980: return 0x2335f;
    case 0x2f981: return 0x43d5;
    case 0x2f982: return 0x80b2;
    case 0x2f983: return 0x8103;
    case 0x2f984: return 0x440b;
    case 0x2f985: return 0x813e;
    case 0x2f986: return 0x5ab5;
    case 0x2f987: return 0x267a7;
    case 0x2f988: return 0x267b5;
    case 0x2f989: return 0x23393;
    case 0x2f98a: return 0x2339c;
    case 0x2f98b: return 0x8201;
    case 0x2f98c: return 0x8204;
    case 0x2f98d: return 0x8f9e;
    case 0x2f98e: return 0x446b;
    case 0x2f98f: return 0x8291;
    case 0x2f990: return 0x828b;
    case 0x2f991: return 0x829d;
    case 0x2f992: return 0x52b3;
    case 0x2f993: return 0x82b1;
    case 0x2f994: return 0x82b3;
    case 0x2f995: return 0x82bd;
    case 0x2f996: return 0x82e6;
    case 0x2f997: return 0x26b3c;
    case 0x2f998: return 0x82e5;
    case 0x2f999: return 0x831d;
    case 0x2f99a: return 0x8363;
    case 0x2f99b: return 0x83ad;
    case 0x2f99c: return 0x8323;
    case 0x2f99d: return 0x83bd;
    case 0x2f99e: return 0x83e7;
    case 0x2f99f: return 0x8457;
    case 0x2f9a0: return 0x8353;
    case 0x2f9a1: return 0x83ca;
    case 0x2f9a2: return 0x83cc;
    case 0x2f9a3: return 0x83dc;
    case 0x2f9a4: return 0x26c36;
    case 0x2f9a5: return 0x26d6b;
    case 0x2f9a6: return 0x26cd5;
    case 0x2f9a7: return 0x452b;
    case 0x2f9a8: return 0x84f1;
    case 0x2f9a9: return 0x84f3;
    case 0x2f9aa: return 0x8516;
    case 0x2f9ab: return 0x273ca;
    case 0x2f9ac: return 0x8564;
    case 0x2f9ad: return 0x26f2c;
    case 0x2f9ae: return 0x455d;
    case 0x2f9af: return 0x4561;
    case 0x2f9b0: return 0x26fb1;
    case 0x2f9b1: return 0x270d2;
    case 0x2f9b2: return 0x456b;
    case 0x2f9b3: return 0x8650;
    case 0x2f9b4: return 0x865c;
    case 0x2f9b5: return 0x8667;
    case 0x2f9b6: return 0x8669;
    case 0x2f9b7: return 0x86a9;
    case 0x2f9b8: return 0x8688;
    case 0x2f9b9: return 0x870e;
    case 0x2f9ba: return 0x86e2;
    case 0x2f9bb: return 0x8779;
    case 0x2f9bc: return 0x8728;
    case 0x2f9bd: return 0x876b;
    case 0x2f9be: return 0x8786;
    case 0x2f9bf: return 0x45d7;
    case 0x2f9c0: return 0x87e1;
    case 0x2f9c1: return 0x8801;
    case 0x2f9c2: return 0x45f9;
    case 0x2f9c3: return 0x8860;
    case 0x2f9c4: return 0x8863;
    case 0x2f9c5: return 0x27667;
    case 0x2f9c6: return 0x88d7;
    case 0x2f9c7: return 0x88de;
    case 0x2f9c8: return 0x4635;
    case 0x2f9c9: return 0x88fa;
    case 0x2f9ca: return 0x34bb;
    case 0x2f9cb: return 0x278ae;
    case 0x2f9cc: return 0x27966;
    case 0x2f9cd: return 0x46be;
    case 0x2f9ce: return 0x46c7;
    case 0x2f9cf: return 0x8aa0;
    case 0x2f9d0: return 0x8aed;
    case 0x2f9d1: return 0x8b8a;
    case 0x2f9d2: return 0x8c55;
    case 0x2f9d3: return 0x27ca8;
    case 0x2f9d4: return 0x8cab;
    case 0x2f9d5: return 0x8cc1;
    case 0x2f9d6: return 0x8d1b;
    case 0x2f9d7: return 0x8d77;
    case 0x2f9d8: return 0x27f2f;
    case 0x2f9d9: return 0x20804;
    case 0x2f9da: return 0x8dcb;
    case 0x2f9db: return 0x8dbc;
    case 0x2f9dc: return 0x8df0;
    case 0x2f9dd: return 0x208de;
    case 0x2f9de: return 0x8ed4;
    case 0x2f9df: return 0x8f38;
    case 0x2f9e0: return 0x285d2;
    case 0x2f9e1: return 0x285ed;
    case 0x2f9e2: return 0x9094;
    case 0x2f9e3: return 0x90f1;
    case 0x2f9e4: return 0x9111;
    case 0x2f9e5: return 0x2872e;
    case 0x2f9e6: return 0x911b;
    case 0x2f9e7: return 0x9238;
    case 0x2f9e8: return 0x92d7;
    case 0x2f9e9: return 0x92d8;
    case 0x2f9ea: return 0x927c;
    case 0x2f9eb: return 0x93f9;
    case 0x2f9ec: return 0x9415;
    case 0x2f9ed: return 0x28bfa;
    case 0x2f9ee: return 0x958b;
    case 0x2f9ef: return 0x4995;
    case 0x2f9f0: return 0x95b7;
    case 0x2f9f1: return 0x28d77;
    case 0x2f9f2: return 0x49e6;
    case 0x2f9f3: return 0x96c3;
    case 0x2f9f4: return 0x5db2;
    case 0x2f9f5: return 0x9723;
    case 0x2f9f6: return 0x29145;
    case 0x2f9f7: return 0x2921a;
    case 0x2f9f8: return 0x4a6e;
    case 0x2f9f9: return 0x4a76;
    case 0x2f9fa: return 0x97e0;
    case 0x2f9fb: return 0x2940a;
    case 0x2f9fc: return 0x4ab2;
    case 0x2f9fd: return 0x29496;
    case 0x2f9fe: return 0x980b;
    case 0x2f9ff: return 0x980b;
    case 0x2fa00: return 0x9829;
    case 0x2fa01: return 0x295b6;
    case 0x2fa02: return 0x98e2;
    case 0x2fa03: return 0x4b33;
    case 0x2fa04: return 0x9929;
    case 0x2fa05: return 0x99a7;
    case 0x2fa06: return 0x99c2;
    case 0x2fa07: return 0x99fe;
    case 0x2fa08: return 0x4bce;
    case 0x2fa09: return 0x29b30;
    case 0x2fa0a: return 0x9b12;
    case 0x2fa0b: return 0x9c40;
    case 0x2fa0c: return 0x9cfd;
    case 0x2fa0d: return 0x4cce;
    case 0x2fa0e: return 0x4ced;
    case 0x2fa0f: return 0x9d67;
    case 0x2fa10: return 0x2a0ce;
    case 0x2fa11: return 0x4cf8;
    case 0x2fa12: return 0x2a105;
    case 0x2fa13: return 0x2a20e;
    case 0x2fa14: return 0x2a291;
    case 0x2fa15: return 0x9ebb;
    case 0x2fa16: return 0x4d56;
    case 0x2fa17: return 0x9ef9;
    case 0x2fa18: return 0x9efe;
    case 0x2fa19: return 0x9f05;
    case 0x2fa1a: return 0x9f0f;
    case 0x2fa1b: return 0x9f16;
    case 0x2fa1c: return 0x9f3b;
    case 0x2fa1d: return 0x2a600;
    default: return codepoint;
    }
}
libmongocrypt-1.19.0/src/unicode/fold.c000066400000000000000000000144151521103432300177760ustar00rootroot00000000000000/**
 *    Copyright (C) 2025-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    .
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 *
 *    THIS IS A GENERATED FILE, DO NOT MODIFY.
 */

#include "fold.h"
#include "../mc-range-edge-generation-private.h" // For mc_count_leading_zeros_u64
#include "../mongocrypt-private.h"

static bool append_utf8_codepoint(bson_unichar_t codepoint, char **output_it, mongocrypt_status_t *status) {
    if (codepoint <= 0x7f /* max 1-byte codepoint */) {
        *(*output_it)++ = (char)codepoint;
    } else if (codepoint <= 0x7ff /* max 2-byte codepoint*/) {
        *(*output_it)++ = (char)((codepoint >> (6 * 1)) | 0xc0); // 2 leading 1s.
        *(*output_it)++ = (char)(((codepoint >> (6 * 0)) & 0x3f) | 0x80);
    } else if (codepoint <= 0xffff /* max 3-byte codepoint*/) {
        *(*output_it)++ = (char)((codepoint >> (6 * 2)) | 0xe0); // 3 leading 1s.
        *(*output_it)++ = (char)(((codepoint >> (6 * 1)) & 0x3f) | 0x80);
        *(*output_it)++ = (char)(((codepoint >> (6 * 0)) & 0x3f) | 0x80);
    } else {
        if (codepoint > 0x10FFFF) {
            CLIENT_ERR("append_utf8_codepoint: codepoint was out of range for UTF-8");
            return false;
        }
        *(*output_it)++ = (char)((codepoint >> (6 * 3)) | 0xf0); // 4 leading 1s.
        *(*output_it)++ = (char)(((codepoint >> (6 * 2)) & 0x3f) | 0x80);
        *(*output_it)++ = (char)(((codepoint >> (6 * 1)) & 0x3f) | 0x80);
        *(*output_it)++ = (char)(((codepoint >> (6 * 0)) & 0x3f) | 0x80);
    }
    return true;
}

// C translation of mongo::unicode::String::caseFoldAndStripDiacritics.
bool unicode_fold(const char *str,
                  size_t len,
                  unicode_fold_options_t options,
                  char **out_str,
                  size_t *out_len,
                  mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(str);
    BSON_ASSERT_PARAM(out_str);
    BSON_ASSERT_PARAM(out_len);
    BSON_ASSERT_PARAM(status);

    if (!(options & (kUnicodeFoldRemoveDiacritics | kUnicodeFoldToLower))) {
        CLIENT_ERR("unicode_fold: Either case or diacritic folding must be enabled");
        return false;
    }
    // Allocate space for possible growth. Folding characters may result in longer UTF-8 sequences.
    // 2x is an upper bound. With current fold maps, the largest growth is a 2-byte sequence mapping to a 3-byte
    // sequence.
    *out_str = bson_malloc(2 * len + 1);
    const char *input_it = str;
    const char *end_it = str + len;
    char *output_it = *out_str;
    while (input_it < end_it) {
        const uint8_t first_byte = (uint8_t)*input_it++;
        bson_unichar_t codepoint = 0;
        if (first_byte <= 0x7f) {
            // ASCII special case. Can use faster operations.
            if ((options & kUnicodeFoldToLower) && (first_byte >= 'A' && first_byte <= 'Z')) {
                codepoint = first_byte | 0x20; // Set the ascii lowercase bit on the character.
            } else {
                // ASCII has two pure diacritics that should be skipped, and no characters that
                // change when removing diacritics.
                if ((options & kUnicodeFoldRemoveDiacritics) && (first_byte == '^' || first_byte == '`')) {
                    continue;
                }
                codepoint = first_byte;
            }
        } else {
            // Multi-byte character
            size_t leading_ones = mc_count_leading_zeros_u64(~(((uint64_t)first_byte) << (64 - 8)));

            // Only checking enough to ensure that this code doesn't crash in the face of malformed
            // utf-8. We make no guarantees about what results will be returned in this case.
            if (!(leading_ones > 1 && leading_ones <= 4 && input_it + (leading_ones - 1) <= end_it)) {
                CLIENT_ERR("unicode_fold: Text contains invalid UTF-8");
                bson_free(*out_str);
                return false;
            }

            codepoint = (bson_unichar_t)(first_byte & (0xff >> leading_ones)); // mask off the size indicator.
            for (size_t sub_byte_index = 1; sub_byte_index < leading_ones; sub_byte_index++) {
                const uint8_t sub_byte = (uint8_t)*input_it++;
                codepoint <<= 6;
                codepoint |= sub_byte & 0x3f; // mask off continuation bits.
            }

            if (options & kUnicodeFoldToLower) {
                bson_unichar_t new_cp = unicode_codepoint_to_lower(codepoint);
                codepoint = new_cp;
            }

            if ((options & kUnicodeFoldRemoveDiacritics)) {
                codepoint = unicode_codepoint_remove_diacritics(codepoint);
                if (!codepoint) {
                    continue; // codepoint is a pure diacritic.
                }
            }
        }

        if (!append_utf8_codepoint(codepoint, &output_it, status)) {
            bson_free(*out_str);
            return false;
        }
    }

    // Null terminate
    *output_it = '\0';
    *out_len = (size_t)(output_it - *out_str);
    return true;
}
libmongocrypt-1.19.0/src/unicode/fold.h000066400000000000000000000047111521103432300200010ustar00rootroot00000000000000/**
 *    Copyright (C) 2025-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    .
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#ifndef UNICODE_FOLD_H
#define UNICODE_FOLD_H

#include "../mongocrypt-status-private.h"

#include 

#include 

bson_unichar_t unicode_codepoint_to_lower(bson_unichar_t codepoint);
bson_unichar_t unicode_codepoint_remove_diacritics(bson_unichar_t codepoint);

typedef enum {
    kUnicodeFoldNone = 0,
    kUnicodeFoldToLower = 1 << 0,
    kUnicodeFoldRemoveDiacritics = 1 << 1
} unicode_fold_options_t;

// Fold unicode string str of length len according to options. len should not include the null terminator, if it exists.
// Returns true if successful, and returns the null-terminated folded string and its byte length, excluding the null
// terminator, as out_str and out_len. On failure, returns false and sets status accordingly.
bool unicode_fold(const char *str,
                  size_t len,
                  unicode_fold_options_t options,
                  char **out_str,
                  size_t *out_len,
                  mongocrypt_status_t *status);

#endif
libmongocrypt-1.19.0/test/000077500000000000000000000000001521103432300154435ustar00rootroot00000000000000libmongocrypt-1.19.0/test/crypt_shared-stub.cpp000066400000000000000000000053611521103432300216160ustar00rootroot00000000000000/**
 * This file does not contain test cases.
 *
 * This file defines the minimum APIs for crypt_shared to be successfully loaded
 * (but not necessarily fully used) by libmongocrypt. This is only used to test
 * the loading and un-loading of the crypt_shared dynamic library file by
 * libmongocrypt.
 */

#define MONGO_CRYPT_SUPPORT_COMPILING

#include 

#include 

#include 
#include 

struct mongo_crypt_v1_status {};

typedef mongo_crypt_v1_status status_t;

status_t *mongo_crypt_v1_status_create(void) {
    return new status_t;
}

void mongo_crypt_v1_status_destroy(status_t *st) {
    delete st;
}

int mongo_crypt_v1_status_get_error(const status_t *) {
    return 0;
}

const char *mongo_crypt_v1_status_get_explanation(const status_t *) {
    return "nothing here";
}

int mongo_crypt_v1_status_get_code(const status_t *) {
    return 0;
}

struct mongo_crypt_v1_lib {};

typedef mongo_crypt_v1_lib lib_t;

lib_t *mongo_crypt_v1_lib_create(status_t *) {
    return new lib_t;
}

int mongo_crypt_v1_lib_destroy(lib_t *lib, status_t *) {
    delete lib;
    return MONGO_CRYPT_V1_SUCCESS;
}

uint64_t mongo_crypt_v1_get_version(void) {
#ifdef UINT64_C
    return UINT64_C(0x000600020001000);
#else
    return 0x000600020001000;
#endif
}

const char *mongo_crypt_v1_get_version_str(void) {
    return "stubbed-crypt_shared";
}

struct mongo_crypt_v1_query_analyzer {};

typedef mongo_crypt_v1_query_analyzer query_analyzer_t;

query_analyzer_t *mongo_crypt_v1_query_analyzer_create(lib_t *, status_t *) {
    return new query_analyzer_t;
}

void mongo_crypt_v1_query_analyzer_destroy(query_analyzer_t *qa) {
    delete qa;
}

uint8_t *mongo_crypt_v1_analyze_query(query_analyzer_t *qa,
                                      const uint8_t *doc_bson,
                                      const char *ns_str,
                                      uint32_t ns_len,
                                      uint32_t *bson_len_out,
                                      status_t *) {
    std::uint32_t doc_len;
    std::copy_n(doc_bson, sizeof doc_len, reinterpret_cast(&doc_len));
    doc_len = BSON_UINT32_FROM_LE(doc_len);
    bson_t *given = bson_new_from_data(doc_bson, doc_len);
    bson_t doc = BSON_INITIALIZER;
    BSON_APPEND_DOCUMENT(&doc, "originalCommand", given);
    bson_t tmp_doc;
    bson_init_from_json( //
        &tmp_doc,
        R"({
         "markedDocument": true
      })",
        -1,
        NULL);
    BSON_APPEND_DOCUMENT(&doc, "result", &tmp_doc);
    uint8_t *buf = new uint8_t[doc.len];
    std::copy_n(bson_get_data(&doc), doc.len, buf);
    *bson_len_out = doc.len;
    bson_destroy(given);
    bson_destroy(&tmp_doc);
    return buf;
}

void mongo_crypt_v1_bson_free(uint8_t *bson) {
    delete[] bson;
}
libmongocrypt-1.19.0/test/data/000077500000000000000000000000001521103432300163545ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/NIST-CAVP.cstructs000066400000000000000000003425031521103432300214630ustar00rootroot00000000000000{
    .testname = "NIST CAVP [L=32]: 0",
    .key = "6f35628d65813435534b5d67fbdb54cb33403d04e843103e6399f806cb5df95febbdd61236f33245",
    .input = "752cff52e4b90768558e5369e75d97c69643509a5e5904e0a386cbe4d0970ef73f918f675945a9aefe26daea27587e8dc909dd56fd0468805f834039b345f855cfe19c44b55af241fff3ffcd8045cd5c288e6c4e284c3720570b58e4d47b8feeedc52fd1401f698a209fccfa3b4c0d9a797b046a2759f82a54c41ccd7b5f592b",
    .expect = "05d1243e6465ed9620c9aec1c351a186"
},
{
    .testname = "NIST CAVP [L=32]: 1",
    .key = "17b52858e3e135be4440d7df0ca996f41ccb78b7d8cc1924d830fe81e0fd279c131ce3546303e95a",
    .input = "e0eff00f3c46e96c8d5bd181283e4605348e3fa10b47945de3dcc159ae86e7bd3fdb13f2ada2c313fce6a69efa49a470689b1ef05aab778ae15dd35fe6fd1e3a59d351c68cf8f0ffd968d7e78b57377afcc9dce3fa5db1f06f6985c4414c0fcc780030f49fef791a6c08edc2a311080c373f00e4b2044a79d82860f0871bc259",
    .expect = "c4061427764f979468ac422891dea9ca"
},
{
    .testname = "NIST CAVP [L=32]: 2",
    .key = "7c67410e0a9e3d7ae4f3d04eff1c2716891e821c6ec1dc822142ce8d9949b1449a1a033a350f0ba8",
    .input = "bfd166793abdcffbbd56df769150d1466c18a67af452c7e67f86ed741d163ebbd874b9d33a91d3671099620b6eddbbd0f31117164eb73ca201db59f1650131cbef5c7b1bb14089fd24da2919241fc9303c02def424ea861d88636bb90b13ebc38cf177f8a8b139e68082fa46bcfc428bd054c1bb7dd3ed7e9b86ed751736b6cc",
    .expect = "1a0d427e79a7bdca7b11579339d0ff77"
},
{
    .testname = "NIST CAVP [L=32]: 3",
    .key = "b2c450128d0744421c3f31fab37bbcdfb5a2ff2fb706d1f7e23c4886992c7d215c648ff8edb2eb59",
    .input = "f6989ebb07aadaeef970f0b5ceb806ecffe77cc20f3c221a6659a9315dff5881961900e68efc320075edafd83de320c6f18f0892489af6d97a2effb252b76b9284ebaf6d42089c1e0a5cd509c20b86ff060d5362c1768f89fafaaf65f1b0fe656b1692984a567e1260c7499085b79f5fe7684779a25855f291c5a192637177c4",
    .expect = "f0d7c63677033ada0b502a4e95b20e43"
},
{
    .testname = "NIST CAVP [L=32]: 4",
    .key = "a7744321d73938b8eea13754909029881bbd727439fe2731b1c67b7083eb7b5d33adfcca65f5d189",
    .input = "71299ca3daff2331082db370bdf8ceec227b71bdc49c3b14dc3fd213d3ba83e2058828ffc6414fd5a2c99891e9c85f316c5b9bdd810a067b4df97f7e4262acfee642e30ed6534b4a0b3b3eaf5d03f2b045ca5985e7bb45c7503cd03afc68fbea9bc09579141d5fb7cbea6d73208fcf913830715dff98401f6d708ef009b5b8cb",
    .expect = "f6302c5fd7c8495e233b5d6129f361da"
},
{
    .testname = "NIST CAVP [L=32]: 5",
    .key = "795a0ba9b02984cfce5e7395fb94d98fcf12ae5db8a06e239c9ad439bf42e523e65a31c3bdf356cd",
    .input = "8b4aa20de6c1f051d11ad50ba2e4fc4ff1ec478455f9b5b96fb9893d2afca969402044c101ccb73c50e2b2dfeeae9690fb64222ab9c94fcd943078785fa8bed9e174ab6390bb16a29c8146cb2fd65a98f44de752d6b0e42f0af2c3df4f65e162742d201c1bf5d22bbee1daf8efc30d0ce491df2632173b8ad9e9b29b819cd8ac",
    .expect = "fbecae19c2ce766d286c8ce70133b669"
},
{
    .testname = "NIST CAVP [L=32]: 6",
    .key = "aa41b5222efdea882cbebd11d343000ec2ff6b2f7bbfa746158ea54f32d534ae31c7d3b7a5fcc373",
    .input = "3274a0326682ba59d6c47db4164e3e9937bfad4199c6507101e5305aeb75d2bf22eb68558d59496f4c389fda04645f0676687f6757fc631b5bcc98cd947bc4d9fae8ddb14bb09a7f15f4270c105c1de0b25bb1abfeb52ce39d3f9baf2fe6c704e3f3670d458e95d158807f10e53d5f6d1221add336fa9211ecc7a1c767bfc286",
    .expect = "cec1ed7aa0f1cbd6b7f667a079a88577"
},
{
    .testname = "NIST CAVP [L=32]: 7",
    .key = "aaa449923f0cd3e6a7e74d9c56a7eb6a3b4c3dea97e6a8400e5517fcff54ee4211b640280eee415f",
    .input = "0486d2647e2cdf7bba36c8f3ff9e2941001c706eb1a44cbd582f638ee7be4482899c9ce07be4ac381d44fa4649004718e33ac273b1707b746d461a731986d12c93658f216908773aee4690af8eb0be275ecef122f7ac9c94859569d21b1f2bb24a6813eef19e28ca56c5f1f776b474b69a6165412b5f9766c7a5b6759491385c",
    .expect = "ae73b3740a7a8a07223635faaef0ba71"
},
{
    .testname = "NIST CAVP [L=32]: 8",
    .key = "6c13d74ed004ee92adb44b755be92e8440434704a1c22790b788f50406e0629aea80de53730b0d99",
    .input = "fd5cf72ee0779aab7daa27d5c8a8d31f4082ba47741e7e73c6e631806fbd7597c337e101b609a73ca0be744e3dac9859f827677069f4dfa91c008b739452a62a8f3f84e98cdd2ea08bba4d6614cd49107aacb1026100de457e36d3da9e78684eeadca88f69db77fec60478c554f12d6b4f7b60a6652ac27074efd35c9616012b",
    .expect = "4304f9864598f801c6aa1a692aabb8be"
},
{
    .testname = "NIST CAVP [L=32]: 9",
    .key = "12541d81c6958221c44a958ecd7f48c08a89a8687d306c2f3814c93ecd498e0485456c33d5fc950c",
    .input = "31f51d395a06885efc34032349bc635cd4b1004ceafcb1c426a2f88b4045790226eeb1084e09e41c4ab157c19d2ec027cdbcfb07b98efecf2d130fffb47835d3ad6eec22a12d1c86d4b94cbd1a64134fec94d071bbc69b2a84d37cb4a572da25efff364ffc7b19e4c3d34ade6965451d5bc0e95299ab711d556aa572bc3c5141",
    .expect = "edad94e7c30813be7c5ac58df418d8a8"
},
{
    .testname = "NIST CAVP [L=32]: 10",
    .key = "a1e8cf95c6d729507661fcc687156922c8975645e5f36eba8a3069eccb298e96c498767c7c741259",
    .input = "48bdae9d81f1beaccfd00374f522f90cfedd8e3dd93be13947104a89f75b9a48ee1ba48f2d64fc308eb1fea7f07c124d930c2fcfc58f9edfbf680129caca9389a686b17b2b219ad3312a73aeaeca8ea81e9deb4f28c0ffd87e2cb5110542b39736a6de49c45120fc7ee269717835f3846537cba548f98d8c4c036e29efea80da",
    .expect = "d78d7d266cf83add4355e7395b63adfd"
},
{
    .testname = "NIST CAVP [L=32]: 11",
    .key = "c7e5ede152c50a935e76b59979e08638a09cfffd01ac7008056a18ab8ebf8d347e955e06788ff6ef",
    .input = "1e1bdaa984ca68730faf61c697d5fb15955b28992d69bae86c68cbc9ce735c4703083c04f2042cd0ffce407a89d288e6b731f06075b66530b90d396f0b2fc91944215d6396de4f4ecc92707cd308a7427a66db00761813ada90adcb6a41aec096acd046c76401b140062b8737d61a0516562b11e38750e87c3c87c47a01b0c40",
    .expect = "b7de3be2fae6ab41aa6386b8460223c6"
},
{
    .testname = "NIST CAVP [L=32]: 12",
    .key = "6ab37be64f4b1e032c5a43dc03e4afb65c6ab1329fbca9c4c10fc766224f158eb6b7b85d649e7319",
    .input = "490700ea587a001c7162f0946f7ca6a5e3655c6e09ba4c13fa7e7d4e22bcdc27f56d8effde9b85d378c751bf018939c10c768bc0754630cd9a3783a8c8ac6486f41a8711ac2412b14d05680a752f3fc6bb31f9949ede3170bcac9426455af211aed69429aa5dd13d56e4dc7cb3b7e03a5a604ff16bca7786c7a656ce7f0eaf51",
    .expect = "380eaf65a9be83322508498748504b50"
},
{
    .testname = "NIST CAVP [L=32]: 13",
    .key = "785a1189381824a8131e885ba4b23c2e94e3dfdc03652cc32a9cc1963ff72452997f077315b0cb67",
    .input = "5bc93a655f35d346f9e96e96e9bb560178dad04ea46259917d2d30a2cfed14cd01774fcb3d62f3f1d2d164a8d68d161d0f57983a147cd2d4afa98b2686012e7efa6dcd36503366e60ecb65d8a8ee6bbc5cef4e9d5b4e6114298bf5bc46381fe50e52bc8dded1b38c787e7a0ea905dc46294bf961c2018eb9b47a764c59b9716c",
    .expect = "b452d180b9cacc10cb012f48dd19e4cd"
},
{
    .testname = "NIST CAVP [L=32]: 14",
    .key = "394575dded531000e776ae4adc64c4affb5b220ac5a96ebf1f72d19fa6aef00c42711e5dfe6fcf84",
    .input = "b733d51a7eaa4b6bb0e378a218caa6ae7475a3f32909184d34d7165264cbf2d8c60753b861cb89d12498204f1d95b52dec3109f8760a54d6de0edcc8b1dfc52c607c2b86f41f6e7ffd61cd2ecba43797e1b25d71a7a20c2d5ffcba335a1d5f6f6cdc860c9d6da37f2186a7c88bc1d2f43d42c8e72399e858a1e9d91dc94a65a9",
    .expect = "3f6417a99d7186bc36e6d0d61467360d"
},
{
    .testname = "NIST CAVP [L=32]: 15",
    .key = "14d45ca2a3d4977dab2b7d442c6f9e57ce348e0a6a808bb3cc7f6002b87789912afd98bce26ad8b3",
    .input = "0c294a318b7c1e884649fe54e4a87285e42f868e3d0a8519414e05f9c78b236089a11052cbd4cd593e22327b23d33569b35369f9bf3dc5d694b8a7762106184d5c5a5241e1ea805ddc46c4c92ae87efabb0ccc263bc24dfbf1412b90e77e589c4bfd17e615e7bffcea5ebb28400dd6a0c403b6fdf8c1a5ee2191982e601a69b3",
    .expect = "28f1b663213043c4d4fb312bd36d85fbe62c8008ce82aabc"
},
{
    .testname = "NIST CAVP [L=32]: 16",
    .key = "2a0466dd515d2f48fec5e78e22bb22c606b09e8184691c5177a46e8c70fed24dab147ebc41e97c8f",
    .input = "d60812433098c44623159153de7cd2721b349f685c43388a74c2a3d04a8e972ada4199177c61657369d78f907ba26a8934cc29d3029d4415c1101e3a8283e4c48bb2b8639fe60fc67f6a57b1b03fde507f10efcb43683e1ae223851b962370e1f144b74f1f9189e66cb831dc05bbf46e03e93877a50dec40dde5239a0fd5022a",
    .expect = "7c2e5f1fdbda3c153536ec7136091eba0ba525b950bfc84f"
},
{
    .testname = "NIST CAVP [L=32]: 17",
    .key = "3a4182af8c3914d1df57b6321fa5dec68748ad746e0369bb64fc2d9b7dc3dfb3ed9063a7d5cc0ec4",
    .input = "3db052695a599813309fae5cf5b19690d3e1e63b3caac1487ef10766978bc9b04a00008c728e7ed397712433bf6256d2865eac3471a8ea5f8011333d02777941ad8c384deed864d47e02a03c364bb086245b3130de40875a16b418296f9eb8698fdc63767640325c0ed8883d03738cf3d460ddf72b7981816a611ef186096c6e",
    .expect = "dd3334fabe8d0d51084c1e99a2a7fa8548c4cbbeec854fb4"
},
{
    .testname = "NIST CAVP [L=32]: 18",
    .key = "56e8ada1ebc8706b94f99bf2290365222f6619a7fc3161151cd0c566f4266faaa5dc31fa34f8c9ae",
    .input = "9ae4b799989bc132e5a50c4fce6d6e44e2940c6ba7dbb8248b447d191d7477c77d5ce83a111889177a171ee0c77d4d74e8c5b0d565ab292e504976157880050ddf99094f6e2ccdcae84148681db6f39360e1d7f83a75ea8a60aa9bcae398ac46a7e44060169f3551156bb36e37e005a9312ea85a8f03a240a5af15c2c786147b",
    .expect = "bddd77019ee3e2a16e65713089b23f0ef13e5f3ae6da5052"
},
{
    .testname = "NIST CAVP [L=32]: 19",
    .key = "1e6d00b386bbbfb7f44001c5915448a516954d7a2ae8f4e9eaba807dc98c034a9aae19d1eb4ad624",
    .input = "009f5e399430038250721be1796535ff21a609fdf9f0f61266e3af75d704317d5506f8065c487218e99eb4c3d4546c4d607016901138739dbdf437a5e6f5021a47d69211ad0237eb08768734c2c952cb4f69d94306273a8a2ff62fc85deff88afe99962030683a43d683fdfcebcad1c11718b8e080c53421e370fea6e3fbfa17",
    .expect = "7794f8fe7ace77512eb98a5459aaebe28ae1e8c62832b5d2"
},
{
    .testname = "NIST CAVP [L=32]: 20",
    .key = "e2127a48f615eeafb927ee53222f5004d11dd2d3a22e5377826b43f08174586a297b82630e932210",
    .input = "1dd28756d292e5a4f3537e88777933335a64f79a4d50257aac791799b083f450e61ac946dfd6dc7e29613d947fdb9d433d7d632b177dfdd1093274e8917944cf1d576a5abfe0bed528578346d4963df382b0c224e7d6942aa3776ea074ab1df1aad2911bdb7834b2d77d7b27de72ba4a11453c0e2721938c61902d4bc0e328bf",
    .expect = "d0119cf3ad1dd9e917ab325c0b85927819ed606084542944"
},
{
    .testname = "NIST CAVP [L=32]: 21",
    .key = "ee0a81a8bd52c9b1422083522d37f8071896ba625ffa22ad32a4fdd1e85c837796b6896ce194f74a",
    .input = "0c245de3b250c33282ea1a02d007f03b34ed427631283eb614db4d521f555136e7e42b4cfbee8134c63dbe3bb79b5a8b9f9f5b9f5ac61cfab1c54d197f1e3ba613f251eed616df952d691b88a16466343ef2d0f63882ddd2d55b8a6786308b2257f5d7b38af166bd7f1339d2d8899c9eda8fa86215850ba547450c267eb3c914",
    .expect = "335ee9a4c96bfcfc38c76f7ace6c84adfd0a57a94efc23b2"
},
{
    .testname = "NIST CAVP [L=32]: 22",
    .key = "d4254694ca38676404cc2cd6a444f61e230c188a9f92d4ad769287bc1397203808bfd6cd5dbe1b7b",
    .input = "d106a9aec442fed61629e77566f789b28c2c2c3ec628878a12f73d37da6ea7ced677d4b12fa9ce51e01c1fa2627b94cc885a4124a8cac55afb2bd0f34642e2faba8c55f319d19d111bfbcfa9102960e5c6002fbdad41c62339a1dd7e88d5205a45ec335ecce1f27e8f71fd72b82a746610c5fff31fb5124e95006fbfe84eec55",
    .expect = "5adf1391c94a60602cefe1bcc610060de90a4b7b8822db1b"
},
{
    .testname = "NIST CAVP [L=32]: 23",
    .key = "61b83d7ff9b82b32a89225eacd7c9c25807c8dbac8cf56610e88c875d2797df99d566bda3718ba73",
    .input = "96560a07f7e398fc739648ce9a924350fbf9b45239ae7c7f626026867dc41d7862211c71cf12e77bb78839afdd0efd9ea251c0ef1bdf6749672f1d7340e290b9cf485d92c526c881a7b6b13969f0c4043f08ef65b03819fcecbf11ab5f2ac4f786d2b4b102a6a5d5eb2a99b266c0ff4b7a2728fe1f41fa639819e877032422fa",
    .expect = "312cd3f6c27e3ece5ed08f1020c815277f7e98bc3bcd0248"
},
{
    .testname = "NIST CAVP [L=32]: 24",
    .key = "adf13d80eef135f3cbfe63ac19e8679b98c01dfd263d72db335e76d47551b31ddd94bec6c95a0b3f",
    .input = "81b8de7e17cc5ffdce4f2213b561d67d244ea591aab5c37f47e946d7db97384bdfa9eab7536b8c5ef7ecfb76bea8dae88063e451ef58804ccc9396f35b9ca2a3145507009b25a539f256ad8eeebcb40fe79807a6b4bb3f57d6ef15c7f49277fb8884db63d744d3172655e1602be78d7ac2b3b698e1272629cec3695a8fc3dedc",
    .expect = "a80b1a06ed13f5579a785f7965ab180908a07f152ea81e2e"
},
{
    .testname = "NIST CAVP [L=32]: 25",
    .key = "f870e26dd47b20d386f63d12458c46d795fe0790bdc81d2e7c025329f8842bc5f74dba955126b93d",
    .input = "f4d6aedd9a34e0a1822362714d4e81794b53b266417678c16a97887bbb612cc96bc5e532b3a654e5d3d65a5155427ff09569906381138cc49e3fc2384c5d33c34abd3d617c487b52ec6ee7b5105f41584b7eb5cfb512b8c31f3f338d5236e30398a8ff927e801c8ed7d14fc5040d915a737967d166ddc266f68023a357530431",
    .expect = "68934f2d0de64c4e4eede0b1d867630da790c111371458d5"
},
{
    .testname = "NIST CAVP [L=32]: 26",
    .key = "cd4f85a044eaf7c5a9850d0d708f0905049dc27718679a8f3713af3ca3b756d95c19c50d7fb90ff0",
    .input = "bbf96d794a6a062fed76429a8b395e5664c6b1b0a26bdf083137507ad1bae0bd6a0cd84a9f111ec1a5faa889560f36b781ac4132858a2e141e40c8537e0aeda0a0c8878fd94abff9b0ca6d9fefbad20ffac189cc6000bba9b09993768e72f1de053663901f9d519db3ee77217fc29826760a71c55b53ed8e8f49972b287a543f",
    .expect = "de9a7e21d30725d253fc4d09a3fd21530d788795d672c057"
},
{
    .testname = "NIST CAVP [L=32]: 27",
    .key = "e6e97a286f575855cec8a0f4d06327929d41f81d3fdaf9f65ebdcc474d85f4974b08399c02d14d50",
    .input = "99140d978b2e37f32684f3bf075c4678fe4b3a95fc93df7532af9096772b7707eab95420d9827970e2ba19f75877c395e9c32ac37def2781602b018fa454ebe0c10dce4c7f11498516c8f74c9318f0e57d7d92c8b95c8199ab94ec5a9e5712e0663805834384ae1a09d612277ee6d34e04a2fa0c7880f3a55912d95e2ddbf5ed",
    .expect = "61a0693f740c3b121238cc904e98c671563d506780960a00"
},
{
    .testname = "NIST CAVP [L=32]: 28",
    .key = "d763c6360763561ed2bf47749080549b6e2db87514e1ee1c85a0bbd346eb6e3cc29267cbedcad67a",
    .input = "41677677d9b19e249d4488c3eb18153d5b705002ea6aae4258d59560ce421aa4c45e0f30227f3d35a57cee6685c2afad55a4531d2af33b29ffcfd51358bc63a726f9fe28eb0dda8b1ea2cbe3d196081d915030ed8e508a08fc0a9194b8f5b0dc2fdf4a497c83fd8ed05d282217bdaaf3d81bed595daa2448152fd0cb361489ad",
    .expect = "014d599f9490a22b69824f8cce92f30c0542cea92b621a10"
},
{
    .testname = "NIST CAVP [L=32]: 29",
    .key = "a4b540971d9bdb20b47e8282cac841a86fd94fff27b4eecfeef893cb7b1347e7c2b24d69bc7b0543",
    .input = "50ee2389b8b70182548ccd7e82de8496c6b3602bc99efc7ca2efba77552762d099af0b51dfc93f718fc65a27957a33001cedfe70995371650c3e26228313414bdfba523cda9a7d9f49c5d83e9f6f1415b3a560acc33c8aa4b807678fab4d7605a979c0f4b314023709f10e6aa9a76ffd12444c884d408f5e2eb04565d8bc4825",
    .expect = "431d287099550ba9e523dd1308b0514cdc5faddb04ebc4c1"
},
{
    .testname = "NIST CAVP [L=32]: 30",
    .key = "9779d9120642797f1747025d5b22b7ac607cab08e1758f2f3a46c8be1e25c53b8c6a8f58ffefa176",
    .input = "b1689c2591eaf3c9e66070f8a77954ffb81749f1b00346f9dfe0b2ee905dcc288baf4a92de3f4001dd9f44c468c3d07d6c6ee82faceafc97c2fc0fc0601719d2dcd0aa2aec92d1b0ae933c65eb06a03c9c935c2bad0459810241347ab87e9f11adb30415424c6c7f5f22a003b8ab8de54f6ded0e3ab9245fa79568451dfa258e",
    .expect = "769f00d3e6a6cc1fb426a14a4f76c6462e6149726e0dee0ec0cf97a16605ac8b"
},
{
    .testname = "NIST CAVP [L=32]: 31",
    .key = "09675f2dcc4783b599f18fb765583668a0fd8ae4096f6fcdc60d4f35b4130fbefcd542ffe7459d2a",
    .input = "0cf2198c31376f5c8915660137725f2bbc180a986e5a7bda27fa81593a4a339bab92cbc39fb2b8581108ee48c794812d845a72ce8008c9e915d9e330bbb90e9136aa53ba0e6693dd4046d6b03362dfb9edfa04c887153cc5de677aab8c7839d517035879679c29727e96c5426324a2575fbe678d6cc7fef5eb6cebd595cfddef",
    .expect = "6b142d4dfe217f1881aa0e6483b271dd5d43f70b85605953a0fef272ddde46ca"
},
{
    .testname = "NIST CAVP [L=32]: 32",
    .key = "cfd4a44910c9e567507abb6cede4fe601a7a2765c9755aa2cf6ba4814223811a26a8a1ef499cebd9",
    .input = "3fb301cb4092f9623aa5ffd690d22d65d56e5a1c330b9c4a0d910c34e391c90a76d5401a2d3caa44b8c5d5aef3e928b90d2ee233e9f9a2cec4a32cd019d06a0dc1fcb1125f5746a4fbd32169ed7bf0e4fd065fa7c8ac97c366380484495f5c5b6850dd1c9d8cd6694cf8686e46308ed0ed1f5bdf98cd831339771db63de5a7de",
    .expect = "20153bf8ea2953c48251ebcc4161f8b6e28499e5c76c24014cff4a9e2f62d25c"
},
{
    .testname = "NIST CAVP [L=32]: 33",
    .key = "5448998f9d8f98534addf0c8ba631c496bf8a8006cbb46ad15fa1fa2f55367120c19348c3afa90c3",
    .input = "1c4396f7b7f9228e832a13692002ba2aff439dcb7fddbfd456c022d133ee8903a2d482562fdaa493ce3916d77a0c51441dab26f6b0340238a36a71f87fc3e179cabca9482b704971ce69f3f20ab64b70413d6c2908532b2a888a9fc224cae1365da410b6f2e298904b63b4a41726321835a4774dd063c211cfc8b5166c2d11a2",
    .expect = "7e8cba9dd9f06ebdd7f92e0f1a67c7f4df52693c212bdd84f67370b351533c6c"
},
{
    .testname = "NIST CAVP [L=32]: 34",
    .key = "9da0c114682f82c1d1e9b54430580b9c569489ca16b92ee10498d55d7cad5db5e652063439311e04",
    .input = "4953408be3ddde42521eb625a37af0d2cf9ed184f5b627e5e7e0e824e8e11648b418e5c4c1b0204bc519c9e578b800439bdd254f39f641082d03a28de44ac677644c7b6c8df743f29f1dfd80fd25c2db31010ea02f60201cde24a364d4168da261d848aed01c10dee9149c1ebb29004398f0d29c605a8bca032b31d241ad3371",
    .expect = "cdeacfcebf46cc9d7e4d4175e5d8d267c23a64cde83e867e5001ecf26fbd30d2"
},
{
    .testname = "NIST CAVP [L=32]: 35",
    .key = "aaafd08fd89bebe239ab65bb190b86d49c5d39faa50b1109f7dc8b179bc693f0810449c36a68041a",
    .input = "44131187c07a8e3979254b0c1d1cfa8081f0beb8890633744932af3f6987c7eace6e153876f639dba46b1e9f3e2a7fe673b3a954a00082cb7516ca9a54d9a1f1f924499960192ee1e3b623dca4a9efc92a6608d34f769efb5912db5267f06a6b0f5d3610458c74347e2ee32916425213ef2f649d5c1090ea3d4f6bcf6b752a3f",
    .expect = "0c19ab5d4ee7b64396eff7b2ca9efa5ca7369c1a1ed14952445d2fb5ece9473a"
},
{
    .testname = "NIST CAVP [L=32]: 36",
    .key = "b06f7ca7a5dd8baf2ca940811edad87a33da666dc427bcf4d54a8e03520dd5c399e9729d39be1494",
    .input = "32b45fbcbaf262bbe347360bd6076c43dc26ba9573fcabaea14595de886ccc793b09157dd0a85d74b6ccab9c49335446a45c6e7cb64786e6997c96ef1e4e3123ad6101db4c6a731dfd36b1be4deed1c92a994b25f5e2b171d81b9a335a83e03230c40b2056c00c7c5f8d2fb70abe4b9615e53bd756569217072d8bf362923f6e",
    .expect = "a9c9d3993fe7ec4c2033ccf3b73b3407cd999d67455b43a75d6ba97efda3be63"
},
{
    .testname = "NIST CAVP [L=32]: 37",
    .key = "2dff35c2fe5039123d4c5d9feb7d5167e3e959b31841abec1e5b18b0ece2ef25e04d1f8d030d9b1b",
    .input = "14890f3b2ee63746c8249909013571a403eb54273760090db5959b06ff59acfaee6d0c4aece58b5964d10b4b771dd90cf1b63d947bee4f6a12220d67b79aabbd68b02a3850352cc33b10072d4c28182df2855aa418b236239c659dad036155be6b9c908bc09dc38c3329b538e81ed710ef9fd3de7671673f3da5745f4a785204",
    .expect = "468d8498d46afe74a0ffb541b847bac724faeabd48c41322bf534b284c4e9fe0"
},
{
    .testname = "NIST CAVP [L=32]: 38",
    .key = "9794cf76aeef22963fa40a09a86bf0e2ba9f54f30f43bff09d44f9d28cfd7b7a45002797cc1437c9",
    .input = "3e8a9030eae1bb6084cffdb577623c4cf94b7aee3d3ca994ea94c12acd3e1194cad6d2ef190e0219af517073f9a613e5d0d69f23aad15a2f0d4e2c204ab2f621673325bc5d3d875984145d014bbcb1682c16ea2bdf4b9d56ce6da629ca5c781cfce7b1201e34f228eb62ede8d36cbfdcf451818d46721910153b56cfb5053d8c",
    .expect = "29973999c4ec891154b83ebe5b0201cf29205d68e7be2c1d59bbc81658d6668e"
},
{
    .testname = "NIST CAVP [L=32]: 39",
    .key = "c1d60814376aae39c4111246353485958f95558fa38ffc14e4a0981d76249b9f8763c4b3e2ce4ef5",
    .input = "97d29ac5ede94c0a5071e0095e6102123d1726132f9dc102672ab87b1cec18abdb04096c21d3fdb129742d250389460fe63b5f79c77c2f912a8f7d4f39cbd758139c872366cac35a40fe248322825adf57481d92832e66057f80e08964be993de6a0fe31e45806cb3c17ad6ae4d2a44a374647a88c3acf260d04c970c74ec720",
    .expect = "50db0ecb5b31524a6914264930abccae0da07f01a2bbb9408207156f8e8a340c"
},
{
    .testname = "NIST CAVP [L=32]: 40",
    .key = "ca5f3eb9308604f9fcc2af1c6a3175cd8a75045593b473bd7ae37933c345ddb0982e2dd7180db31f",
    .input = "8734e49e3e629deb352c77f58ff4dcce2af3b1182e7d896ae68619f6cf66ed69efd95913684ab1484d51bc06b47a67d70d48b7f9b27901bdbf8c5d2d238158f1f7e0e9740ffca742cf7938b5400c0dd063824c6bc6040e905499cb2671ec12cc47507e085a01e5a163acd2495b32367fd6aa5ab492a518ad50b54b28e23084c2",
    .expect = "a5772a3da86365b46638f1e97037fc0d8351d2e19ed929f85448ebf4e8379a8e"
},
{
    .testname = "NIST CAVP [L=32]: 41",
    .key = "808d7aa9aba6a40d1bc43e9b932ec8e9273b892ffc0a769e4f7255f3b83c224bb090b23952ae9616",
    .input = "61c5be972faa61f67bcb332542c0b8a7c74ef67cdb95d6f65c8acec8fca8bd6043e31677d8de41e6fc5d3ebb57fd8c8cf723490b96329adb1b014da2648cbd6043e9f6ffc67e1a2bbc72046374612a50c854c8565af03b6a1eedaa2319caec1368bfa65783f4b46dc3f0cb4622545c9c43c9bb86b237804a6c382e72a2cc1222",
    .expect = "5f1b8de0e3b07da6f9ce1a494be5712e54ac16080bb4f6d5373620d86d5ea5c7"
},
{
    .testname = "NIST CAVP [L=32]: 42",
    .key = "d8b994bb8df02d7803ca2e09d601b918d6b5bde90b611bebf70e078d1ac7b152bc4c2528e60b70f6",
    .input = "b31d11cb4f5c572ccf3405c65cbd218ee8abdc08b6c82e5d1da2baaf8980f7a9c29b915a718b0d43e000adae01b29342b29b28d53f63bf81281c76fa252f5d1e6896dbce224c4dfd4802ef0697140043d6bb21db5b84ffdbd001318937be64f52c76b5d06a875e8191a4957627cab1b8dc758fc3121334949cb9b303c6155153",
    .expect = "8e44d685fa79395b4761cab89688e37509e69ad007a2794c8c0b4152b67036ea"
},
{
    .testname = "NIST CAVP [L=32]: 43",
    .key = "a89bbaa86a339951ddcd37799e21b5d1688e4abedbc72daf7cc9b5adfe10be34c00a504196cc7bac",
    .input = "3ad17308cd259688d5b52c32d01a3b868bfaa4758bdaa5ceac34a1f908ca24e71a39224924d17f00cda4d4d50fdd716b50549e71cf5f271c42ea17d5becac32fd64e0a1b0717dc5f542af9442d44fb8f956e97b384d020458aca4cb0b6413b2ab637b5e73f9fb48cb06f22e6f2f6e3dca27016a272d89830ccfdcaf3b9d895c2",
    .expect = "905d55da5d290d023f6940fcb904c50e70181c95000eb1e6a33aa01077692736"
},
{
    .testname = "NIST CAVP [L=32]: 44",
    .key = "a9560fd61746d7f986b691f070c920256a535c21a64ab5a2bd771aeeab7119681bcc4761e68ee230",
    .input = "46eb5059055d3345c1ea84a4ebd2d7cc53361707eccd70e7cfd86bda83585bfe7c7ef937e1634b7e93f9ca7c6a42c357c2bffecc362c9e7eab6a488d91bd876b65376feb7a74819bfa88cf542736610fe763d6fa80c94ecca0f08855a05a485909fefc9e58f99e44fe7fdc55ab17779dcc08e9bc530e4a79b65274593a996671",
    .expect = "9045dd3fa6e8f2ef7c57b03932d244186caa1bc1d4b694c47e1f2901d9eba193"
},
{
    .testname = "NIST CAVP [L=32]: 45",
    .key = "f987eb83a3fd6d94ebf3626b7d34fec23ee06c63dfb4078cb38bcc97bd250fda0e286ecd4e64046a985bdfda8b",
    .input = "390a9dc2ea20221c5993c581892eb4b04364294fad919c451e83376531398a4c18ea808c334a910ae1083aa4979baa172f3ebf20823930e238630c88dfe5632b3b4042f6dd92e588f71529996fe840e13212a835cbc45ef434de4fa1ecb50fd14913cd481080875f43c07aa93a9dddd5f5e7ced6b1b88d42b9fce8f87f31f606",
    .expect = "0b3b220ee7a4fdcb0d17a5c8b595b981"
},
{
    .testname = "NIST CAVP [L=32]: 46",
    .key = "ef257132b7be124ea0886d587765e8e70357959cf39ebf621420c3f3c70e219fb3c5d349b7f2deb222fa26fa27",
    .input = "f90768954cdcbd5705f9d318fca6591787af840a921fbd06f24b979ef612034f3f64c71cd2012c756c83f75d169f9bccf8a8ad52725498fe69c3927edfbdcf87c73cf478172ace3a1e6b446a181e8aba00209894a5d2db01001d2acac5b3fbdd3897d7f142df0b6dc4b9a1862bac8ea845202d185321ecd75f6046c9cf7af116",
    .expect = "a17d0e0f021184a3937222de81be627c"
},
{
    .testname = "NIST CAVP [L=32]: 47",
    .key = "2cb8e269726b75e3a6258541251f6e3c5184c5e6878decea51eae315dc656115acc224818ee9851ace474f51ab",
    .input = "c1d80128fa208ba18bbb13424012ea651ee75e73f796e94c3b9aa9e911521040a605dd67c5254bfda9d088c60f9c68958f945b6f2b7e9ded2960ace21e42ff3e4c34f5322d930c955089538764d3225493c7089b119505aff4cdf93d46215d2f586d31d15af4353229ec5cce683e7e69d2874d3ece628a5944e97942b07992db",
    .expect = "da4571749322008e73dd436a13c5f11d"
},
{
    .testname = "NIST CAVP [L=32]: 48",
    .key = "1eea906ca11432655750a4e1af21eb1e03465c6d6f3b0fd8e20391077525d965fcf57d7edb1426ab1c3a42f2be",
    .input = "f57ea84caaa2af18dd7efdca356b9625f9e70d3a803a9d31e95976460c0a5512af49570cfeea0f4f3581d69ea07f62a5c59d9b81e07ea9838f8f5231cf33838e271d2c9c23fc511e045e5fa2b6cebcbf0240a19c05b02cb1e105b1d2b23b5269c4c1cf0303209f0eb2de3fe060a2cafc1898ca91d9174d4445823c2f9d6ce92a",
    .expect = "20cccc1ea0a8a89b3bc5fe3d5a9c2b24"
},
{
    .testname = "NIST CAVP [L=32]: 49",
    .key = "b2f1adfbbde4dd9a9674166ee08c2f4341072475b9b80b1032ad4a3658b408c1aa1fe12ad1c5deaa3149a49ebf",
    .input = "33ca6eb7ec1091b406cf64495ccfa2169f47b3b590477d4073537c14c05015d51ba527b3869ae4ebd603df906323658b04cb11e13bc29b34ac69f18dd49f8958f7e3f5b05ab8b8ddb34e581bde5eb49dd15698d2d2b68fe7e8baf88d8f395cfcafcdff38cf34b59386f6f77333483655ee316f12bfeb00610d8cba9e59e637ca",
    .expect = "5eeec5bd9583ce715d613d4c04a702f9"
},
{
    .testname = "NIST CAVP [L=32]: 50",
    .key = "a2617206e2b382078fddb0af3743a69a5a7484eecfff6cd96288443bc21ab79f9bbf7d70ff4edd6a0a85704ec6",
    .input = "74c4ca4db1aa812b4d75852c6717146351e83299448ff84d52262ff99d991d97c74f9f64a90d78e44817e926049882491343373f2e3bb6d18a30f8e30acb16fab34d5ffb6073a736b79ce1a25b2df16a6335bba90c4d8072aac36a14e5f7659c2104319b3ea3b529824d9729d3a009cf2a04e660448efd399b25ad1394e3b285",
    .expect = "64d5ad7697a29529ca3ca4ff65e7d735"
},
{
    .testname = "NIST CAVP [L=32]: 51",
    .key = "7af197b78a27038b0cec128001ce6bb7dc02c0258956f62ead678676301423f4f9329d48f881054e6adf12f358",
    .input = "68bb5b6289907589f8d91e46d44417ea80bf6be10245f52ba9f82211f371f810ad54571a5c277ffedc64d32447ccdd7d19ff91ba914ad6bc5ac0424c6a8c250d2b85caaed803f9642af1c098352474dd8cebf224ace82a33981edf53c04aa84927773b88c5cdeaa52baa6e0b65f4e4f024ad15881dc7fa78ac3a808dbd5588ae",
    .expect = "c4fdcba979357f639cc6d89e7970943a"
},
{
    .testname = "NIST CAVP [L=32]: 52",
    .key = "96ab1d64acad8cf69651c13e4eb42d7382e38019f3a927771ba6134c12a1bdbeb2206793fa35a4a3b09a1a8d4a",
    .input = "900e4152131d8c4dcc38a9e8647234dffc7ce88ecbbb65a8089d302c0a2efc95aee62852f9c58875fea368af02c1ce7cdfa3009ba62246c188bdf18ef7309cc00848b2a71cf531d9bfa1ad26d0c097cee3a8bff2e3a31849fc43bb14b7f62f5467dae83ac5d30ddfd7da7f351698163ecf332e7bca6862a82ada97a694a93db9",
    .expect = "ea411f749902bb0d2fa36e07e694da8c"
},
{
    .testname = "NIST CAVP [L=32]: 53",
    .key = "582c13a6c4d497e4edf69bde35beaababa1b068ed168af20b04cc2f06adf0478210ebfb27640cddb453af27790",
    .input = "7159ecc145a3f919044c851a4eca428279626e68cd8fa4c5f4a7f932acbc44f3bfc0bd3535edca94c86415e09815e22120dea0d869f7bd887d8dbf751fad91acb9641a43962514e2516a1c838e9e0575e73b72a72a30a423c18590d97141359e488c2c74d011810c89a6c189962f5487b7bf0d5c7701009da7d794e50a40d9d1",
    .expect = "7a699c1ce4e323fe1b9ff6dea2038aa8"
},
{
    .testname = "NIST CAVP [L=32]: 54",
    .key = "baf1d8aa12f5ea6264d122938593a8d677c82a37ebed7b43042680625e334c674f9f8a666c3a1bc54fca019698",
    .input = "939bfaab9f60369542928b1490894259c22706747f0c48215b08e1e59ed6f95a460728c74f3cdcf43198fb3dab75c9e4bf560bacfe1d6da3057f213f48b4c9ac0e739765bd1db2025839dc50462053a755f9f478fee8a626eb83f617b686ff0af4c78dab726c8264be5b7877e9f2a74a8cf9090109d4bd5213fdaa9571b2641b",
    .expect = "e2a380effe8de7d29948c5d9d7bb39a9"
},
{
    .testname = "NIST CAVP [L=32]: 55",
    .key = "735d943cc93f783050c7ccb09acc5a6f60af4efbc8919793e7c39038857ee00621d59fc535e7babcbc5998c5f0",
    .input = "29ba205089b12e8be5b422faf99c3d69aaca324eeb732db8e13c148245070dcc0b0c40ab412bde2039806247ea3917d194a4dab4a38c2121d6c63cb7a007dbf6cff9d1f66b8d1759e192147e60871bf784ad363e326122a3c3a99a89640dd9d2bca85a98d07ee21e2410c006232e53c4c10dce525f993825ef0cb76158c00d49",
    .expect = "54e871ae687626fee5669ce20cc48041"
},
{
    .testname = "NIST CAVP [L=32]: 56",
    .key = "c782597141b52135e34d240df67b9bdc274f2d41e6866e0f0da3a6fec241d3a09ea7f1960f9d7803fa7e2741a5",
    .input = "f7321718bbd3b401fb5d72f2e8931a5ebb18d2a1ecd4f189a59912157607687c4aad51719a702da6e031708f4faaf668c1999779f121fc99ea6db0f1bf967a027dc7ebea5e9f33e23fd6390c5424ea6c1b5ed0338ee3e7449d36adf1dbec790578c90d086f266ebe0095f4f161c89d70b1afa6582de15d92a63d319d33d10b8e",
    .expect = "eb5b96d2f51d56464b95da4927ec5a64"
},
{
    .testname = "NIST CAVP [L=32]: 57",
    .key = "498584e364f632184bf26a253d0e81e146730963b785eac1d5c2b51dceec34e3f16a464c1dece9277a4e99d868",
    .input = "cf25d619fb46bfbc39557914dda02d767ac511120d173b787743b35b3134cb943b33b36955534810720c2d6f6a261d26efd87fcfc2323b8426b8cda2965098cdb35e7c35802daa17d191b78601caf06be4aceecbfcfd6a48f01f52eb39ee1b201fec5a02e49c8ed93f2b40e10c554f4e4187858c24416dcbbbbf69bb84d8ff94",
    .expect = "020d5aca34d8c7066ef5d8c9b3429669"
},
{
    .testname = "NIST CAVP [L=32]: 58",
    .key = "e4298464a0457dcf98ef09cc00d92238d06d9a7574b46769c5773ec939a4639756f2bfe96dc833ed845c2c2a94",
    .input = "e2a26ca137027066af856453d2a4adc4d5d0c9d5bf068f8acaa4b74d0c7b9c9e562541065d98924c17fcedec68bae1c5fed636127a7e2d9bd0e3082df047cd47a6574816bebc4fa36ded4a4cec47f271665f586f149729d2a7ef31c6e61e1fcf98e288baa4942ed477ff8159a672662fd41438d4d7780c9616713a023528199e",
    .expect = "0d700ca9ffc418b29fc8e316acbc1abb"
},
{
    .testname = "NIST CAVP [L=32]: 59",
    .key = "28ae9e327911b76898af1fa0de56069e0d8b67bd2813828f87b88dc42a49a74d4ee30dc13e6f90ff6c6c4715c0",
    .input = "3b9a4948d67dc894d70c9ec37104a7147e22bcccb98983c22d648b21edcc986a06ec3bb8b263a648cee9bf388e36738f70204d7e6e0347e67865e01921da6ee59926b6cfdba2ba9c27e1d216b392fe0c9ea87b9b25b994ac19a4bbbe9077d8e6dc90e113b902ab97ca3a00e347e2f192f0056daa4574131ef8694597a36b7e73",
    .expect = "6696e3812da4807f05b84a29ad9143ae"
},
{
    .testname = "NIST CAVP [L=32]: 60",
    .key = "9117cf3ce9f5c6e19752bf0b1cf86a78ce3adbba87dae1399a2a937b0b722ba3ff92183871e84e282774e10de4",
    .input = "935a3c27249dcf92aedac8dc76d22ff7742e5cee57711778c92afdcdf36e26b8448504ee6ee48e9eb25b9e495e9098d494ac4ddc4c541f499cdb652638b611b0353090ac125ff1fef8564a78419c57f038dd65951fe06e8377b986947b407579eec1a60a16f540db0931921027deb472e8296bc2d8fb4e4ddf2c27c0c6f49c3e",
    .expect = "4cd095ce641f217f8b5f355152eed00b1d9fd721a08dc5a0"
},
{
    .testname = "NIST CAVP [L=32]: 61",
    .key = "363b32accfa593e454cc3ec83b9d775a0dd027b017ca2ff863c1fcb9e6215b5cfb2e8fea10eba2179f3bf88061",
    .input = "548564e5b7370426d575bbe8175b48c244dedcef3daf7252ec625fb777d02a5cb9ba9db0f2af1c5abd2f367d43107a3aaf218c77e20e78df6783452aa994ce9f635dcdd759e539c34649d2f11516fa0a53f6c6a0e58f5526f6a86040348d133e3cb51be252a3016a560ab6caf3346f3a1aa4b2f0affbb12f8218d8808083a240",
    .expect = "646abbd426255d2e369b7ac9eb3c3af19c7185ecd28bd82c"
},
{
    .testname = "NIST CAVP [L=32]: 62",
    .key = "134a50abffc94d8540d7ec939b7a28b10916e505ad90843d08b4b51770d48c27beb2d8d548a1b0a50fe64ebb39",
    .input = "dd802635f714060381d2ee1dfb50f2daacc637598965fa7158ead3eb15723bef95904dbd699dc99e054f5e19228d29696082792f30f1d565f1c8409359f7bb4517820cbcb6d5bee4c5596986354433bf02b597b1160065786a460a5f6e4a1254ab7feb9aa666ecbe081695ccfd1c19c2da861945023bb3930a8ebbb91b124806",
    .expect = "3d731839c004ecef8ab60fafd811d0bbe6e306f7cc802bdd"
},
{
    .testname = "NIST CAVP [L=32]: 63",
    .key = "c83ead9a131a1d7d126b88642221ece7d3a6ddd6016ecc6f40d089d47e1407bce3cd6068fc6918d91906a640f3",
    .input = "e80a112713b2e0aafddfdb71c091141719e1501c1ce55ee526d4a804146a08bab28eddba76335d306f7c2d0278232f56b11b9b543074512df3806d5c19341c2c52d0af7a95c3eebc11c8af426556a7bc13377ffd32762afe647f77260882e2c8b118b0eed6293b55cb0d8ab8eff12451287d269e8cb49461611bedea481d0298",
    .expect = "0a4f17a280f9017f1435cb8a11738fda4f14e3f222f06b86"
},
{
    .testname = "NIST CAVP [L=32]: 64",
    .key = "430a7dbd62b3b3cb6a4b2024bd796048ea60990d8222f94228a26093e88f59acca9e4fa2a616fe8e3992277b79",
    .input = "7e5d6e5e9491a965968a08adcbfbbdb19949f00903f7618270624e74aeae975036002079b2ed7755bc33b7a3e9a7ac0f066f3703a171f4c1cc0b1baf1d05a4f1f9c4af3d12c022eb2f38944c2c246a3d416b3ffc87568a3ab7447a7135a025774e11e254bef0f35176ff68519c583f64d2a3d09abb8c6915bb753562ff67620a",
    .expect = "5007afb09312d144091f2b35618c26714bab8784d8be35b8"
},
{
    .testname = "NIST CAVP [L=32]: 65",
    .key = "4953408be3ddde42521eb625a37af0d2cf9ed184f5b627e5e7e0e824e8e11648b418e5c4c1b0204bc519c9e578",
    .input = "fc0624c9d2fb237707df2c7bd9090b031329835432d99304c575f8691a2df35116584cf3650b9726d4ebb6d1fa3f9fa31e4a600455d7604beb15e73104a5e08583f2de222bc15e1f04094c450104c8c6df86292b508e428f591ae50bf940a6710b7be13d6d43ffc862e0f4bf357f0cd42086e8b36b25c338d82dfbdf3f26cc7c",
    .expect = "08c4699d15dcaef9e99556ece73793e006c86d25c8be3fc7"
},
{
    .testname = "NIST CAVP [L=32]: 66",
    .key = "da6d09682610d23a666ab7f63147a1f05db8b3cfc2c12de3415290b9067803ec09d5f53ddb4e04e69f031d2c56",
    .input = "e35dc1d0e414ae0e586ebec9a44c1918d795db378a89177d0b521c8ebadcf6d2b2e73826ac5bf9d121db1db9af9cd6d7be7869e8633e3665854df3b63e6138a383ac400b0829eed85e2d0e325e3fdef3cb29cc5b334f82061640201a4b8bc8c59ed460e7be26930b578b199c7bda395646d18cfac263034608532b24a802b022",
    .expect = "66a57a169d8d0ba263dd954b342919f4622592eed20c1981"
},
{
    .testname = "NIST CAVP [L=32]: 67",
    .key = "22f6c7ddb0e46ecf627aebd9ffad6f36682ef5c98791d25e82af8d333449f0b7ddee5f91181e69e40eaf9dd1ea",
    .input = "dc4354ff557dfa58b17a0e38f63a61c20e0fd1eb6cac102cf37fa77913413a7735cb0dea592bc76cfdf7766541e1d4374a8cc9b9e49e30e76b17ded8ebe1e0f086a7055616eb9da814537feeb94451cd62b203fe39379dfe12623b069351553d9882442dd5e60273be3732bba38c60ec202b89a0b49eded7b009c5ec53ba21c8",
    .expect = "7959e5367720f3af55ae91843397134032ee73de6a8db8ac"
},
{
    .testname = "NIST CAVP [L=32]: 68",
    .key = "2e2b999290c9b4a3760c4bf767ae44b28a8d12461552cd39095088291dafdf0df7c9cfbda2d4cbb53dc20b15f0",
    .input = "36581b498cc8b9ea79de28ca91a9cd0a87e30bcefe73b9e59c37d3a860016f2436dff37bc9a086879993c4c14d92b6614a3f01c7848e5d1a9484492f0c3efeac0734a16d04bfbc26f4d9ef4a9124e32cf22f80655cf460755ca583ad12a8444cd0e08be8e42e450fb137112f05683cb3a638f06f2eada83e1922e7e91d472a4b",
    .expect = "d39eefe024ce0b545d77ce327f0731c5581095ca734c21fb"
},
{
    .testname = "NIST CAVP [L=32]: 69",
    .key = "089aa37f72b2962c18fa4e9858ebac2fc1655ff41ba30715a76d9ac3a88f0740218b1a3ae18ba057bd99cb111d",
    .input = "45ae84fe11078713bc87c465e8d88f0b23e2804a6a3e19afebeeaa5a0f4c729db84107c6c8b7f838e251b0c174599d27f5fa92046baf6ad431fbef4df75bfaef0a79dbdbd6a2fae8a97abff4b9eeb078696bd95fc84d71195a9bbaeb1cf12989c2bdc7e643aed74b976ab9a7bf800e26079d1d04880276a4f035d4dc86f74893",
    .expect = "3accf0eec5b26ea6c936323b42636e5899f4bfe7e7cbdf3a"
},
{
    .testname = "NIST CAVP [L=32]: 70",
    .key = "4e1ad1054c00b6cdd0267739c8c92994a4af4bf373ba066c48bcb483e38da0e58d5b0c59444279f3181c228ad5",
    .input = "f6f83ff6ddf386bdf3af9409ef5cef16acb376182322f57b9729f76f0f04dba4098a2a526d55287dc023a9779a7c26a65a951087187564f3db5680a20c4e35ed2b2e1dd8c1ab2f4f96bb90b02342ac8a4aee86a5455f4c42dd8c2fa3dc6272cec4aec08fc13cc2bcdd40f1bc73f6a94ae6867f77922ad5ee0392ac7c6588b9d0",
    .expect = "55adbc7d757e6904448ebdbae5a8773a1781f952f5bdeec0"
},
{
    .testname = "NIST CAVP [L=32]: 71",
    .key = "36e8128355a3dc7ab3fcb28fe93c8e695066334f6610b398737233626cbdf28717ae88cd70626c5d4c6cb9773c",
    .input = "25c04b857a224389e8a2a304e1bb8ee1b352e4cf5c3cb6e99f01fd9557df8bac0c1241dcc453834b1b9fe97d9639377835f2902647a8e6fa820db5d653a9f12d73233d65bbbc5d7f391ceef9835154f34b15f592344fa5a2e4dd607f5b913f358379a5e60864b96c69a11a40500ace9a1f427bdacb3ad927edfa6756169e5d0d",
    .expect = "22950977bf0f3fb8f4fc53ad2ea2c91d936aa98d06ce067e"
},
{
    .testname = "NIST CAVP [L=32]: 72",
    .key = "ff469d80d2dbef999d7d4815d123cf50ee9c2c23fa2e9aab2c7e3d4ce8afb7f5f0cef6a5d86e4f2eba8fd1392c",
    .input = "6c15d1686e680c5aee2941900dc9af9d2503b3b6a5623f5c1c04873c939dfd5320be8055b858d050457c468cf864c2b7e1b7e43ebd097ffe0fa14a1c7280d9312d9fccab087747705ec6a2c47491616c096566132ee365ee587c999cb478b550ba3d1e3105ce57016292bcfd27577405c696a1fda1f8d973201ada82018d79f6",
    .expect = "646031963fc8bf827a30924763dca11b589358e7029daf1b"
},
{
    .testname = "NIST CAVP [L=32]: 73",
    .key = "93fd8e208a1d6052388611beb9f047fe91e33afd4bcd74ae6152d5fe5ce3d9073c921e861a24208f0c68477f49",
    .input = "b99a110bee03f440f15145e28d32c340297fb810efcc36a82e3da171fc9b6d981fa629062eadbd93f35df07614d72d00f205868bd22df9ad3bc6f2b19e8b12473dcf2f7a45109ce33dceaa1ca49d6e78d67ac5f1305b9662740a57f76f32d3e1d9ba2a4e7c531998994d7bbc87af100f9d867e2c527d9531a3aed72bb5b838ce",
    .expect = "00aafb9109999ccf61f6689b7405ad2fa54129c3bc4e67b8"
},
{
    .testname = "NIST CAVP [L=32]: 74",
    .key = "f189baeeec507e945f0c4d628a0d0548eedfd254b11faf25458e29a3456466ed9fe76793f83b8a064c7c534cd5",
    .input = "c821be1cce09579ea899899d24f8329994c2c839cf0084e27857c688837fb5c4f4f72527eaf7bfcfdda75b37248eb153ba4d31dd418d2fea473643c0c9e1f0ebf591838e349d3ef868f1b67772777a71f8cff5b0654696fe31062ef2628a99095355a0f8b4e41e41d2e162051899d519d6b0dc5c42130047bd2f4dc55761f745",
    .expect = "1c8b29577349cf99f80ca11477f401f61e0b1a4d6974fc61"
},
{
    .testname = "NIST CAVP [L=32]: 75",
    .key = "b763263dc4fc62b227cd3f6b4e9e358c21ca036ce396ab9259c1bedd2f5cd90297dc703c336eca3e358a4d6dc5",
    .input = "53cb09d0a788e4466d01588df6945d8728d9363f76cd012a10308dad562b6be093364892e8397a8d86f1d81a2096cfc8a1bbb26a1a75525ffebfcf16911dadd09e802aa8686acfd1e4524620254a6bca18dfa56e71417756e5a452fa9ae5aec5dc71591c11630e9defec49a4ecf85a14f60eb854657899972ea5bf6159cb9547",
    .expect = "737301dea93db6bcbadd7bf796693961317ca680b380416f12f466f06526b36b"
},
{
    .testname = "NIST CAVP [L=32]: 76",
    .key = "9fe42dfac92a4a136fa7c9f6e331b5d3a61aa73035b53a8d2517be43721b31b215a96b9bd43798cb5e8febfa97",
    .input = "f9660fb784c14b5fbec280526a69c2294fba12aea163789bbe9f52a51b5aebb97d964f866c0d5e3be41820924fcf580db0725c7f210823cf7f45a0f964b14e5555070d1c3ddb2c281a80c7fbf72953031a4e771d7e521d578462cafae5a02ac8eb81f082e173ddadc8c41d964bbfda94f5180c8da28a8ebb33be77b0866fa798",
    .expect = "7786c155d10c741b63ec650b7b1aa3bfd71ac71881ad06ae98fb082f17e0caa0"
},
{
    .testname = "NIST CAVP [L=32]: 77",
    .key = "98fff7b5f77326c24471bb9c317490be1febad28e2e825afc41c3b97cc03c963405ce3ec68dcb7b19523b76e62",
    .input = "64a78a4d6fb8ff3813df8dc022faaf4415e4df2949e16467683c6c47242e5a6b2c02610e5877528d2766b2266ca41000442a956c4b73dd6b10260570c6f506673cc541f50f0f5b021e864a753efab03e2f7c689acfc35f928ecea6c522cbc5687c38518bfa48c19ede887d33ffc23806be21803a3c9793e5ca7c75cfa1783f77",
    .expect = "c02c6022ee0de099e3027850be95a29ce800118ed3a97757dd8ab9e60f69a005"
},
{
    .testname = "NIST CAVP [L=32]: 78",
    .key = "8d649e5ccbb8bb0032cdddbbe44ed0b5bbbde78a30c0f8437bbca985fca5ea08da15c34bea9b5086d2550ae16e",
    .input = "a7734a0739d51af0ac2c4039dfafa86f36fc06c2355d0f654d4ae938f52fe0a5fd6f5ac71fa80dd2d8396faf76016ee6716a62c1fea640afe23910e684b8a14c47d07b98168915b441cc48668724043074c14275edc239dc09b4d5fa2255652b2c9e94c046019a608ff0b3a83b9ed015e6098d24273864b769c120bbf68f9408",
    .expect = "13e0834e4dd72a2ef7872249bf895da4432329c6e8ade8665d702ba33bb677b0"
},
{
    .testname = "NIST CAVP [L=32]: 79",
    .key = "57958d7e4c73fa606ef405d77ea4977ac96b8813fc1210483a037e7b6c502ceed8f7b22bf6655aa37e38d495c6",
    .input = "0b9a58cd96351a135c559d17e82ede3434a0caf0befef5dfdf138ec5586793fb2ebe4114b9e2cfbff7a25bef261b253a9136fb7faa72f4cc59e4617f947c01ab308974bdf67ff25ffaf83d9c28fad44520786a94441b96100e42ccb0a8478c43b604d90f7695edb90c602b651753551d886dff77b4804472a835b7a2bc509c8d",
    .expect = "cd251e66c421bad1b37cfebfa3c04ef30b8be4e5526b10fc48fd5bc5d6f04bb4"
},
{
    .testname = "NIST CAVP [L=32]: 80",
    .key = "6d32ba0c063774bf8d0621b208d72095f684faa33ca6f3dc62fbdf95ff0c3733720c6c34d3027b6f2a2bc29cde",
    .input = "e5804b099ee4b351843adb9c9e3c231773256e6a2070d697a9e29e258dca677f9d88a7970d4c58cecc20ed1811298a5b37297419ca49c74fe216679dafc938a656cb92bafb78efb31f24e71c2d5b5f994f6dfd82862adfd2faeb8c408fd22aabb852f2bb90f1e2c6274cb1f0195c089766f9efee7d9c86e79a69f557526da555",
    .expect = "9d283d8e8e473a16162d186e96355b1885370e83954dbd08622dbe64f0aac695"
},
{
    .testname = "NIST CAVP [L=32]: 81",
    .key = "6b97478fdafd3a85d0d9b339971a70c2fd24d542abd3e20eb2bd630f67b86668719df258204bf66201ee80acaf",
    .input = "8b1d4523b6e457f856e5f09875d389eb6587223e53477ba01f49878c6c731ec9f365f28f1cb9c4ebcf89d8648732a6dfa958d2c0152b5e52fae81f69eea26d463e421fba82cdb78f75e5d92304930256a54376a6ea107a995642c45c6f1530a914bdb4ed11a696abf100dc1b147b0518014ff639fc80373ddc605fac1755cdbb",
    .expect = "6ab8f69868b4c87fdec9a031045b34b66660212f687a83d561bc4f9caad59fff"
},
{
    .testname = "NIST CAVP [L=32]: 82",
    .key = "89c77d79de98df18f0cf29a9316d6dc46b61eb7af7f1e2de2f5ca6c525bef3c996338194193fd85b9c6e66a811",
    .input = "ff8662e9af3a38d3efc0143138fa619a57d569f61e29b3895ae08f2d055befdebc11787c7379d9cd672b5cc25442bafbe804348c78c5df02f30840a114e818f0dbb681783de43ac81b2140bc71c69effd07185cf0eef9f003c60a144d89520a944bda563774103ccf3ece8a9f64fb3aff564854646719b8c1d2fdb9db92cac12",
    .expect = "4746e6f151caf29b3534b2f493f7cc1308fa119116d251481572a1b53a8a1b3a"
},
{
    .testname = "NIST CAVP [L=32]: 83",
    .key = "08cce7d7f3ccea0212cf0299f27f3d3f393a97d3dd71caf1954e67bc8d9a26db5edd7ac23dc7693372ce9b040d",
    .input = "33ab861f089bac0e5c886f66adc568ae7ba331655a371de7475e269138ff2725f7904c702fdcc62ac703c31d70c29d8a7af451c8ec59342ed397e133da7e76d41b90003635c1338d9f7b5f3c3ce59f3e2f6554c4f064d11f9f5158e199e8463f4ab48aba42d25bff8af92b0b38b7d69241fd20a28fde5e84539473e39dc4fe2f",
    .expect = "2c723282159ceabc5b367b95cd807f249f1dff7f9ebf5ba179a43081454e1b05"
},
{
    .testname = "NIST CAVP [L=32]: 84",
    .key = "1a2e86f6ab2db235e5d7f00cf438680fe5b442dcb1f8c3ae7730b92f097a1a8eaa9be8d216f2576ec3aa321567",
    .input = "5a2240f64fc704ce9f8ed33d019e4155cb46747a659e3421fe6b42d67f44eb84bdf3dcf1f31e38886f27e85b8b503368df238e1bb511b515bd59fa2c032bddb31d0ddefba97f8f19f7daedea027ef055a52c61d00bb1ec2668c57677e632b180e339ed1c5931310b9d718af34d70a3a4832b96a04fc702db65785ebf12a18c73",
    .expect = "22de07c3055a8935b52bb2c85a9a6b7ffd4038b5db4069c07e9e86ee1b171d25"
},
{
    .testname = "NIST CAVP [L=32]: 85",
    .key = "3270b4e48d575f0312659a6202adbc4e877d69298de4090ed47278b4433fff95802e844fbd73fd4ad5532b9b97",
    .input = "f407f815a33cd450c0b72a378f00762788f91bc44f09f93de67a41d2222088935b3c1b6a689f935bca13a90b28f64b7ffc28ef278b28271b1a7975a45f4b61fe3657ca5c950b7a2dc2e7fd9ec327b26017a222aba3f29183efd5d33a92d36136eb21acf412c6b14d0efccef849d9d451412e5d587fb060fdcd55029ba401afc2",
    .expect = "dd1a8105ab753d83d90ab39adbc748940fefda05bedea7eeebdbdf54b02d9ae1"
},
{
    .testname = "NIST CAVP [L=32]: 86",
    .key = "c704d5793539ef3909bdaa7c29e9c0a0c441814c37bcd062325f6e2e16107be4a2aa3949cf4d14b0f8f8df283e",
    .input = "dbb84fef130f929805b0876cb4646a046330bc33ab1cf1e9ca3869573ee1a1549341ab007915dba719b3c4e8a94b62163e6d99dee2cbde2ae74135467b125b417c7544978d50c80c694399db77e878109f59a8335df3a326135a0d50a4bde6fc3e5c03fb7747bf919c68ee8f45c312bc2dfdd279411ba7a5f78dd9bfe16baa4a",
    .expect = "441c7fdaa40e50bf1eba073509769b1c0942f3a16e1e183435819d3b5f8538cd"
},
{
    .testname = "NIST CAVP [L=32]: 87",
    .key = "5b2cced47045bca47512fe226c1f415ef127a209bf885b8a76f5a24f9c6bce61e166bc3ca75471ddc14a001c7b",
    .input = "1de00288a6e93930070183de9d9ed0ce86f6cc0f64b7bedb5df8af24676fd06fc2e516e5c5e827a7dec07963d5a4b825502d696f9c0ace8baaf6092058e78304f2888f51f9ea4bbb2376c720a2276a61a9f691712d9578abe95f5e69a490e4d2b6b1b7f3c9576e12dd0db63e8f8fac2b9a398a3d9ebe86e3201df726d2d1ba82",
    .expect = "15c62ce7a3bfd5b3b3856d6f47cb19bb7030dc469e35a27807511f81ea83091c"
},
{
    .testname = "NIST CAVP [L=32]: 88",
    .key = "0d4dd35f90f0a10d7d8030e9919446f3d5e2532472bcef0cc5db84bab65c48dc46086f2768d89ef912b8a23d93",
    .input = "2937aa2ff7c942bf7dcfa670154e988c28177391969db4995804ba1a647acacfd0ca56f63b2e7fbc6965d8f62d066d118c14044c1fd2a224b9d951104a67216f03fa6dbfbb1e5f0f9283b6b7d452c74620c1c2bcc9e637fa7cc8d97623bc81330aef76f1403feba1414fc91bd1daaf132b4737495b7e7c01e9fbd9b3b720f303",
    .expect = "d5596bcc39af2782df1cd9fc8c37a8f96789275422f511280971d8429a8cb661"
},
{
    .testname = "NIST CAVP [L=32]: 89",
    .key = "5ef946b64ff80e4df8ee98a357f07c825c3acc434d0f994069c0b88ccc0ac5e192a469d93f19d9615fd49f6b69",
    .input = "dfa3b06eb1e30b47ad9f0bf0f441fcd94856ca8b1f4cb88cf6795582e860ad9c7f30bc2eca8e289bb0942f78831addeed934836097fb664e4e91b47acb5fbc49e9a15d6baa25bfbe864f42700361b46586f9c7d869dcc2444df17685b291743ac5fe7d6f78303a79d8d82d209c9fe804f9ae7d39be7435359ca385ecc57c3d39",
    .expect = "223dfaf583140a769c805c33f1f30bfb2f0926b088f55439dfeb4f5a9ceeedf1"
},
{
    .testname = "NIST CAVP [L=32]: 90",
    .key = "79f87734c46c5a11d86aedead22ed3ea01577ad4ecdf42969650e12000350676f0cf3c04f10a11339baf783914db6d35d7b0d77bb44ab22c18f56d0b8f9d918b",
    .input = "509a0a45a1512b5072474b297f9c1a8c24890016144468504e245fe94d065d437fef6232f9f34500695549b44ceff29361d417e85d353701e081117aa8d06ebe058242ca8c23f3341092f96cce63a743e88148a915186ebb96b287fd6ca0b1e3c89bd097c3abddf64f4881db6dbfe2a1a1d8bde3a3b6b58658feeafa003ccebc",
    .expect = "b5b0c43028e81628dce82517fa36aa29"
},
{
    .testname = "NIST CAVP [L=32]: 91",
    .key = "eae255d9e083268f896429ce36645502aff9dbeaca7159f93c7d51fdaeefdbfe14c396693a5ce46e9f1157a687e866f94ca165bff5f7b425092236d2a6a004cb",
    .input = "c28f6a09ce076ef270458967fe19d46e6f6b2cbeb6362bdc4fd55684177e984a600cf0814501665c3bcb4353e94681c83a8381ebb0c8fcdbfbd73c0eca738cf2e121edd46b2c0a0292eb6e2c4e46f5107a7780572d0eedb9473847684a4039ac6c56c9caea90432b9e2e72bad422168e5ad093c9d612e7c05c7fde5c40ed89c0",
    .expect = "b84003c417a472fd2935341962744330"
},
{
    .testname = "NIST CAVP [L=32]: 92",
    .key = "42521bc3f168b2b3434cb4e44d92f526b41c5f10bfe0a0e6b0eb20c055a636e9da599b86e1ed1f78d4f69a837af126afc9c98beefca1fb00e5cd00948321b2b0",
    .input = "5a600c468ec22e42af5ba93eb79452864ebe469a86f83632c85201800f3288b553f7bec649ddfe704920a27a8f65d13aa755985a238b3cdc8fb0cf5ca7e40295c7603a27a25ae69837290f9801aa30896ee2493e93e52f031ef626de8cefb1159ce4a9f003038dc061be1920742d1a7b8bad80cf3eceb5b05d6c2d8f261b3f3c",
    .expect = "e1c3c6d90820511c8d685c73bb757ee2"
},
{
    .testname = "NIST CAVP [L=32]: 93",
    .key = "81b5f12a64f3c347902549a1fabd39ea1d9efeabed3851880df40dc541d23f0926507d62218f7a8a95b1d76959853bda6966a5b2db6001ff1595fa8d3edf10af",
    .input = "04369f9592b00626d15b0a4b0ee2f92ba0d086c16d016ce7b05654b4f9adf90875118a656f2d50011707901982ebb387f3a4a49759f37a17183957ad0c778f6ecb780dab2b4df30e05fa81e6386f38c0f0ba3f37287a050d6d97287ae53096c391d5f20fcff73977239ca55c3657d1fd1f781f48e28057f136d890c28cc25432",
    .expect = "5f840796e0d35c807b3d715727432e68"
},
{
    .testname = "NIST CAVP [L=32]: 94",
    .key = "34f5d28d58364da4b95a48c07e01b0a99c5ace173ff2c9216bc96df8e3ab2ad54abd60308857da336f11986e9f21d1cca6e438c66cba7fd6cf17192f8ad745ab",
    .input = "59a6b0317f130f6248e746e396cc684b32b9a0eabf15c50bec1f2f76ee8dc9392e7368a83e675ba312e344176deb26c799efbe4d5bf2175b26ec59478f6de1c7018497f9b2df7ca6d53383c712dfa24833cc280d209751330df21898f2474c9d3b9fe62ac1c39af3faa0acfa6cf0055568178632f44b9c1809f81570ff633243",
    .expect = "5a33b8f7cdba999ed61fab3869b8f1e9"
},
{
    .testname = "NIST CAVP [L=32]: 95",
    .key = "cec8280c87170f1d4836cdd77abb2a34410b8d5351d96d1a03e90920a71a59ca1ca344b49f9d1352e1c226d75c74e555e601fa268725be8c88d0f094cc2aad40",
    .input = "952e93853e9579c2fe353dc83203d34f04963fd64880a095a4de6eb4f42e00baec615148ff31030780b5a4df0833316a1735d8a8fedf02f4fc7f9136a766665b8df727021cfd3f78bf4226e74a5de2ca98cbcea472419af2b341935eaaec2435c0179d1b5ba034fe02024a48c128ef59cf7fa7346e4f6e78134bfb93c7674232",
    .expect = "aedb7ea80734d1a65723da4f3ba18f86"
},
{
    .testname = "NIST CAVP [L=32]: 96",
    .key = "9f65a426106db99dcb2130be14839241d4a92c8becc108d2c9521b8238c5c0df7c2365ec9f20848c0559d6e847dac3103ee31ce55dec0c3644e64c2993c497dd",
    .input = "7d3d9286c1fa057175c33c556d2c4b87fe46d1b764727d6b6172d1ac27c626fe7835f1960caa44c8334198bfbba2c970148e62d0b2b71b45b3d5a05bc2f694b93b15d6538fef03e1eb123c8f143729f696d13d4b1de63cd6231efba6cb1a68840d06c925147249a4e45db02f40937200cb3aeb8e6da7e905f8766bf40cd9a846",
    .expect = "9f19ab5e517e884cc1b1d3124ec9ca50"
},
{
    .testname = "NIST CAVP [L=32]: 97",
    .key = "2edc66bcca9f99ee1366992fd0f0f954d3d4c5ca2115c2d053f6f8e33c0f6e7acca135f43427a7cf4b2df11a3165cf2d32f89797ed1a7958b5e105513757edf8",
    .input = "188a7fb0222c9d8e19d057ab22d71e0356c4f8d1184179aea663eefcef2edb85a55ca860925a97152f94f90073f2a2fbe9a29a370519156bb854a5314264afac48291c6f265e509a86d5604632047f2426c1ba60ea4ae6cc1e88d63a5695d129297b42a5853fb268451ef44506169fc736a8c2156dddd2180187e7e0d5c92844",
    .expect = "03243d10c48609e8f4182638c23516a2"
},
{
    .testname = "NIST CAVP [L=32]: 98",
    .key = "f987eb83a3fd6d94ebf3626b7d34fec23ee06c63dfb4078cb38bcc97bd250fda0e286ecd4e64046a985bdfda8b01b34d9dc0cf2ab3bf5168ef64963bc918f5f4",
    .input = "e105ff11481159c52baef5de550898214e1d9a90da2d9083c36b29fad8f956323613ae76c68b103807758a600e2379e4cb54f2998da86149c857700517232bbc7d8b610df0424d5a18df751e54d6d380fea73328f055dc51461a721f66591b333ed4e17ecd1f5852e55580bf2f09ec1c6f7f24e4091c49c4c51cf7f1cf836fbf",
    .expect = "03364863690c439b306a2967daa2418c"
},
{
    .testname = "NIST CAVP [L=32]: 99",
    .key = "5a35a2909aadd278b810b101ed44e1548ddaf9ba8c882bb142d9243f6b23348672baaf99ef63938e6e0b6ad472b972c7b9c2fc82c23c12f48db45c37a224451c",
    .input = "ba527305604ef5581850b222fd192e6260c3f20eb30d8f04a5f4e1438f83915b0febdd22f2d69ca958f97c6e12e88fd34f2f06cf789e3ce458e4f6518060e988ea337ce2dc9ad0920f7bfdd8113d9f77e8dd9268f83ef9d027c185303e16f4db9252d7aee54199fb87fdbdc6c0bf673473f61e40fb96d0b059b31647914eba3d",
    .expect = "d360c381d230d21cf828782ae5e389f1"
},
{
    .testname = "NIST CAVP [L=32]: 100",
    .key = "96da746779ee441651fb9ccd2da621eff4091111f8fb795cce92a8335ee7e31636195ac724955bab0394c672d5e5c1fb12ecac7140eb58bbc4807313f86f47f4",
    .input = "198b79d09a3dfdb5d41043e679baba6592f3c751cd7cbb0d1860029f6e7a9c56f137d2b03a9d217aed8c7b399044afc99d282544d5c2ce26d8065baef3dbad8739d78da7d54a9e789e7f8f35ec3e9597aa9519b2add9ae1944e7454911afa44517f4147d134d5af41070e9a236af5618e3c30c62fdc94131868a293a70ff69d9",
    .expect = "3df86c710d782309023d65fccdb91db4"
},
{
    .testname = "NIST CAVP [L=32]: 101",
    .key = "43aae2621459a8d5b5cc919445f3dabc0165d136ba01e58187d5ffb2b73f15b90951fce5207a7dab3163aca3ff1875d309687830018e17628111ccc8fae8c0bc",
    .input = "bac0889281fe55dae17c45079bc44f8976508f5a92953c26f940daae77bfb16eac037d7d5f8467b615863415e29bbd63806a9f169eae33737a82c1f5b2dbf0f25856817c44343d86aea22c47fc3e08e4d8d8f14986756257749a644513c70240e641fc55d914c091d35995678eb51a51a722efbaf1f2b21c0f112d66428acda0",
    .expect = "83467cdf51f59916b492c5aba554c606"
},
{
    .testname = "NIST CAVP [L=32]: 102",
    .key = "fa235ef9f48a666e2e55dbc448ef934de0d22ef5c0ecedc75548c8b364eaba8ef8fb605a9f26c2c8d54171fbc130d28f1f06b9da7e6e3971ab4abbee6d994ef1",
    .input = "da32314c22dde556d886ce2dde1291f1a4c1ba14aaa95b694063f57e91049c2cdf4e576c1028c66c6a4c07e39b40d9a1fc87026a1618ef04660f9b8f5da3b215ab58f562bd75e01684b98af8794ace8ddeeea8ea467de1c65797efd3cf92174fc5b6d4d532ad7c7aaf3521158018b5ded25e723b41c179d69d61baf3eeb91301",
    .expect = "0d88a7f3a8369888b4c3223499412256"
},
{
    .testname = "NIST CAVP [L=32]: 103",
    .key = "bf248c7c6101e6e0281c8955e5cc028d98e5688d3f36d754f05620bd26a1bfa6597d0e52d1e2b80cbb196f0d7dc3e2a0471ee984ea840392ee34039fde5506a4",
    .input = "557f845dc8962ae11561f63ff9f7a9fd73ad5da479f1d1c3e9760236c292fba894e4ed5735398217b6b06f9a951d49ee34ac99478ac732ff1939c2db2093a89011ce0586453316dbef78c1ab4f2c6d8f285517637357a24d55176ffa4f612e2bb587f471614b8d34a8ff13fa8debbfe635ef007f9b6acab4855a311cb7c43682",
    .expect = "84ac389ad6e42798a97784941bb76fa4"
},
{
    .testname = "NIST CAVP [L=32]: 104",
    .key = "8b4c9c2783240e19128fcc2754c47d68d6acb3365999cd85d3351c74b7b94422765fe5c346197bf3228383491216e030ac9f7cf2dbf03216dfd6ecec954b0866",
    .input = "dac416df793ee5fbca992682974a0c2cca63eb49805df0a75e1410b628133eea8f12e1614bbd85c66ab7d075e8dfb8df7fd2f430c0b1b03063248567dc9ea8852fe3620104c8c0fffe3a8b7749827a9472c7a75a7cd5408c301d7fcdb4fcdc055f408106cce8fe702d2b3ed1e2bcb9114b4dec0eda5206836c07e52ed9b44032",
    .expect = "fc38c3bddbc320bf7373834f3c83ac67"
},
{
    .testname = "NIST CAVP [L=32]: 105",
    .key = "a5fd99ca57c1fec8159a798792426d296fa1b17d539241de3dea335819b7ed0d92c596d72867ca2f8273924e058f9391a5ab8522fbcfe7d59817f1509afccb6f",
    .input = "5cf3a5202df8706f6bff5bf2590de37c902c7ffd4e6c8ea611288e4e658a8e15fa51e647f9d22583983d4b1ced2239bfff346556234cd22d86b140530696a04446e4cac4013a720e9e32582e05e7c0acb2b4226a073e22cfe7b4c2258055d7406833ba61ec373f5aa566ebf24c62618ace341e01a34866d65cb97e8c7cd01c53",
    .expect = "2c2bc8c87017f204c958abd9aab2beb6ac67781d8d9d804c"
},
{
    .testname = "NIST CAVP [L=32]: 106",
    .key = "30bc3e321a8978e235fa1b550064b82eaa0c107525eacc827cad6f1d66ff88e31b092cec663aa3aafc4462140c68390417f4cede020a4a736aa2522537d2394b",
    .input = "c1263be423e7888eaceccfef26f0b5aaefe03f3ce732dde98c78a7f66435e6199cefd62eee85aa2bc8c3d156aa3478b6cf3750c71155917207d23f3b7082acbdd4de3e536857721933eb21136ff502ab324971614d806ebe7491e989a0a23d3eb21dfabc5905e73e358b478c3ddc5c735e3e2a72645b7db61edc2d49bd3aa186",
    .expect = "d722b57c48128b37ba38770cbf4660697757bab95c00c484"
},
{
    .testname = "NIST CAVP [L=32]: 107",
    .key = "c189ce5334f670ed2815607ba9549f07682e11f70259dee3854019a431b3a0ad7bdd439f58772817b73c6dca4f9d10d59cb50c4e247fc51fff47a614965e0932",
    .input = "a5deb712fc3bb9fbaf1398698b5696600fcd61ac68489f26a0f8ca32121a3e8c21d5904529662208b67af4a2f4dbbdc1674f3bfcdcbec714a0922c7aef63b911afd495345fb853fb4a7ac6ba00bb17cb063c148ecdffcbade1a958a5632bfb82b9a16ee9847a755cd2dab6ba963ccb05555c96682154d479cb05f5bb55b82c67",
    .expect = "3d6305ad9dcb3a50105b92f331009a3cb03ca7ec36882fcc"
},
{
    .testname = "NIST CAVP [L=32]: 108",
    .key = "085ecb69492deaa704e25aeeabb7b7795fdcc807b3255f2fb30081f425a9c7990ea104b7785c288c733965965ab8906057e8c99d291e5e7325eced197b51c9a4",
    .input = "2dac1599844d82a79c7cd1669a1c6976267f655167872f8b2e0c5059717e8651fccc1770638466613b3bc4fc892f880e7b2b625856abecdab0418251df3754feb176b9a95ea6c7e6ba972097afe00eb2ebc6d344d65f3ab6c7f7724f77b21cfbb673a34b5cfdccbc83588e3cf37723eade175f1eceea41a9dbf5c85e213607d1",
    .expect = "35fa859b3e4a793b2329652cc61f9f68816fed67fa402e1b"
},
{
    .testname = "NIST CAVP [L=32]: 109",
    .key = "f5a07e3741f03174c6efcb1f9f186d1f233b367073c56e814f4204db2e203b048db6a0a387853fe4a6bd161ef903cab46671993942de90d71f60fef1e5102807",
    .input = "067ef2ee1e95ca546882e2a9d441dc563235198efeb52be97dc7894f092b8718a89c8571e4526602d7cb44ce86cb615a70a2611166adb7e79c1f5e3d0101c904cc781c2657479c21319464f56fef5b41429062a9cfe0d27a3a3c259104f5f379989b21d3207b55fb9d66ace837b4b054d189841de15762ec7fa44814bc0eedbd",
    .expect = "aaed7dbe184423f0b4c9ff72dcf4557ec123b49682fc24c3"
},
{
    .testname = "NIST CAVP [L=32]: 110",
    .key = "887c37f1f09920ba51885934af50a4b065e9e2160e971ed8a676cd26ed5554610cc7cbd17b78019a22bec0ecbf70527b87fb432f10b2691c6e6622b49d37dd3b",
    .input = "d6fc8b4b72b7eea80b1c6f53c11a52510f920527feb8f95598bdb120a0ab1994809018ca83de68674412a6656794a51686de08656ee110608ca4b2f3a22fedf6bea75a6b6dba05002c3e7bdc1f1424970653d38a6ca29c4a21e6e66feb1ec09a798a79b698136a7daae7173e536477de75378f1e5fc5461b41ca741be33f3c86",
    .expect = "51ac4d2b5923a5df8ec48c14ec514a0629f8e385a9ea4985"
},
{
    .testname = "NIST CAVP [L=32]: 111",
    .key = "e9061ef9b298e47af4bfe35903d22e2ea4cedb85c53e5ae16b5e0501eb7ff7615dad22044e909c71b5903afc283c604650ed17079ba6600b303fc97b28c33d5e",
    .input = "5e873df5f280723dadd718875684592a7b2c56916646bd874d7c99b1c9546f5c890f867a48d286e6fc0345f051f6dd1555c9020e758c920da8a56e43ea7389a5ec323ef00a1fe7ea7ddcabebd215979d9a64f0006472c8b1e860d06b85656dceeeb80e5f20b0bcd19729f383c12bb049b3c6cb6f1b4087fb757368338270445f",
    .expect = "20dc2be5c7f0b2fa8eaf026c152a09cadbdb82e52538b393"
},
{
    .testname = "NIST CAVP [L=32]: 112",
    .key = "78bab2c40d60d0770c5d2bafc455265942b0d932174afe255b6c0ed4f1fca7750df031dff408c1e403bd3de2f375c2955bf8422f762772ab27ece35e3a6d6ecf",
    .input = "c2925d3d09cfab81f32f769d61dad5a03aec0423be785a7417cd7bf331f7cfbbcc893385d09aeecae00ee628311714079dfa357cf317c26e922423f736b9200c111198611e0f7587b27fdf57549fb094cedd28cc84e3e37f05d10784e0c9c2a7b9b1f4979b342800900ac9f46f7a938ff61d47db18e4a3f1985c9161d7319fd4",
    .expect = "da713e318a9e5b4b4f1dfe0a2af0837d70fde54442f264ff"
},
{
    .testname = "NIST CAVP [L=32]: 113",
    .key = "a2f1635f239f03be853b26aee7b8035a5f267bf0ebd7a8ebabc0b8984d21fcd3c8693c124d544ea67a56e63dd23cb0aa6a119ce9e43e7a5da1f6c65d33d1c5ef",
    .input = "5c32698a0a56b9aabd41270ec1e475c5f965bdd07366a7843f8adf2f8235c7fec694691e94deaf2245d9d6a5159f203079a2c95eb3ee3d3da3ae88f8e0f20eb307af7cb75307fecf6ecbb3f1873f5e21a51d5e933bdce010fc31539af0d71c53c88c8b9b6f5c0e79e121a53c404b966225dd62b834b8f7c3f31c275fdc6c59a6",
    .expect = "5ebf7b7d25b0ff498322e4264bda56f7512e9d4ce3c9d51e"
},
{
    .testname = "NIST CAVP [L=32]: 114",
    .key = "69f533836771a3cc0087fc2fce7c42318f24c76acbf8f139b8693db65a7484e8ee777e3989438426fd729a3bfcfbac3f800318ac69f66d6268d7729b1dd46b22",
    .input = "70901c61c43a67e647b5274e55fd3a934b0b8790eba58470027afc67476e0fa087337a76ff1918e60a27a944fc6ad32e4d8d66bffaaae404286041b40a26e71b06defd5813aee9c8660b13c24d16ec855b2c306ec5b8686f0c4cb2bcdcf1c4c735bb2f6fc8a0e174a489ee2f11aa9080bc0f6c0715781697f667d8e78577af8b",
    .expect = "4f0a78dbbe767218eaeac0400656c4b4b23f908a9e7f4708"
},
{
    .testname = "NIST CAVP [L=32]: 115",
    .key = "2daf08cdc015bf361f66be9cfcdd6aa7f1003db66fc95e23f70475c88cf8bdc268495b74ee1deecfe07e67d1d2001b4cdea316e99afab26c478d693a4b7de818",
    .input = "a85ee973c99d8da60d745894990b24b9cad7e450be0e4369175e883bfbdebdbb5f45106e865a797bc4ab9d048882f3b69a15259fa0fdb940e7e9f0e46094ee30e9f41cfaceb5cb5f90e51a0fe5f119ecffd02ed4117eb8ba10acf3fcb7b61cf0cdd5d5c0aa96ca79f88a955eb73fdf828370c8961a7989ff190d582c062b8d26",
    .expect = "e6e7baded94fd4042c2d3ccb586d8ca983e8033e4ccffc68"
},
{
    .testname = "NIST CAVP [L=32]: 116",
    .key = "65e35c88ebfc4c425d0362c5cd125ba40a0aa76516347840da281a2419ee82fba364292fcbdf1b6d1a154aa9453b29625d6a76274647575a6ae3a934aee09509",
    .input = "7ba8ff928460a47c78aa938519d33978d7172ba2975c0d2bb421b2a643b184e69c9c2713166759fe11831db23a7c184c0a733b0c90cea2ab712ebcef2da1ad7ea31af0f0d81e4127f4bfbae38dce3c91284d1064fd23cea7fb137e520ceffedb9a09a44e52eb23a02848b3419b326cf03a8cf3d367c359c75bb940f56a0240a6",
    .expect = "d9eafa06a75b5b671be1b1f1e6296f17f71ff467417b7837"
},
{
    .testname = "NIST CAVP [L=32]: 117",
    .key = "84d5824f5b0deb22f4476578e8d0dd192bdb87f93019236a54897e9079923b15f14fd31f9f2adb7f58ac862c8f936aef3225875fcfc58510fbc43d08f4797b72",
    .input = "20dfbdc107b5e0af83b2d16021039d0269de2d27b40bbe6c3ea492597c19e589b076230bbae95807317fe8a5b22e802a78184c652d0e6b490053a0dbf8a34a4f8874966d637cf33a9173c6d5c31a5f9fe47c2c9ef0742d24096fa8abc8731e04d1617db1aa77978fcd18d3b8fbd023a7d493369da545ee448180149293914bf1",
    .expect = "e7928a55a3e4274394d81988a08196e07d5a5df047140690"
},
{
    .testname = "NIST CAVP [L=32]: 118",
    .key = "833b09f3a7e41110f35ae33acef5c9a76ea93119548154fb154815ac60892c1b3dbb839493b5e0d9ed68c5757dcc954d621bf778263e7f508b848cc9879a6c02",
    .input = "62d432e97b1214a94ab922b6bfc7f0a32f0e9973a737b0b67f067af532e05a506d8a8c66653316756eb5fcc2ca18b43cbe57d95ceb67244fdc769757dc71fb6f0ac88d2eaf75f5edce3b772cfd2b6d32746df5f4643de7388a340afa03c9870f62179d0800e1975993d3fbbb020a05ce78d75303b8c0e2b9b0c839a650f1e479",
    .expect = "b4c5612cb1c1dc4333450daae500cdbcfe3ee1e3ef7a0d61"
},
{
    .testname = "NIST CAVP [L=32]: 119",
    .key = "5efd2d24a034c9cb778e6730c3739a2e48abdfdb0e2c2203073083d5f38b59db813c7730b742afed93b195e4f3048591b2b5e84d140bb2c564342fabdb9300ab",
    .input = "b08f5e5926b68f1c18652c7f7fc593fb3c3f5370fed6331965bb77be681b5e2bf43cefe2d5c8f50dda6949b634954f3a20acc3fbc640b65660b3d3d59e08e7a549f3a14a28329691202087c69e88e7283ab7989a94d5f69b827516786e6a4fc0f9dcfaf9e49c779131b57118854462acd18959b4313dfbd11526c7119eea9f66",
    .expect = "3d0a38dfe4a8801ab9f9dc1446c535d792393ea8d763db4d"
},
{
    .testname = "NIST CAVP [L=32]: 120",
    .key = "992868504d2564c4fb47bcbd4ae482d8fb0e8e56d7b81864e61986a0e25682daeb5b50177c095edc9e971da95c3210c376e723365ac33d1b4f391817f4c35124",
    .input = "ed4f269a8851eb3154771516b27228155200778049b2dc1963f3ac32ba46ea1387cfbb9c39151a2cc406cdc13c3c9860a27eb0b7fe8a7201ad11552afd041e33f70e53d97c62f17194b66117028fa9071cc0e04bd92de4972cd54f719010a694e414d4977abed7ca6b90ba612df6c3d467cded85032598a48546804f9cf2ecfe",
    .expect = "2f8321f416b9bb249f113b13fc12d70e1668dc332839c10daa5717896cb70ddf"
},
{
    .testname = "NIST CAVP [L=32]: 121",
    .key = "ceab398e4107483ede64ce107c9270e6022778b61f6a258d3b7045d4ad8506d32ece0a738d2cb948a562dbce8d7b66f30e6694d65ae439cffaa454af09abe449",
    .input = "6dde9ae867e2feb367008a975d7853ed8f89690f3c87a1107f2e98aa7736f477a527ed64956f0d64c1b23361b261de78688ea865fcff113c84817e5b377e829cd2d25bcf3adbc06762cfda736f5390d01a49079d56e969f03313e6c703e3f942bb87ed0f9c4d9f25120085b5dc75ef5d6d618da0926d3293568dd7d8238de3d0",
    .expect = "2d3a760595f3fb19293cc6d23651222a9f5a4f02284457a9c1ed4c43ac993ca5"
},
{
    .testname = "NIST CAVP [L=32]: 122",
    .key = "6a6155dc4d59c6bf46caa3de09666326da308c51a23e6ec342bd12b227376e8a1f11da906b58c8c515bdaf0d84dd48904dc6fd614cb79f5ef4285757e30adf72",
    .input = "107bdfb55c601e74f6505015a5cb87bc0eb0b2e7cb04594fbeef8e0fa5072007eed21183cc854a188a128ecf2062ad8604dffa924236fea9cf5b6e001acd5bb0e51ba95e53a7c21b42aa8b89da78983f66069c6f63a923c6d7208394e5d50f2d9d608f8f194ded45c51f318bfe94afb2df2b7fc657e42e6f7f47b3152ba7a547",
    .expect = "6dc2b05619ad5458ee3de70b0c1649b3788e1a5312e8924b5486905506970881"
},
{
    .testname = "NIST CAVP [L=32]: 123",
    .key = "ce97ded47e101a6d0aa1041138093586046524f54345ec9e860550c9415bfc002d2c0d7beaa4d4dce985d71d89bf19c680429c637d1023350c963c28b93c7e05",
    .input = "f62796faaa333dddae596f98cd4de3931ed90710287446604a158b575b4901fd8d841e8697b4df85131c555c246060f75ddcbbbade3a38b7c0444d25b4f6d00de6d8ff47288bc3a54ca1366ed1b2620ec3ab4c0bdc6a313bef880f3587766705cbcc4124a4dd72a7228f1ab61c6a704017eec2ed692ab7549f8ad86f1bf14e4b",
    .expect = "837ecd647e03fe8df9a92c32dcbc87d0734851ffbc17376e03218cce9cbe974f"
},
{
    .testname = "NIST CAVP [L=32]: 124",
    .key = "554e344537a09659920c19b40f2850b07235c3c7209993a6de905c82db1e5faff148e16f2883ce087c6da219e0bb892d8272c591515b5163bdb0c4ecbd1c7730",
    .input = "44e9a1f1437791963c1a3e0aaaae24affc3b405844d16a5233b6e5a145c4358b390c305bc4bf585f864f68333dd12d4139a69789105a109e92cc0cf1ff8fe2527891dab4b4fa8731f457574e39f8687fb4969dee7e3af27889590cf8d74415c9e9c0c6867bf0c5146e7c32e306ec7c7055557a0ff738b7e700a70d3e33a975f7",
    .expect = "9cd24a0efa26c107738f5335526b57d8c93e54fef8c1babbbbb2d42f3a1d03c6"
},
{
    .testname = "NIST CAVP [L=32]: 125",
    .key = "76d8e0342011d2bca953b26ee200e56685b721d50ed4dda7cd3a05633a50f153884998e67da901528004fb7df4090e1ec4c0b11f3f10bd4727842215044fd9ef",
    .input = "0ebaefd2153de2c70537ceb27e5ee70105ae85bd4da38462b4abebed11dbcd36ade16d808f3aa54ffda5897a3fd74780a670521fcd2ebf231f60ef7d999e6e94d1b81be038ec89b49c5ca65bf1bf9a675056f2464021fe16355477ba5605652e8327401797bb569fea456c7f1b7da85d0c48af592de60ae3fe6dcecfcf767cab",
    .expect = "1cbd4f923d683ca38aca6cd0ad81151062fd642b155b2a950eb551ca8216b0ca"
},
{
    .testname = "NIST CAVP [L=32]: 126",
    .key = "731ec9f365f28f1cb9c4ebcf89d8648732a6dfa958d2c0152b5e52fae81f69eea26d463e421fba82cdb78f75e5d92304930256a54376a6ea107a995642c45c6f",
    .input = "d98557504a21fc3a434c780c328ec239cf8d7c26f58d6ad7b23329c79a8e1e176058aceba778aa1215cc14e5a92600714f94d4d8b2e5b7f45268453ed6f787eea3342264ad13cec78d990aecd5e30f79a069024a6d846d132d2ef0479a093439cba4218205f951a2d53ac4ea5bcdd599e9956c45cd73767c6a0c92ac8ecd0d40",
    .expect = "4f2501d2a88cb13046a6549f90e4ea924773408bb684025b5126a8fc21f48670"
},
{
    .testname = "NIST CAVP [L=32]: 127",
    .key = "cc38826523a9097e0f7d075a3a039a70ca1e2b5590a6443e820ba1c16c3b89dbe2c65f37794074ad37e81f0a4786100ff19ae1bccab2eece281c6786d9bda3ac",
    .input = "6e09febed308baa41a8b6e0f7fab61808c9c8471ea32eef178a4888e9a910a77d44026e2972c02ac5ac0ec3fed5f4ab90aa7cf4b2ef7f5dea62ea7fdedb63def35c2ae2344d301d2818105df4f78420299c12f25ae43a60e5089943f07c5f51abc15004982069e5db75721b54cff33a261700cc8151ee9c89c3bb91c92c51942",
    .expect = "83b1403389173568588e5b6b8cf9da180408c79f91d054ac5cd99de0b728ff66"
},
{
    .testname = "NIST CAVP [L=32]: 128",
    .key = "62c1d149567f05a0b76c4fd32d1f365d170cb165cfb38f922f1716225472eb36a127327007f8f5c08479ca7beac4b0aee26f3bb130bbf1ff390ef344c2a4e0b8",
    .input = "7af390cc4edde0f3d496137d0cacd0876b54c909dc5ce36705619742cb42989418d4b6fcdbd80256512a338f843b48b711c06f582dac2607ea5ca038b7126a5726a54e14f37778fe41a6d7532687c6166a50ec638c14600006f51134d29566dc2dcd21bb9ba289122b74c870fc7992cc006a07d1007cdb79e192b4dd25b1d34c",
    .expect = "2f1a4c2bde7c8bdd7d8a9b6315b19ac654266120c652fc24ab19e00ac11c5461"
},
{
    .testname = "NIST CAVP [L=32]: 129",
    .key = "af81e327525f3a9104b7282959a0f6600fad7efae7709bb8b33cde34b12f830c1770a342efb6abe3250a0ce7dfcd34590cfcbeb840b3e59cbff03f9cd89aa870",
    .input = "75ed3ae9085bbf2d034b864d7f87057c2d0b12c7395feb0375237903b3ebd60e724e0c8fbe3a200f518a4f61fedb971c509b794f6e62fe6f4186f894d9ea8ae50d16ea51628d66812f5aa50afeed30e634253025f5ae7ae0428dc86f64f949db8e6d5d96befb996ae4e312b04664d8c223d2c0b396e9673dbe6173fa1cc21cd7",
    .expect = "579d35cef5b6f8468c8285829861e93587c8dee5791208406a7f4bfafb70abfd"
},
{
    .testname = "NIST CAVP [L=32]: 130",
    .key = "17a5baecf916634433dcf133ddc2dcdfcf4a680e088928985138c01d1d09eef3b437cc6290614f14079814c72bb75c45eff255968bb29b7421a1feffa00086b2",
    .input = "7809e59ad48aeb2c6f03de775b1371b7f86926ae0b87098e10c69e19d29b18073818cba862b6e4caf45158ddb2741a554ed791507d2649795004e92cc25065db8ea774b0432a457399816daf062025108dc8b210d75124d284a8434ec314c7af20bdc7f99e6e74ef069a07347e9df8b05d4571353e91026354b896c9fd6da64c",
    .expect = "810d7bda3421589a7dd60597447edf2b987f1e7283f3c65890248712c80969c1"
},
{
    .testname = "NIST CAVP [L=32]: 131",
    .key = "e09ad7d2ff8d559a26e0454bcbfff844e8d2415b07872bc59c93e73698f308483bb8f3212ac29050c1cc46f9aaa92732afcc67accc0e139689acffbe878f01fa",
    .input = "4745100cec0406cffa146350ee12213330d192123af4a1bafdbc5c98801eaf6ecb19724a0346a7b9d6b1fc381ae798ebb0501392afbfc6b8be48462dc2522bb7baec1605e665f2e42f1679b6c383fa1f00a35a01937b5aabe1f2174da6e0d7afdb680223de886fb9cdeee1b1320dd236e6716f492f4fe3fb2c61d8df73f03bbf",
    .expect = "055ee0ade716231bcaa0a7d18161004127a37e7aa12773433a376073474d3d58"
},
{
    .testname = "NIST CAVP [L=32]: 132",
    .key = "fd013d615c6ca959030a520e148808a07e27d38a215634d53486ae8be43a856f3e5dc6eb4fd9874a8a6570276a9e7b25585af7e1ce39d325bd7d195f2c1bb951",
    .input = "91ea78334108ce6261ddee5d98045bb307a6e8f3d0ee65c1d9bc7d28cd9edf3264fc9cb6e592d072e9238559616cd42eda584d5200729adb619f5ee5740d632dda67f5dce34b89a054fda301685df6f31416cca78f19a8a7124a2a22dd7834847a934b4a451940152cd20ffdb4bd07273c4a2b9a86c9d94e7323a9860ec89860",
    .expect = "eb5aaa4ee702ff7b5324bc72c98fe87df6d9cc342b053ebce6cbf27fdea0eabf"
},
{
    .testname = "NIST CAVP [L=32]: 133",
    .key = "62e3a735edcd87fca0dd1d2797cc0e574160da9ac23f60e39501a5b77688d1287f947a0791922556f5b50afc434818bc83433968931cd752c9df9f04d8818531",
    .input = "ec638734d336b8da6dfaf3da9e18c7131494fcc0709cd3a9a6618e9ba62533153c958e44345a7531c3eb503a22a5d8bf7c1d1e1d0ab5cfe07d6db7349cfc859d2e20cee81a325462cdfd8747dcd04c7dead2fe82cd96b2a4ecefc070eb067f6c8ba94f09cbe6ddd354d9a2eb13c2adb7285aa3d8ff68045cbc8faf35dd6aa9ea",
    .expect = "26db47a48a10b9b0b697b793f5c0231aa35fe192c9d063d7b03a55e3c302850a"
},
{
    .testname = "NIST CAVP [L=32]: 134",
    .key = "abc9ccdfbd92b6919a5d6c6b5a765a39662ed90080d3549204dfaa5f6d70d48e1af8c84d53369d658765ef11d7b38510d9f431f99598f8cfd4da73d59b3b75a3",
    .input = "ac4756b851fc8866b9adfac2d02599148e0db7757a62b1e06d26cf8c99556b79c91a5649ea437752cbf3b5f121961821ce1a2a4c635da461e3e14626cac707d04dfb6ed1e4ac40f106ff5ba03304e28a38e99a6daf6d9427c5980d1440a99296c05168f5441e2a6af13ab4760f55407855e0cf7f667ccb5d9bb2eafd03e455f6",
    .expect = "0e445d77789a6947da70848dc4da5dc9c125869bb6945b04304bde93829a75d9"
},
{
    .testname = "NIST CAVP [L=32]: 135",
    .key = "07c358ed1df3b06d47b5ec763afa07a6677ca3a722524e6103c1056d8c56f6cd0d318adbc5a4a3804afd23a62b9fadf0d358afa8b0eea0f995fb865e5dfbbc5ad2a4f26acd76",
    .input = "2aa1d94ec83ce7c3c75c6bc847759b085234fd44b407d8f80ddfe93c243556e87e4be8fb30b4743ef1169a24732fb2f5f416042b10c3371dd9d20dda29844d58370700ce69f7df5e69240df77b96027a0ecec71b904f690b875da854de05ef047c5d898d1c0d116c580e2a0906b271dec8e5b0dcdfb2550a40092270eabf2533",
    .expect = "b3a189a17e8d9e986cd31bbe01b49fb3"
},
{
    .testname = "NIST CAVP [L=32]: 136",
    .key = "ab8dfba4414e6986513a9767af5eaed9720811c4b38040b991f3fd8278b0adfea497002ce0cdd48594b5578ffe1c6cafc0b4513e9bc47ee07a1dd011b250e601881ecca2f430",
    .input = "d1a7086d134c11a8a3204e019f52843e89f2d01a02a88a94d4a66e8d36dbfe924c6922f7ee5a1225aa8e75340cf8cbbd1c0b08e9296e81cec5f70cfc11d763523b12ca174433f246073d1c2877e4812828fdf2e41134bc8090fdce3faecd1e54a58948f59f3f78b2c1148b05687d712ab2b2d630416001513b9efc7f9523f53f",
    .expect = "7aea0e2d93e9a6a3004117ad4a4a72a3"
},
{
    .testname = "NIST CAVP [L=32]: 137",
    .key = "fc68be1e46a7ed0d4293c6ebab8d7546a7b6e95d495f7d315ac1d8df59ee112cc008176289b1515bf1c281db7c40ee23398cc2c247d9b1af98e3db95f5dff46e42ada2530455",
    .input = "eefa0d62254597bd67c87e00fb35f69c5cb2dc09f58d9d14292b547b964232b79b482319172cae1874431deae585df51ebf92ab81e6ee57e2a6cc492186ab540cf417b4adae1983b6b4371f8a09fad9806dede755c52638399a58de1300f00ae92cc5c1ef4ce1dcd53afc053b6e92818b4493f6a35a1e0cc7dbef5916699dcaa",
    .expect = "04c8f6ebcbf13fdd2ab1e5c5c25bc7ec"
},
{
    .testname = "NIST CAVP [L=32]: 138",
    .key = "6e9ce34b4fbc78ea92d3d14592e1c0725bd053d70f4c599b89d4215a3f11851d6d67278970cbfb566fd40603411465c88ba890cd290ee099d0374fcdf1dd8012e017ff50352b",
    .input = "56dc2b84da28f94847f598980ebc2d5892274e1639d0b7ecc24c3ea8d968092be8b2fe0f313c7b8d1a9c479dc737c95eeec078b9e7fb934103c7125e1f5bdcab79d03a9cc2e08c6474ed3b166544ee0a9da4018264fa338da06f9e2c5ea4edb4af3cc973b59c9496fdee5a4a0f6c042244dbcfb9d855fd98404ccb5abecca20e",
    .expect = "c7e82b7b2478c319194fed944fb7c772"
},
{
    .testname = "NIST CAVP [L=32]: 139",
    .key = "91e87e19a4a4af9b2068f842e624da9a21e57c40cc4d4df57541ebf140e144792ebdfbb49f450dbb1682b4ef3d048b8f291cf38ade4bb69116f9eb713e6a1aa0c2efa0158a59",
    .input = "3a51f6fbfef38724347ab1a4f7aafb7a999aee9b890a19e87af6585dc16c568bff9a5148012b1da5e4d46c207d294c1bf8b6f18dbe4bb5f89d975d9b23f89ee84a92e0385b9f41be0c05ddb9eb2e4dee00146d56ae9b6214db24dca9515f996b63602b34d3f6fa57f3388cd80b6004dcfbdde95e21a329247dc65ef113474ffd",
    .expect = "589afd7086a58d77f046c59a419504a1"
},
{
    .testname = "NIST CAVP [L=32]: 140",
    .key = "1abf71698a7d52b41caa5c26558d46e8cf27a490d270168c23e4c0c4213efa7b0d844876aa438c61061c7a6e977f4d3f89b7b806572720eb99d308ae1d22cd8d38e293685e8c",
    .input = "aa02f0b377f161ee60b0fbd6c56a537c0358cb8da62b63d5daaad203239cd6ac4ee8c892a8fb73256d6a264a83d8085c681bac706a9ae5de16f9dcfdf2f95f2d6f997c1b19824f4011a118abbd169001be4d7ec2226a85cddbeb4027708891f8f35e35d6334d9c46329ff880daea9573eb3768093863eaac13c6270906131114",
    .expect = "8cbd8f921c55d36e5b7db27f7891def1"
},
{
    .testname = "NIST CAVP [L=32]: 141",
    .key = "f8dff7f41b7e3ef6d558dcd83d344db5551d410eecb5a0bcc2cccb29ee3125c07dc8d2a25cddbe9b78b8e1542372c2caba073afe84ab7befde6250c595cba74f943c4cafbf14",
    .input = "72d18951da90b1f6d908253e55da1b5b476d6a936cd6e4433efce72422f92fcde3c3ee795f0b1f0b8065174f6eaa5d83039abb1680c695af7eae7a712726f97ea5feb6b9dbe1bdd1537e157b78e699fe063503f5be754a505ebf2e9dd0a31086a2cb089ab6da32503b9a4848db5776d5368669b990abaa2fc6792a2f873a1eed",
    .expect = "1c649a21afe336c72c4593cb3d3c9462"
},
{
    .testname = "NIST CAVP [L=32]: 142",
    .key = "9fb4d6fcd697d4522dc7e386ab41dd9f8a637906e0fe123b7facabc719643172a84bffb50ccda872f6edf0e306d91bd130c26b0664eae4046eff52f71ba78de99d5cfc35307a",
    .input = "eb6b60d0858d6f87f5b9ba7fc75acba8751784ef886061700047fde7f692d868800e5751d5260c7cb1b338b9fb168e7ba6853ad1d5a2229842526cf0e0cc40ecbff0cf8e30db94f22bb8d9c9edd87e09e506f6e3d11492f625ba02c2aca1195f71bad06ee0d48e51296ea697e5c921bafc42bf0dc6df38f07028c746a238e929",
    .expect = "9ca6f24c476e59b5b068c37b0383ff4b"
},
{
    .testname = "NIST CAVP [L=32]: 143",
    .key = "ce3a2bec5ca00b544e8d392ed309e9ee5d48d185eddd8b33902a3b9d291b711f721451633e27f133018b028b9149b3f32e39d20bc12d3468616c589e1b62479ef395be4326db",
    .input = "36b5cf31af37c90334f2f4adf6a918a22eff5e3e54dc1a4f9212e8d47841fa05f1f8b093761c6930818e9a5245081d349c48cb1e41714ce73fae2eb8a91835128cdaf213229297f548fb0ad732ca38c05ed5ace1c67a601a5a3fd3c0adb65b9eefa4bd391b61fb5971826dc427b6134d5cee2a0d4dc1fdf1cb0efe75ede315ae",
    .expect = "48fc1d0123e5c7f686d74f5903323f9b"
},
{
    .testname = "NIST CAVP [L=32]: 144",
    .key = "b127e4819e172ca09868c28636dfa63b2eefd1ead22dd3f0db04bb3366aa37b53c52fc6956a46845a16a6698fe8c939e8d3e9f512b78f58339a69e2aa0a262fb11df313a92e7",
    .input = "f1ab8fda839d00f0477d1ab6f3badd421834fa89a4ab8075ab77b738677a4cdf7d54af2a81d5ba9bbdb893cd2e8ed307d0f8e8111c19b846ce4b86ebeb111abf034e1cd3b3b4c29c6f7eab477e620a4c46c10646ca22610271de58d6091ccb340b009e7e21205f1ce53829cdec1ec83a03f81dd1b8acc4d01d98f5a0c884a865",
    .expect = "41fe6d923bfb13fcec839d3c272383a6"
},
{
    .testname = "NIST CAVP [L=32]: 145",
    .key = "a04b6205d7e712aff28a8d520a79547e41e42800001970b383f8dc9998a7482aa387e3ece6669044fff68c8cb27d5165e9cfbb4ff97a6a77274067cf6bca0a64749a1bedeb42",
    .input = "6bfdc8539fe6bf99892c1c36d521f7b17c224ee3837755fee57a0dcecefb183e09e4cc1dbc19862253a2412eba0c67d2cf0ce61117668767af0d7c0a868c376fcaa48310a037cd6d1865c25060f4205638f5c5aba5a40d15ea915a34b4fdf408958714b3b3083b80c2bbc8252fa1ca459e23133997fa8e107c4cd2d4bf17f60f",
    .expect = "b6aa4e0beccfdd37588699435e2d40de"
},
{
    .testname = "NIST CAVP [L=32]: 146",
    .key = "beeba7959995358a1c238dc2f457f3c0aa6f47372f5f3471b85fabf1cba590589a74b385915501002ba5fc99094f684c45db476804a808f14a75fc42132609f69fc5a2090dc8",
    .input = "b551096a194aee8992991325de92c9597c4d1c156c57b47036a7f93f2dd47be6f585906e43283fd8e4e75cb101d7f5e7a173eddb6f4ae7b7bef46502ca4a317240d7fd010189464223ac7ef6391969dbd5abc8c44bf335eeb72d4e92417215b79f2f974adcd5cc7058d2bf1b11c1eedc20ddf4f887bc65bd293afa161ab3ee5e",
    .expect = "98323e25ea0635d6abe384e8960f373c"
},
{
    .testname = "NIST CAVP [L=32]: 147",
    .key = "e7747f39b1c6c0157a9128c012391e5148200ed5006a193986040a6a22e48cbaed929b86e2e73915381462c4f0e74160aa4aa4d4bc0dae0485e5cbf8ffb4e93d940ae68833ec",
    .input = "868bf010b6e26e4c1f91f0614ff42bc1403087c33b7e229af6c718880072024f5e7abce977c36c782daebf804deb7654298e22ce83652b43ad8917b6ef34094c29d28800b95b82989fdf91d8df637cf527eb014db2d8d2546c74ddd257ccd04c2dbeddbf4752bb95bd4eedd1cf04468d846fada6907e1eb67bb0f14200e15f35",
    .expect = "591d11b2bd18f982bccb6b3a44f760a3"
},
{
    .testname = "NIST CAVP [L=32]: 148",
    .key = "2f95c1d1d94db8ce7bdafc8af1b7e48fefd96b7ae8f733f72f29caed5db42df6f2248a123f9c4a9c836b4f7d54df7a9f405e71a5b5b29fd91ea57c654fce0ec723aab07f63ef",
    .input = "852f420342b4bead2e714424eb0f287f077602047f40553d816d6e4e76588f8540e94d33c00d37ba9c63b8e83f393f8321b69c254858ae4a0fa23ba8260e1fbfda49a9b0969f4252aab44f834c7659bcdc4f6be96d9fbc7780698eae124d5641dab61d23cc54269de1cdd19e1aafbf52c3aa37f5f5fcc9ea5e2c310744fb7e34",
    .expect = "3d4a25554afa0abd26f72377c7180e19"
},
{
    .testname = "NIST CAVP [L=32]: 149",
    .key = "addfd600416f8511f3f07b03df2248b6bcec047003f49317546c26a4172f05d45f0c8d20136174f04fec550c08df6853ef3290af983d9c48dc86c6f87cd88000069571f9fd4c",
    .input = "01c6d5c0272b631c3f9d1c0687f7c1496e77e1479bb9fc8f31e6e8b252297453e2624c7e8d1f1c3b0bc8f862a219fcb0edd52f1bddb9ad63fdaf06eafa45e1c5625de513ac26d98d794b095f196aec3751c7059b5b42077f2f863c17018427ea0b2069288c29e13d118f17a6f3d0db0321b4296e1f3a500c4fd253e170cc90e9",
    .expect = "2d2ac1291e545de46a42ce6c435518f8"
},
{
    .testname = "NIST CAVP [L=32]: 150",
    .key = "058f604e53051a0f8550de16b7245fdad3da639a6cc3c84eeabcc5dde8027390da488cc7f30772eb461673a32b7a4b4be47feaa2800878c200239756b9e0e807f964d037ed39",
    .input = "a74100cf30cd26416e9878739dfdb3c1fa569d6427ca8ee9d06630e18f6f83db0df7248f6bafce5ce0fc21f5a34da2570bab04fef492a65866ff5c7a71ca72125b36ee9cfec716d96b53327dd35c9328a89dd498ffe3601d391e344de2b8e7f8d925e75fb1bc05a058c53475f6d38d1e1854979c0e66c62091ec41c3aae1e877",
    .expect = "08e3a1718c6d1cdef2c0c67660f7c1e8a45963e5ffed54a7"
},
{
    .testname = "NIST CAVP [L=32]: 151",
    .key = "986e0d3c3e7645e493d35962291d979ddf09e8a610d5a73d0ae7b397c2b1c35ec6d7fafa7294bc0f675abf4639b8655168814929922b179ae675a202dc4c305623f01865db53",
    .input = "72c21be6f0c4df7cc8a53f9226f36146f9ec5bea9c94f3b7b604a8bf5f05f72484ddd7888c6986c43b6c87ddd727ec348a2ad1fc086929f17192bd47799e71e1c6a7c9c49af9adcbb16b699c6df0f8da3069829d09bd231f942ceeb81be0320c01c5fb83619bdcf9f24aecb72e750fa2b35177b3e9b86aa7e57945f88df3c10b",
    .expect = "b579eaf7706976152b1622c17fc47c5db3802aa3f46f6a3e"
},
{
    .testname = "NIST CAVP [L=32]: 152",
    .key = "7a41ca8776a3dde0f5c7d029f28a9bcd3c4daad2ccf9d604563f95501e256d6e0dbeafc304386185701d7c201fd258d8526464b013831a8bc8cf3292095316d5af4f97352d3b",
    .input = "c7627c9a6d1e7c41c18657b598ac29b28c4d0ef047008af7feb329353b58624ee0dcc1b369594676718c085d77891d35e3adbe6844d5a7d2dccdbdd15e0cf39bf69e6ed58a61e8614074527740edbdf7bbca7afd2c2b80b6ddbe0f73ad7a93fc1290cb275a9e2aa936267e2b7840cfa11c8b8ad78569df4c0a6c6744b10b0a19",
    .expect = "53f3436a128fd497c5cd1a534558d6a6bdb5f086efabc6fc"
},
{
    .testname = "NIST CAVP [L=32]: 153",
    .key = "ee36e5784fcb43427be072aaa968ea52bf3b73f55d0b45fb1d996d4a1928725eae32399c805b26e3bea38465a8df27b54e6a4f209a18d041906b70d0d50a91bb6e6e1078cbdf",
    .input = "8419330710968fb40ae915e66548f1ac445509e361f583abaf5f87173e7346295f4e3bfd0a1bb0447c2b85f424492d3ec047f9c1c4dd99fdfbb4e00a70bdc7898fc7b5dc8851fd92f49ca825bb0576e835921f3b8fcbde0171cb3054dd96da775bad290b53e07d86ba6409e2f025d492e95d03ba8c665b9f58cd025d4da785d8",
    .expect = "5a841e55fb2250c431fa397f1d0ec858b2c4a08e40dc897c"
},
{
    .testname = "NIST CAVP [L=32]: 154",
    .key = "27e1dca4978d2a05d3f9cabc29cb18c76a210b4eee825d37d915ecf59d1061a0c0740f4be0f81e92f442e872d45da35efc68418e8c8b949b9430b6498f6fa8a32dc9394e561a",
    .input = "57d73f3bdcaadf51fd61aa65a01dc75638546dccdd899a1da25a086d23c05d1a5d93a157c34cf6168e0f832c54e9b2afdc569ba33106c0d6f5e0fa09f848b350099d56bc0c0604364d6f89ae14ce8e767aab0fe87adf104f4b9c8c05edadafd803ff45b2e061717ae488a2350956c371b95cb2e3e39df44f4d94a7a82c79b779",
    .expect = "dbeefbe2f550671d7fcd3d5bd66d19ce9faf5e6b29308ef8"
},
{
    .testname = "NIST CAVP [L=32]: 155",
    .key = "b415314e151701a503b62a5c8b5dba5ac357235a533fe2f634b85f04b85f1426cbfef29d7803005eaf3046684593e9543cb9972e451f258383e977bb92d6a1a9c8744b61ba90",
    .input = "0c8404fe10870fdac0e8d21c99c73d04a78b6d4c8fd3cfb8d3ae87ee520e13880e7a2b683204ec4b547b36a1f7e1539d541fd9885af8d15af33c188b893e0627c9874e21a6cc25e9a11ea7404861764cfdffa4e7f9ded33d918f9a96b7c82b70c31433d174c902db313aeca1952fef392b929613766b1c88350fd5b6e493ca8c",
    .expect = "95beb7fcb2b8d049adef7e0f33a7792c8d71e10b71ad3efa"
},
{
    .testname = "NIST CAVP [L=32]: 156",
    .key = "e04e9731742a767445247fba9701ae17fc9acc451b8c4ff3af307c5fd3cece277c0d9b5d47aef5d9757acfd3337960b11f65cd1d095e025bf6dfe0d96bf19e08e89f696bb2a9",
    .input = "fe1c33cadec693cfa53250d906d35d1e2db8df4300be8f2aa505600b44a063c60e91e7777ef4e44bde7a9a930e197517810234ad88d44a0ad30f84d734cbed08a7aaef69900bba794380ea7cc98363cce264807046866eef30cbd2661d4db2d9d14f92c79c73dd01db2d87bcc177f1e458c60db3c23dc283c52192e0878e7ae2",
    .expect = "2f8d11fe7f6c07bdd0d33dfcb3fc7dec51fe2048d1e8db44"
},
{
    .testname = "NIST CAVP [L=32]: 157",
    .key = "bc3732e901768fc9b98303d599110be8236c5151780022796d1b22c6e0f43fbe4debe3709c126e0f3dede3e17776e157fd64d67ec3ad6f960f4a53ffd33a105d3ac955f48112",
    .input = "023004dff89f0820892be15fb91dc4c498936bfab92320eee6c117d412e3006c8fe3dd8382a411bc9378ba90e941419455d730facdaa435b1da9c1b4d9620cae966a772259ff59dc50ec609fc0ad276a3fd40afa23ab39903a1b0bf4bccc95ba7d8e7cc467f80708284e789328a89dcebe51a201a36e2915a7e09c9ea26bc219",
    .expect = "f51032cef423d7846270d8bb43f7d8426e392fd92b753a57"
},
{
    .testname = "NIST CAVP [L=32]: 158",
    .key = "d2229832e4000614fac6db5c0a235e49217fa4a9a831f9aae7f282eec79120dddce9963fa211ef0a07d21a782a5ed85d633ed8b8838d1f885d64aee185955f3e579c11193bd2",
    .input = "0d612e1953e7cfde5242fae7d51c8152d2a4a7e44de128fb7a467ac4228653ae47aa6b1f0b608365ce96a6ef9747afbdb5950b15a619c0783777aed4ed3515fba4cd5854760001d0de6e04201d644826ddf563a9154ca64c2c4059c16129473a6af27e205b705008caf29de3311a557493eb38086322e061a1ca02f3460bf153",
    .expect = "a87d01c705415dea8cb9f0e2b6663b629f88a5ce793ea8a3"
},
{
    .testname = "NIST CAVP [L=32]: 159",
    .key = "043899af301424ed13d00066c0c37a448591f27371a284b314d2e7ec866a94c1ab502b67b47a13b8e9a86183a653fc27a4e0fe607a1a5d6064dfca224219d9fbe4f16372843f",
    .input = "62908131c688711835177348434fdd1016941788765b50752430716e6dfe4f3dfe8b2588fa4241b14a35fdfa3562f1ed303567fbf74f0f63dc86f5555f2daf570095dbe951d3c9644fc47428f24fb7f603eabd9b2e60bacf58d1d85c33fa75830fb68b9bf3c56ffbeccdbf1aa59e95f538ba01b14415b782401904cb0eed0787",
    .expect = "97f3b4e61b5885dc4c7f69f79a07d7a40c2d1d2e3936b91b"
},
{
    .testname = "NIST CAVP [L=32]: 160",
    .key = "b5fee466f106d7a526d468468a16981251815a022073a402c4d7c5f6244af9fb747b3befacd85a3339674faff2f1ce174d661b6dd37d1fc8d19bbb5351f65c9848fad0ff11ec",
    .input = "4745100cec0406cffa146350ee12213330d192123af4a1bafdbc5c98801eaf6ecb19724a0346a7b9d6b1fc381ae798ebb0501392afbfc6b8be48462dc2522bb7baec1605e665f2e42f1679b6c383fa1f00a35a01937b5aabe1f2174da6e0d7afdb680223de886fb9cdeee1b1320dd236e6716f492f4fe3fb2c61d8df73f03bbf",
    .expect = "1fc68ed1bad0898d691970b530b54cef7c2733a7f1ffd276"
},
{
    .testname = "NIST CAVP [L=32]: 161",
    .key = "fd013d615c6ca959030a520e148808a07e27d38a215634d53486ae8be43a856f3e5dc6eb4fd9874a8a6570276a9e7b25585af7e1ce39d325bd7d195f2c1bb95122118809c7fb",
    .input = "fc0723c3f84de1178d14375c3307f0babdbb2086813f6970b8f477fe289ecd3900bcc4a60315d077e89406030155db741c002fbfa7568ada1709a5298ad12c39aabcc2b0d5c646847ca9546cc9f60f9485651e953869f5a49208560909ea17d4c4b025cbb887c9a611fc2a7fd3121484c191f7ef7ea23338f2999288ef121672",
    .expect = "10ab06d732cdf46a1711dfab98e136c4e6ed856ea0678efd"
},
{
    .testname = "NIST CAVP [L=32]: 162",
    .key = "05915a68f16938d7c6c5d4326904e0f3b89acf4d7063e01a4e38581575bf0e4910872dc9385436a218b7440e4fe294ea95bb446aa22f5b0c4cc90acaef83329411dc25fd462a",
    .input = "5a40298e323ce97549d4c820b0a77cbdefeaf6ca9bad947a2b60985a0795d934e208b8334adc56497d2704ce7fb1fb6a69f94e3404791c1b962b0a86fc4cf037f960d375ce76146a0bade6caa4f705b5471da6dfed04a9eeb02e1623dc83c73d4852629ae7938ba09a6f575b48020367315fe6117fd4a4b91e70a57bcec3c50e",
    .expect = "aaf4fc8d00177a99d1c895d72b3a63e7ce15f1bc3946f338"
},
{
    .testname = "NIST CAVP [L=32]: 163",
    .key = "b05f0e3bbb12b9351c465ad5eff31e65e55956c5f4e4ca684d53509f8f199d1a3a035aab661c7b4eb5cecc678649cc4a6b29bf00de52ff492f1f93ddc1bd02f776d169146861",
    .input = "99958aa459604657c7bf6e4cdfcc8785f0abf06ffe636b5b64ecd931bd8a456305592421fc28dbcccb8a82acea2be8e54161d7a78e0399a6067ebaca3f2510274dc9f92f2c8ae4265eec13d7d42e9f8612d7bc258f913ecb5a3a5c610339b49fb90e9037b02d684fc60da835657cb24eab352750c8b463b1a8494660d36c3ab2",
    .expect = "edfc7a2815d6779681590f3855e668f2c2d44e64c773e711"
},
{
    .testname = "NIST CAVP [L=32]: 164",
    .key = "3714707839daf79122c782416351385e88a81d31c9f641d8dce538e90e63c95892a2ea9b1962ed0ba372f48e9474aa730ae2359d6e4e66e449ee33b859576807e58999614d2c",
    .input = "aac4256339f6377a4fe225d50e74424c80e0f96d85d162c410c3135a93ad397bb8e4e7bc523cad3d93706d2c7fc46a8aa0e8a232fc205e1744a207cd4e3f3b4bc54620ef20a6f8c2d052f6febeea50cdf49796549a3742f025ba90bfcbcb90633ab37902897b40916f516953b32e1e9ce3b57edb495d37d71bd25739f2995f4b",
    .expect = "ac38d22527983468cc48efbf64cbe1307022327207fb7f94"
},
{
    .testname = "NIST CAVP [L=32]: 165",
    .key = "c09e29071c405d5e820d345a46dbbf1e0f8202e92de3ed3e2d298e43aa4f846866e3b748990946d488c2c1ae5a6e99d32790d47d53d205481a497c936bf9ba29fa9c2821919f",
    .input = "ea7240529980076d3b028a083ebc4e24efdaa06c9c84d76bf5b2d9fdb842e1038e487f5b30a5e010cddb4fcdb01ffc981eb0fcbc7d689207bc90ad36eef9b1ae38487a6dee929f3ff929f3357cb55253b7869a892b28f7e5fe386406a2776ed4b21d3b6e1c70cc6485947f27e9a5d8bd820380b9eced8e6b865206541be39fdc",
    .expect = "49ae1c4a7a570fde47f7517ab18898b1b991d03cfcf8c45bb3615b5f755da682"
},
{
    .testname = "NIST CAVP [L=32]: 166",
    .key = "bce50cdfff843885d4f364d69f93bf58a2322c707b82e878eec96d11e5db97bbb54606a3a3ccc3bba716261070a6f759a70ed3cb785fd1354fe56648df11863669b70c803b7a",
    .input = "93b7ef0e470ddfac6aef93c0dcd37b8f1c4baf5eadd978e3bf0512fa0baeb099ff9ec1061b6172479b5674db5606ffa7e6b5173309370e1647054aafd5904816bad5e1523032cccd4d786505e241ac83a484911189666f287553d6a8164e8dcb0c85d75c4e29f624c97ceea64a2c8b0c9ddfa560f70fa3ff91183e4b968f88a1",
    .expect = "37f9f32918308210849dfebf8dd456804babd6845af07218f9d9be9df9743d55"
},
{
    .testname = "NIST CAVP [L=32]: 167",
    .key = "0cb35a02ddc8c7fb7c93aeab77b9318118b0fd449524209d879a1cd69d5439e192741f9c5c64a353a774e28681c58ced576783ba20bea51ed82ae50e30e6a147843130900dac",
    .input = "21063443bf02ffe9f813dc6688920d036041a2a3a63a9956fc254a2c05ae03472537ef3489c93c7c68517c7588094c5e033434ab4b0ecf9e6c032c17911f73adcac6ccfd0ca57c427ae85127e2ad41d98bb94e5f2e6aad2e42ed26f87cb1bec6971c9446517c0966b6402321a06834997f3ab66756377a2f064d0277cf4e2bb9",
    .expect = "5c258ba6241f65c2ee5356bb47332236baea227857e29506165861a4c7379c51"
},
{
    .testname = "NIST CAVP [L=32]: 168",
    .key = "cddf76f985d6797c9fe3830c210567c5094fb979343fd5a1804c239a2ebe9a0e8ac283b0cdbe802c42e2cc5da800c4c1d89da72ba7489ab80e2aef0488dfa69ebc8434b95c11",
    .input = "9724c0d5c989e5adafcd7527fee269ea14c0aec3ddb62596f3fdee9b0993e6c689466e877c0f6fb4aba29bc40343f53d3edb936fc04ba263bf00ac0fa7c816cbbde4ed09025ee2405a9d9229ed360b2ece058c20db7d8d28e43cff000fe2d5627a24c3c1231c463805e3e4c08462b5a50b65223bf4f1edcda8d872d6078a2c73",
    .expect = "3c5a9ac2a0fa2f58825233ff676bedf93d8845a409a42a05a9ae5218cea14680"
},
{
    .testname = "NIST CAVP [L=32]: 169",
    .key = "731bdc9fb219f3667c9a135ecf34c7f52cf638c39c554f1ef1691ae84e5a71ace915d9e91043a8ae6a7b6a6780b684f77b0417072f7e279d597cfdf02508c97bf4928c505be5",
    .input = "12353bca6b0f3d545ec4b470c69272f72bb5589793e6ca769a226018c5acde83145567a1d6fbede5c150ec3142dc58f81246d4a00acf242a381fe51432447b7eaaf84c8d43222c0da3a0175aca442680a21cbca1d7f70097e82491db7f7d75a5fea552555a8de0122c3d9eb105d1c4d802c17963a1664706d3bacc345360b240",
    .expect = "f15a210fca2cefc4d92bf14ff572d021463bcc28f60d034e87222dc6076eaffe"
},
{
    .testname = "NIST CAVP [L=32]: 170",
    .key = "85806ff2a642f729d28ded0734aef4f6a3f0bb32771e77729b4391cae4b49bd0a15089fe74071e576099a44d22a0e0e3c5d1450f717f68628460b4eae3945f5893e39c5e8347",
    .input = "df073817d8687293257d7ed1816803afe292d779f34e14b0c5ba6e0ac1e6c3b9e239f4f02110f4a430a71e906a3dcc7b0b7325bd9cf63600b25d4544d8556126cafb3e61e4894095d935d647a8560929ccc9559cb393b77472c707fbb7ab8838ff16be71091c7fee8aed4d0022fbe3428f5b0e1f216ebe946dc05d3746305f79",
    .expect = "6c63bed6c6082bfb085cf2426ef3d0dea97acd717a57ff0aa624d0b803f2ea14"
},
{
    .testname = "NIST CAVP [L=32]: 171",
    .key = "f13794e5ea5e27507a7bad638f8eb8b86ca5ad73b5a17424c63c74ef494bbfea084189c6fff5dfb2b6a5967cce3a81f9d9cde7a86f6b33927e15ee74e10beb20344bc121e754",
    .input = "cd3f17355a1e254b9821276141a850f0b71cb3cf4824a803b01c71d8dfc31d31fd33ad1cac1776a98d18c6fd0598caa241a3af21772208d36f5270f4437570f963c8a323dbb41755d948f72369e7672b843eb0a849799d448ab7252e8abb496d05e44074715fd2f6849b02fbf6fdef3488d6fc8b45922fff0832d7af3efc7234",
    .expect = "d08563dad7c32c02b305b87fad504918fd566c433e98a1367a3dbbadb26e9b64"
},
{
    .testname = "NIST CAVP [L=32]: 172",
    .key = "e3d0c3abdef069e6e4fa35015797bd8a9d64bc9b75f20b028b12cca04a4fe80ff1bbbd88e9ef1003564d499fec88df4503671188eec5d7d089dd18b812c41db43a3746f77b97",
    .input = "934dc1ef76993aa82061cf67aaac7714f12e25aa8f6f54840a2ae3d84af32481511d300126db7dc612a5b2ac0fdeb9c47eb316541846781e270c8ee5f6731c2e86c94e4482594c7e75d70ec43bfe7250b6778cb2c2fd3d176abf07ca5c051ffb9a17c4c0735bd059b2bd8db81553c94100412dce73dbcaf63a0af58f63f15571",
    .expect = "5717fc337916d66b4e292e69d507b1c81663d8140536670f3e70e33b04c83ac3"
},
{
    .testname = "NIST CAVP [L=32]: 173",
    .key = "51bbdf37124cee0cd5830e9d8f4b0ecfa44c8b1bb86a6433c18f6ee961ab694d74f93316e5833c44c5e83a039e5d1ed104f246e36e17f4c5445eff423982c883dba9707b68e6",
    .input = "c84394086457d8fa900a57f18ea50a93be16f06fc28b5532de40541da5959bb6d2646ebe7491ef644ee39cb87d1219625b213094a4ed163dd707ef80dfbf9564f38195cdbb657babb4015071d58260c973fb418562fc10d95d67fec8a77f0bddf342121b82f906368b0d7b04df1c682ecd4c2b2b43dfcd6f370888df45fd8689",
    .expect = "3e0212e7982f43fc303d5e8457d2ab630aa257302ac489c74976cc5678823931"
},
{
    .testname = "NIST CAVP [L=32]: 174",
    .key = "e95751c99e14bed0dd9ba102f48e5e440519c53208e03ab7133613dad99042db7239347f5a47f9a8bbcda428ef52f5d7408235e4f3246268864c8c4135d27f1dc302a2d57695",
    .input = "36bda8d33b3bc10f367caf71c5ed387fe5f1493c1d3bd2aaf97ad78cba3cc5704c0c02ed78dec72a5bae329f17639720c8f91817badf7511d99e257c68bca5aef6e0102a8e36f01f2f1553327be0227db32aafd8e31d8d575a1ca4145da7842e1d7ffa11e60be1f898fb3bb15b2b81a08fca370702bbc285663b7edc02c50cf7",
    .expect = "d965907e6d0f926a7ea719464b1034a5879c865a00d4df0342b2d4f4bde0976c"
},
{
    .testname = "NIST CAVP [L=32]: 175",
    .key = "9dd10a4c713776700f7e7e0a710a014b923bf228234daf5e807c8eb3e26cb97fd6c93d6cee2a5d7ab63c2c46e91c5b8be5044fe95d2a76e54ee5dc323412f92f7db6ceb03ee5",
    .input = "3722eaa433830abdbcaa9177e373bab05fcb8fd82fc3afa581e34f08d3c07f5f58d0aeec9d7e71866c7a808ef15301251b470a9c455a612c16a586e8a5f1f3efe184a2e6313bd0a657d901319a9f44eb241db807a9474f3f49cbd2c8b8a225859ce5cd7b36e3af8545701a482780086a42f4a1ffa2b30144e3fd3b9052fc9e87",
    .expect = "9c22961d48d0651bd592fd369129e44822ee22d35c142dcb6b60a725bf177c85"
},
{
    .testname = "NIST CAVP [L=32]: 176",
    .key = "36bbb59925c6432139c7cd1bbc2b1b05c4010e09645f797e230131b2ad3468e7c9f2369b8b4f790dcb14dffcd6a941b262383341c80fd90d6d46fc8a81a25c47edba482c8658",
    .input = "03074e714d5eefdf5b714381d80e694ef37c2647b374d8a38a6dac2a2e1d11dfa43c6de19d8b0e93061563fbdbb46c683cd86f58c284ed981399d4adb457f6731f21ba04168011db366bac3acfc66dc8f3281b7fcde159c5343cd9d98001cd719d3e9ea25e47e1ff13fc87055d4a53b741f592857c94067216dd23763a227e21",
    .expect = "a6109ba372c4564f4ed8c875619ff5bb64d503225197ee9259dd50264eb1f4ea"
},
{
    .testname = "NIST CAVP [L=32]: 177",
    .key = "ffa63ebba8239b6896bbec6af1c7b87b9c69257a0d146c0d5c4e8b8a99b43a18633f1f11b6c745ab05c5cbd8895dd96ad89cd87bb9fee30c373378ecf42274dcc02f3ef06ab9",
    .input = "739f460034249e805aff665d6248a594250695835aa24cfa5d9c9b962f7d374abd0d163f65c51cdeb687f72b778d4854eba00389548a180fb6cd5390dd9580b6a1ecd4f8692d88b3eebbc77c42f2cab5105e425e252bf62e2fddade2c5424ed6a8a446d249422a268b029df9c96075de1baa19a8d56f2d8051357234ef6ae7d2",
    .expect = "c580c8e0f6a1f36403322f7b0ae3d06dd2dfd16ebc6dddd205704e97dc2998e2"
},
{
    .testname = "NIST CAVP [L=32]: 178",
    .key = "30be326c2ffff6d031affdab0a27d5a8cbfc4ba9dec626ad522615f77307e56d9e23f73e53c9f2c78cdeb5b84d2390727db5b3b4f4dae677d5fa7b161eec81b27d743bd56609",
    .input = "082e7b4cde8914bf07c288441be643e408f6cb5ca932f67e9b975bd54ca706885468708009afaecd4d9ee846ab6c0d70a364c5a24131a766f558ad219e06e4f7e80c68e9d8289040a586662fca865ab459c037bf92465596b4281178133e7a806b214dcd747b24e0b681ea459fbd9276d31108fcc3f968d781106f20d3d62fed",
    .expect = "a51f5988a8f0f3992f549ea7f8c370a06d5ae8d65880067997536385d632b206"
},
{
    .testname = "NIST CAVP [L=32]: 179",
    .key = "19fb88775a517bfedeb2cde7c9455ca58d40d150b0a47ffbd0288e42e4725822c48d130eec98b13e7cbb044b846026f97f9f18531df9a9fe464a99c75bf9ff7ebf72e80796d6",
    .input = "892525a0f02aae7f2264cb024632f11e8adbdbecb7d0c7080832e2373c94014cea02914c1542d1d000593fab43524fcd1f3a63670f6ff8509f1b1da881fb2abbde65ae27ea89a942bbf7fcb65b611d6e1ca20fb62b00929d68ae979e7595f6800d55637b98869f9cfc43eb6bb5e9c2ca281cc720340bfdb70bf5366340edce65",
    .expect = "974752b18d0dcbf29cc6104295e041259622cb7733cff63dbcf6808b15a5ad45"
},
{
    .testname = "NIST CAVP [L=32]: 180",
    .key = "815c2a911aaf0f8498706110a95e6f9c26c3ef52a3b13781448cb03fd2c887520df4a55144f8e206249b7517ce48afe52c11eab584f4bc0e4d5d706142edb6f0b67a99e82757b2d015d5",
    .input = "8b7fdf792a90218f91998b084756f32ff81488466bcd66ceb4956702ab343ca59c15bdfd405f7e20ec61a36e0933f55fc49a357f062db0b6a7b613cddfdb812efdfee3eb5b617f02918ecde0e9f6852313d8fda41a64b2b5972124a7258ce8901402f84a62df4dbfe6e8b064cfe6cd044d9489bf8ebb9552ec9c4399658e9952",
    .expect = "7966440df79b13e95c41346eb792f3ec"
},
{
    .testname = "NIST CAVP [L=32]: 181",
    .key = "4809f31e93423cabf44cddcad23da7d7aee734d311fc7babc276a1bd3d35139861ead10369350d421d0af4944959cc006fee3f51b996f66031836a9134f1f7a0240a339e5e077d366c99",
    .input = "6e4abd414dca21a6ad433146986273e2da952ef613cd1f9a0a836ca644f9de19d6c24abc77845002d9fd48333a447ac936518d1bdfc043380fd26316fdb5f6ec0f05b5dcef92c3d5e16498b854fc3db9b6ddbf098d4bdeb2c45305c2420b7fabc21be7eade7ce0e76c80071c0e13267a0540ab0846f758ced00d3bf13c84e11f",
    .expect = "d7baa0117d008af786c2bacb38b9d386"
},
{
    .testname = "NIST CAVP [L=32]: 182",
    .key = "1ce3f5bce2b176bf89eb7015005ed1ff5177a4746cf8ed7226efd49381e906e02e6359e95081af1683031c381d744b63b4a41d00e059941e4142f009c42c171e23783addabcdb640420a",
    .input = "b6acbe5df01480614143c94790974c82d046352124f56a0246861042293152f7ddd65d22b491afdfa39092dfea21e318f70f18bb882f82671136ce9c5dcdd27277e8878bcb535146898d87354ada2fd2f694096de5c2d06944ecbca8bb2d4b444c8941807f81edfebce5af32f8eab716947c0f1f81d5dc70a94fe14f8a7644d5",
    .expect = "7588b290c3adf86198354e3eee4fc06f"
},
{
    .testname = "NIST CAVP [L=32]: 183",
    .key = "c8fcf6fcfbf498b33d3ecf12588a596d9fecc79ed43384fa4976138446ef9861ab0c9a8cd6c407cbc72878e2823ab706b5017f949bdd82032019b01846bfb758c7b0c6c3fcf397bffd4e",
    .input = "dc058f909e7170bee56c4dfde862b4314f68314a9717ccbbb79bd42d0407db7552eb02c45c29771e66043b0e207a2997ced4346da67bf066790d542b96b0be33eca737f26e23f84dbc5b2e52ffdefb261428bd3eee7492d235d21c8f3379818df15eb6809d06fe322f98ad314d3632c46b8d542436abbce93311b4c3a30a2e6a",
    .expect = "99066156163139a8735711534c022937"
},
{
    .testname = "NIST CAVP [L=32]: 184",
    .key = "8985c5dbc6725a4e1ca26f5667d6da4938a8d542cab69a6938023075ee99846f5d73bbb8f49bc74d4b8f384aa1ea55ad88406c5ddf4a666b01439e973c91f41685a81d92692c3d734755",
    .input = "48ca2fb5b7e4f471a20911af6a66158e45aef700ec0262ce941350dc208adaaf95a84e2cce2983a2716f690b21dce48ff580db4a29f48c4f148522ed5a958931633f81ab0c3af1759c007e72f92f5dd41c2f65e1c21569f664c7c4cc6a6135fa9cd8eebbd9dee7f20b05786b5a262764a004bf4c1d2da2ca6d215f01b6b68713",
    .expect = "0bfa572019e6d0f987f79b03ad67ad09"
},
{
    .testname = "NIST CAVP [L=32]: 185",
    .key = "e243c480ff1de35ff7bbb71963e145b20dc43b31afc1d4f4fe4ffc46e733b53419f3b99cc38c60869f67c5b72f8a2484470c87e5cbcba2caba61fbb26b534e79178c2f71980af1b570d8",
    .input = "7e8bcb42e9c0015e96f4f802520a15cccf3fb280540e7108b251cfb97aa8fcd86d1eea5d340aa3f65234e14f5639d89155315729978e0fca914732b513374138c3c01f74cab36964cd740a1b1f59094d3554a6115ad2a6e5a3e2ebf3269a479367b692101383faaff1fc9bed1532500957f1c8c203a0dc62d2691ffb199ab7f1",
    .expect = "ec8356beca9d87dce7d010de113b9fd5"
},
{
    .testname = "NIST CAVP [L=32]: 186",
    .key = "2293336d9fd48570e6515a4d7c4985daf0e1230d6b6bd06589e71b8567ca3723fefff320af2cebf81e36005d4407071fc08fbe4f6e0804a43b7f491d389043e8ed71e283ef328721b542",
    .input = "7d70d5d8676518e8f4ccfb3660bfc14e20aea6c775a616b342d21d3a1b421f819eebc9d106ef47f5fd1fb7e3b2bede9f2c881a5ddef398e67bb5c73c0b860d813f27b81501a337ff50d58a8e4b2af73f8ba9ffe2b63090f951007c61d67b2a34072d8ced810a50cd94f65b7e528b73f7e6163b9f28e265b56eba23efa4a9de61",
    .expect = "b7a1d83414cbbde7a7738c7e77cbfe3b"
},
{
    .testname = "NIST CAVP [L=32]: 187",
    .key = "d30c4a44e6429bb5a319252763da22b8593b7884c4ca9124698f677441edde996fca574374f08230a6b273f2dfd2f9f172a22bb3636a435bd70ab070c9e066e0ffec79453c32ea66b860",
    .input = "20a0f85250a95615b7a40f25132af070aa388d86df777bfb03c0bf0d6ddf8787cd9718e6bde708b9998cad4e91c7d58afc60b719efeb2ac80f4a152ea3732792ee74c809bbb44fdf397b753809b409f796f2e6dfa5b223f82de08935689c4a532a3def047296934d3e794f2da47af57f1ff501212753cc5604880369e3e05894",
    .expect = "495f4ccb0530c7b1f03f3285faaae818"
},
{
    .testname = "NIST CAVP [L=32]: 188",
    .key = "cff586fb91a1e9d43c36a76a4dceb9e123df15670324d1c75fdb8c3b58310a8281fb1e33e6a6cd514d71b01fbbd99a363a557bd4da448477f6248cabb804b320df3c45ffc05be17e8b61",
    .input = "e37e9da1ddfe11a2ff6a95025d1970fa1c2997bb7974d0010cc017ec4e36410c5a16dfbaf0a865afbf768ccfe4b8f446ae100ed6a477396fc9772b011e9c938e6925fc8335fef5481af36f163e1e66091ca1c476849b827ee35410e3c5bbf71b9813bda3b3e908969749077e74310e6aef46804122c6f255e4be8d3b4b7db4db",
    .expect = "836034775fc41e033c56ecf21d1874aa"
},
{
    .testname = "NIST CAVP [L=32]: 189",
    .key = "ece40441a168c83e0e356e687788081f07f4b299726c5f8fd89fd836ed84017157355e455700d78dacbbb8efb459fc0ed5bbcb011bc8410522c0716e37cdaae4badcf9cbc6aaee031522",
    .input = "4b7ab71376d83edc4149b74ab10b7c1b1b6fa9ce977f2d63b2e321626306591e4174393bf287ca6ee7420d84467d90a628423edb05787bce6cbe71d2f89aa4237fd3cd6e8c1be59410f180ac54c65c47325f3af7857aec12deb4b0b379aabc026f5f1ab52cdeb6d72420b6c8c22f0986a18c432affcea8b66f8d860dcd7ec943",
    .expect = "43385c80a077720fbb417848e4fa0138"
},
{
    .testname = "NIST CAVP [L=32]: 190",
    .key = "a3a9c55995ea04d6ac3a93ee579f6e7c966ab5edaf1801472377f86ae00a1f97b8adf02e127c2dbcdff27334d04e127dc63b1c2d8bafbc95bf14c9fd15a69b30bf1c1e3c268a2473df86",
    .input = "806e9111c731be67707d49b9e4248e82039608dfc6fa1645227eff6f30eb349b8c7cd6f6fbf0785550de26259049a6a55474fd536ff736a3d1135ef7ab43d3ccd413bf316c35df7ebfd289426b1eed7dc62f9b107a0f45717210c6a3fa5f646621dc52ab6229794a840179f7bfccea732070e7ff2f69cd16ce1c405b64686fd1",
    .expect = "9014a5bb17057eb39ab9fe59436e6c9f"
},
{
    .testname = "NIST CAVP [L=32]: 191",
    .key = "ccf7c4e2a8e7a27c7bc54422214c880e7c2582d0680b1395f02dbda8c2d3b539e0453a5e99e92657b8abc316fba1dfffc6ef23ec19e9a074c078ab6dc9bfebaf3bfeb01b05b686dc350e",
    .input = "85a438185205f773b7b39db2a71ee86aee341f9b2285a2edd7a5c53913d2de4b02d79de7ea309c09606f3771bddf9e5fcc66289cc5b0ebb97f89899be18b4c389afa769b11ecd22e9fad8f38fd614ea5f8eb7a066c0ed8d86fd25f09cd2a49b8b5d36a3db17fc169db334d0e4fee21c2dc8bbbe1ffe892d11148ee8abff6fc55",
    .expect = "e4c09bb7f5ee13351baf8f4fe7386711"
},
{
    .testname = "NIST CAVP [L=32]: 192",
    .key = "8a81d2ad65585e1e1383783faa17f460c39560ab730f95657d8c8c71c5ae731608920002cbf8068e91a446435104879d2712e9104a7c76493e02fab64b2014482dee8e780d44ea88b021",
    .input = "18915f3811cc77d3d9e41d543f3bbdc827f5781cddff193da94f4b7da46d0a39c93258b84fcf31573712c0e321e5d34763188d675c605a4b069f2880cb65d5bb9ab7e3c039107382dda6718cf8ee0c9f5262699d5b8298a5c019c7803cc1b53cb1a96a167796269ef32897156c5f4e1a1b5d7486816eb994fe458e459e899402",
    .expect = "a43a35e87ddb24ac3420c60c99090ba8"
},
{
    .testname = "NIST CAVP [L=32]: 193",
    .key = "8281addf9835f1308be680dfae2dde6c52a58b698c9ee3d3391643a240e56d9f17372e76893f3e0cb62a67125b52e9db53b51e6a5ea55ad022c115b56f234c34c7db24ec1e9cd153deb6",
    .input = "48dd9054dc7703793557e492fc0fd0d45db0de0ec48683f1e402b3affef849c9600ba9212c65a4575aab9c52002fe81dd16879f5e4a0bea0b8edc6007462a5e77386182dff056c005da69b7c0b7db97b45628eafcda285eeecf4c5ccb4ae9d6f8938259fe0c1221d45322b36a3600a97c086656307f29e838afef73e4742fa09",
    .expect = "d02c59ac11fc434a37eded33245701bb"
},
{
    .testname = "NIST CAVP [L=32]: 194",
    .key = "183b4cda5c0282dab62aa4e48a19d3a5a00aab5524046e45f1085eb70f8f6af379340d9724ad742f3effdf05b3f2493bf6c34b16fe1a3e9d8f3ba063ba80b8a1a7077d8792a8b5d4142a",
    .input = "3978b24f0bd0829e22c0596627d9d6d858f1c69b8c19486771cf30d01975aa5fb50220e7a0f85d169f96f24b674ed8a75f795867a84a28715b00d72c11606a95a9634890452c537b963c58095ae9a94e220c081659fbc77b82b72eb7c1661d369d03f2f00454adf58f1c5349089390f32a139f51a7146fae705afe16306d0969",
    .expect = "c6d5ed018b85568d03fce635a1332e1b"
},
{
    .testname = "NIST CAVP [L=32]: 195",
    .key = "fee603258582e3a3e8feb886599d4ac405a1634c320e85ea8ab0dc6bb65f72012f82a2e951d2cf4ab2615661b1dac0db520a3d82499f4e1c5430c190ce7ee24b82faf0e2bd87cef9a780",
    .input = "67541f77f4e40d143035462505de14a02124b992ec1d0064bd15185d4d30a2696c510919f23b12eaf9f6b4ca497529d81475456ce4a80757d1136e6cf7b48d3f2769e22cdd0de49b72e4db839339f42df245953b3b53eee84a22d1919b8bc375026353b99ca3aaaf05c66457cb739e26235c5007db66dea0900ae9d621fb6b93",
    .expect = "f914c842b78c3b91fe6626272c04f6bfa39c586d4823ce0e"
},
{
    .testname = "NIST CAVP [L=32]: 196",
    .key = "832f87d596449aeca656e0e0b4ae92dcd16a66889020a9d2bbc48eee45ccc69b809150a990f993b82053aa425382ffdcfd5e1bb81457bc6f615c28fd7bfbc20df6c9db78d804ca084c77",
    .input = "782ac16bcd744ec016ffb6b014e0c8983dfde231fa72c31212349a7766f46240e047723da60350a893ecc7f3e79039c53d6f363fbe5f4c83952f2177a28bc0c6731f312870004ce45547ce93e6ffad26de41a92a289d244b51bc33173e44f5051afc24b69331e97a4658f51677f4cdc506ba657c9ef3f1723023f8e0a0e8aa05",
    .expect = "c68f215b059881c9f97117b3c6d9d6deea2e0945e3e1972d"
},
{
    .testname = "NIST CAVP [L=32]: 197",
    .key = "92a0e01315efb0b347666581560b44bc582ab63e8f8ea651ecf72bc3d3c9673d1e02afd0646eebd17b1e40e73b16ed62854673ce84bcf9c83317ee11203ff0e16f53ed7e21e3880c9760",
    .input = "7b2f5c2741338d25d8f9d4bb0fa718499ba960c65eeb399fe94b59c23f4e81f5db11a86df583559c02d24d4a7a236ee7dd86db20f82959b065ccf9795174f8d38164e3249749feb192b5e7b395ce77aee948e9fe44903eb24c4adf9e57fe85ac750e5673b0ec510b9289eb1fe811fa43c6d5d388cb89af4ea6af545ad953f129",
    .expect = "3d516a213a6b8c7e3434138238ca5e339fc21038fb7bfd21"
},
{
    .testname = "NIST CAVP [L=32]: 198",
    .key = "ce4c926c0922ba36269a20d60dcf08d43a1cea120f266af76f1c8acd883d1f68f09b8209f41f87822dceb39a544aa9b2569ce6a9ab30aefee421463484b8647b112fe48c6bbabcd55cc8",
    .input = "8917aa6e1cd35af30eb5c7ac200e54835d4a0777a06a2fa756b44aac85a8252c0e3745ac2f3086a64bfb02dcee8934eb0c8b5e2389e22796fe57896fbb8dea8608338931b17e1c5cc1d7b8dc8dd1f000f45d4169e641ae1c23c6a7d645b12fa001753ea2aaa7643cf6b2b05305ccd0e99f2979f1be6e0a614c686c882dfe3ca2",
    .expect = "94c47b509bd0c9b7aa95289a00a8a54efd425481307e9ebc"
},
{
    .testname = "NIST CAVP [L=32]: 199",
    .key = "0649b582dbc59816a8042cac30cee6772a0ed8cbe8e07bd538ecab8a88f3f3dd4da70b35a5c09f1e3a4c523e6a46038ca66b4fbc184957fd8999c3e781ce07afb0eee49e8ca132c13c88",
    .input = "1c685e17890ee079ee85cef5ed709356f4199e657aaac0bc85a1d5d5707ea666ebbe0ef1430d5c96e4b8f92d1c614b9121f6d83e56e4af1fca8704a101e51a0cf89d6613631af1aa390cfe177219ed4c10cf5f745cde9bcc728430b4ff48dc064aebada6719c665af56b24dc7900412ec78d792e14014b6a857fa235f20eb5fb",
    .expect = "9bd70f0386405c04d1bfcaa538b4099abea343c5c4379482"
},
{
    .testname = "NIST CAVP [L=32]: 200",
    .key = "3d7094e005eaf0b1231cf60536f768e62f79dae86374660bde91a2e2fa94cff531e2536530406ace2cdd187179936293596abd20125ec7944362351b77a40cf7fb131523ed1f8a3696bf",
    .input = "9706d7370b66bfa78abb8b25a9d6143a9aadcaa4f60c9baab98717ac8fb3d2fe4e960af7c35b8a44b14ace8217f8680db2bba312c36165ec12225aad33d24efa085cdb1d876b4555bd6aa27013af3e9cd1f33d7be0068275d4c0d0522a3b2f08cd3f92d1dffeb681b7024d1726635c92ff3de206d661baee074bc2c4fb553dcf",
    .expect = "59526ab645c2c0f464a48e411d111abe9aea19edced55383"
},
{
    .testname = "NIST CAVP [L=32]: 201",
    .key = "74d72be7fc8f4fd566f863ef53bdb361137cb6d96b79efdd95941161897866997b16710ca552d3ea46fb6b9feb01c1a8ede2a5a53b6613b0598c5aeea9c47d63ea5eda0bfe430926f0e3",
    .input = "ff8468cf11d6190cae4a1e16871ae0817214fd441a889bbdf564fdf5779e542686d2d77a2d2d151694898a5730d9715b37c8dac4579dfcb8a762cc2cde45cf63c33e2cb1e4f205858bd807a7ee9a40bda6be31146285259ddd13c1360dd1db2b9e1090fd9eef90627a7ebd8c2923f5aea73d2bbda508bd747fc1019a6e0a2187",
    .expect = "8ce0b5dde0328c9de6d4acf84ff61b3f7d01f9e9e8e36b91"
},
{
    .testname = "NIST CAVP [L=32]: 202",
    .key = "94869ff7b6164a24e89ab734f20322421bd31581548139c6b41f6d46243a15a05c02b41e0eaabe376012a759a0a440e6337c437dcfcb2c7aeb7d4bc0731918b6bfe9c68fc65c1bcf8fa8",
    .input = "32e5a9f3c3f9576a21dbfed017b961f118cd23f3808f2c2b1d294e35ee2b28432a804bb584a19ceaae08fa561ce820d50a1bcc3fc05b213d15b6495b323c605e98fb8dd7652d72f8d2afc7a701b541d1f6bdb901e3c18a31a8b13be09a205e64833eb782eb06a13c96b8aeea4e8a8e8ce39a325f6f2830aede026aebae3febfe",
    .expect = "549afd1666a491b7ee9ccf6db2a33b2e3c2a21cfa69a1b17"
},
{
    .testname = "NIST CAVP [L=32]: 203",
    .key = "fbca586edfa57645037b6b3cd70fc341e4d4ec97af4b3dcbe18b36e9a6210aef531b5a824b6044e023439c16045779735184f43c8a5a2ca171a68ef06b4353092833491286eed76cb3fa",
    .input = "4bf841ec0a4211b05f9a45a127bbbbf6434e8642910e8ab11b2a468e8feaf009f096c7388a94a55b2bd0d364906122b71e69372ed33c27607bc544232726364fdb9f4dc587b115b038832b0b908450647452bcdf04dbb47dd0c25f9e4804d6c575db7a9ce7e28a38ef7af59d0e6d6c85acd2bc5d0d315b9182e74009dccbf8f4",
    .expect = "0cbfe6e817d297b69d5bd7740bb0e5172d86cf870a9c4da4"
},
{
    .testname = "NIST CAVP [L=32]: 204",
    .key = "624248769dc2742a13e6b69b5e7212ca459b36bf86be5dd8d35273601a1c7a6309a12cc1d2e1e2822b42b46999cbe2ccef9273a311781bdefe1362fc0eec03d978eb92c7160f62e16d62",
    .input = "633974ba735a5e57d1e804bcdd4d72d4a9e9df0fb9bf8db2076ef1714a64143f784e39658ad2c0d17f814ab1a3071e4111a5cce177e2106b197df8c319a549b0f56c20ea517ad574f7fe242b1ceb8fa0e560fe232967a92079e337af5dc42766e17d707150b864e54048da52ce5f8c982b01befb58b821792d8af65aa028760a",
    .expect = "ed1fb08b8473af53d2fe4c607e5ab9639cdd11f728462294"
},
{
    .testname = "NIST CAVP [L=32]: 205",
    .key = "25cdcc9cb014784dbbdbb13f56ffaa63fa234c916f02367dec0303e8810fcb13b29fec7965190abdfe5c54e2c89909ba97663ba1ab0dd46bd82ad69ae475e7d431dc0c959bd5b522a4f2",
    .input = "ea526480a096a4d89306b3cf86eff742ab46e4e9ad991ee7f344dd9f24e896cae619d8c6ec5774312f40e0b77b03dd282e1858ce3d2f8efd776674eb0ebe56c253d0bef4c1bc97cf3d6392519cd6c93d660da36ed9ddf76c3124743d2747407eb8dedfb227ad57d945d79145f04e03a9da8e8c738c8b9f5baae7a43c78699b23",
    .expect = "4cb070e34b3a2ecb460670ffdd457f23c9a1174bccd35f25"
},
{
    .testname = "NIST CAVP [L=32]: 206",
    .key = "3ac105a2bd07056d3e1c3ba547359dba94e8f79a6c32ddd532bee4ff37641257d2f192a5b326ac697403f5317145c34bda2de49c068390d00adb9bb48b17efdfd02d3a981b2ae4f43a77",
    .input = "f6eac4c4099c3232df018fb3c837527b8021a1a20cbb5d1be5aa5ee5581800852dbedeb38742dd540bc46da844b40bc546e60a4492e8943a3a93ec6a46e0f5b855fdf8e188a0a26a9b9c4cd655b2801c23a9b85800a068c197a43fdbac7eaaeeb8ce9bb6d35e885cd7b0b6a5c3d9b76a5d9232481c8de2984405e1a15399270d",
    .expect = "e5d5cd2e163ec1c883388f5f01980d3bbee914586ddd5b0e"
},
{
    .testname = "NIST CAVP [L=32]: 207",
    .key = "b8d9d674cb623d7a449411fef509558992b7f6e314c64f855c9ff2511946a681ebe9acdec9b94732a0f87bff3c5314716c73ea9261cf64bd58c43b5579e780b6fe9ae16c97dd28a40d67",
    .input = "c9f902c8c02c5b24bb54e2dbf5c9573bd46bef39ccf15462817eee152b7561f03f8f57884c2b7f5d22e5d60d3a6925c7528aca03588ebc7089ccca2eda7a233e97c01b374a102c3adeba3b2704bb1d11d6d65af0bae731968a73dce5f283153e19b3d83c83866ba336fc9c931b674a02a87a2669bca3bbbcca9baca03a3b3dd9",
    .expect = "64ae3ccfaa118acc556ac50e53cd9fdf7d7e3f4b785b2e20"
},
{
    .testname = "NIST CAVP [L=32]: 208",
    .key = "c39ce5407c0c03ddfebe82dcca408c52f26b64027e38edd00dd57079c0f89a825374c46e8d0a7834db8130f038f860d94f7cb773e4d6a20670a6134e0bb680748f882e3dfb31af82156a",
    .input = "c1490ae9579828b2d6d2935f417e0dbdfff5d424de5ec50557ddc7c3140867c4af9bc0c7bd6c9e780ba1e341272029642247a84795de5a0ee2495e6fbc029bc2ea47a5584710e40e0e44f322542c4645d62810f1f5a163fcff3e996eb05bf490f9b78145ff6c429d67258ba8d18bad88a200d2ca079028f737244265f8f9bb53",
    .expect = "0d2e37440adeb6836d7f47d9c516124ebbd64abd435d4c98"
},
{
    .testname = "NIST CAVP [L=32]: 209",
    .key = "318608b213046a3badd1655c51135c7e1492c6cebc0f2f36e0d77f8b4a987f08a07299fb4451e0be787b50e9c66556c69fcb930542ffddb1df828663fcd1e1b6198103fa8f8ec72dbef1",
    .input = "45fcbdb93acd8300ddb88012ceb55950f4da61145adb0d4c3dcda868632f4777ae2a008cf01857670144f9510ff0ad48369d875c50865e590f6e81a6499ba66d922323fc1066616c8bdc8d80c41190cf08ed42260439da28db5faa37767109981c6d90d142c08956a408a465941eec2f9254fa381efb6800ca2989e393b9573e",
    .expect = "95b0a9f0ed9fc80581407664300488f5223720148618b1b9"
},
{
    .testname = "NIST CAVP [L=32]: 210",
    .key = "81574323c973540719d192833ddb51f13a52dcbae294aebea51be5f6aa47f3571f5d97facdcf0c7befbe809f44bdc73963d8514e4fd559774bb96087ef8eda6e7c64275d6d96c42b4e4e",
    .input = "b9e944e0b42d0ff454f7f8aa24f00e9ee039058ce4094111e39731b6dc3ade2a4acec4cf9c5be078e4f10a72d3d685c1e5e4d5abd92cd07b64dff87f266f0853ddf1cd61d9c637a9b07ab0be32ecac119faf827218b17ad4541a27519477f76ed918089f54b63d0e1e5a92982979ac187764b5e989e066a61b1065340e9cd203",
    .expect = "514bd18495f6de0e237054b8e3ba1a74c3fada4279ad6b8550f3a14712c528df"
},
{
    .testname = "NIST CAVP [L=32]: 211",
    .key = "44f71c2317cde52151c84260d1d3c04a28cc15ce5b3802b2e5357e2bfcaf10ab15d77dfaaad1a3883bada502939948234c559dcd95e7e158338fa12ac6fd21874ec2ffabed051416ef77",
    .input = "2ac0bb0524c22b902de34ce64e6172d1b2074e159f517ab1abd152622cd10669f03aed8e2eb51c65bd0f38d084e288c532724e512fd558ddd257d2b1d41c5eb6040767803ddbb18b95a035c5d8492d4d35936b7b3630ee20f625b70f8e71d9dcd0efd0e3387d138c1f5eedce32dd88f223334b9a9eab65017f04aa8442179f62",
    .expect = "ca0053d51f6cf6f9998ff1e0db00b90e82c7b18cb5377acc8ebe9afe20da1c3d"
},
{
    .testname = "NIST CAVP [L=32]: 212",
    .key = "7edeeb6b63c3b9c836c4843ba46bfebd8ca9a6e205c7ed68a29f9710f50c65ac519ff17ad494d9b0a5041f587b5cd05e5f0de4e8b28566e5715fd5e9b8d6c9388580d921bf39bd8d775c",
    .input = "f5aff283b3aaa4c71b13c590771d8bd3358d76988ecd1eae653c2f9d72c9b2dc9fc08e44b2e34ec52dbd245872332e342b5cf945e99344da0bca069ee221b2c913b7b9973cbf50fadad7758b6a962cc7ce640f78f38f0571b19b527ef2d9d09b173b7b64976633cde909be13a56d0df3e64ec019f2eaecdb1d571b27ea1994ba",
    .expect = "5131ce486de164491b4bbc84e7e461a874a2cfdd769355584a063e306960acac"
},
{
    .testname = "NIST CAVP [L=32]: 213",
    .key = "6e1b663e808a6986f29956b7b9708066696f9dfe0d7bcdb55696d8bef9b3b7c052c857884d2499fb86039d4eaf604079330ae3e818fa6f742ae49593560c5bcb545bd46d89b22e7f2b7e",
    .input = "c0bb12a5da628363a71f1f5c9ce715ce8995e607148d772b669f6532242f9830a1931bd952bd2a44821a8def46b92504b4b0c5da50bc43bfc727cef5e0ef81faaf24390c0c92a4ed43a09be40d78b204bf680db0c288755f439eaa9d2b3efb5352361547ef2919e65479f142d86ae35714856692523b359442cba333ef662ec1",
    .expect = "665344e5618e0c1fb8758d049409a484fa69b89b009746067ea036bfa0ee8a37"
},
{
    .testname = "NIST CAVP [L=32]: 214",
    .key = "208f91ccc87965d365cc325d3262b64277f6112b0b9371a4174cee721c2eb32638735ff2a5f8abbc82f24c71d6dc1b9cd2b473375666dac0b789e490c0495569f6a4864e20da0a97071e",
    .input = "854b32866273c6eb110e380b8f3bfd169cc87a6f6149c75e5667b305637b0895465c10c134745773c31ab3be071c8215fb9a33ba231b087870da199564619d03765965d6b8a1a9fbb79d0726a3d1c90cb0ae67d3bbab4cc63198dd4e2d2fb81de0ed39ad362043e9b6403d2aab825a6481ab1ea271221eaf614a0716050ee14d",
    .expect = "42680195f431e71b592899686af630e15996dc718cc29030163d677688a33021"
},
{
    .testname = "NIST CAVP [L=32]: 215",
    .key = "915794a6c6540f1ce9958c2784cefcc13772198cabd4fa17c88de45c281d648dcbd59a100cf4d8c8d3106c960db7b91f59578dd0045bae203897b61570e6210a2f11a5aff2f3c25163db",
    .input = "99494422460ec858a24394f603b1d9b940a24ad9c6a3d1e9e88781fe77afcd139389f7acc057cbba3d328cbf914e2f32667fc7259afc412594645162d4feac10ce45780cf9a400c3237ead50077132e421dc066bc19e176c5f21bd312e98ec29f384af8a187dd13afc2fddf08ea34a971ac0eff36311bd86f1c8acb5ac03f627",
    .expect = "2ca1bb808448eb29085286594de21e254fb3416f9ab01e99ea33ca83c1d14dc3"
},
{
    .testname = "NIST CAVP [L=32]: 216",
    .key = "b1a95aa80bac5acb7a18332fc03067600610f376d99e77a272be96063ac5a0ca8d316e6cbe978e575cdca1b8b4a8008d9718a6fe5eb34af12aa0cbd97116d1ceb613b2e3975192b40d76",
    .input = "d8efcb416f237c7e05bed9212c543011c39e6a5f25d7e2cba065788a29bce1464d8041676be9fb91216cc76d049806ad943e534a6fd45b10c41bee5d0b005626f3c0e73a9c50d7cb07fc502acb4ec4d2093181a8a1568581a6d793e5101b8613b1f9e6446b20b9349fb69bdfe83f11880ac11b00252508252fe18ea9a0d41a15",
    .expect = "988d4a6fa87f8138d754c5de9d176c45eaccf8eb8ca1799d87c8f04a966b6f4c"
},
{
    .testname = "NIST CAVP [L=32]: 217",
    .key = "9e4ba7d72b76edee6a6f290ed318bedb0ad88c8411f9c449bd4ffb3a661b7e41e32ee662b552ec4283e57ee6c7c712bec6773ae2c578789b7afa5425c1b6adb3901a4db42da6c0559e96",
    .input = "1a0223261ab437a4ac1701b4780776c43f0f8949b3e7a1618c3b4ab6d8ae2aa6921f38a2772b28d415f32905251fd3bd1a235bacfac00a486dceedb8143acdf11b4b611f1229c346f89f21299920b56b1b08f7f4d32511965d7693f0eb326893dd0c096492b6f0427ea450e87d1203146748c3e9e51d9e9183baa42806a0e3d5",
    .expect = "ee6492a669e22bcf19bbdfc45495cd0efa9c2f2ef5d42831e3f13a545cbcd6a1"
},
{
    .testname = "NIST CAVP [L=32]: 218",
    .key = "8fa12bc017bfeb6c894020e420c5f76f9080e8733b998ef3a7d0b6563063b66afa3200a82a21f6ba56be003a3924dcbdac1f3610d29079c19213e4e14ae0e009c1ef919b5e60ab4a9819",
    .input = "faa6ce40d931f3c0cb4538a82a22f0d4f3221f027b99d3d85dffb729b751e57496b4fcadae5c72404fac2c54949e4c4cde664b948052479abcf59e1aef84bb9f088030473e9505c603c350ad33bb06ed928c1196757ea3e5bf3ec97e0f3c43f638529394f2a65459cfd1cd3d7041c6bcf8db9a91c1e58ec24e2461dc81412580",
    .expect = "9611e838fb1d816a0ff9cd269217d93258c34df9e26b74476fe4da0f7dee2335"
},
{
    .testname = "NIST CAVP [L=32]: 219",
    .key = "c18bc28d496beedb25ca42d1b217bc81891d4c2bbb35380e5bb9bf7e3dbbfd37fef70ef14407763447d6c06e915766430277f124165061236b9fcf057d785199b4381e49a2bcf3ef85d0",
    .input = "28b18b862ce9541ed6daf81199f9a331133b0ea3e48ff486c1acc6d5c40e9f8f063b7a15704ba3d3cea76b222511206d47e53c93a49edd8d639b7551b224c3f65aa802189648607e259ab1fa9ea665910435b7dc9a4c28aef8f32cf85f3a23e94a7e8a5945e9736702383261aac15ae571b4e8466da1bd31a83a5291745ba7af",
    .expect = "0bb4127d89d9073ea425c303adc3f9db39e40adac23ea61fba8b6e251d79390f"
},
{
    .testname = "NIST CAVP [L=32]: 220",
    .key = "dfd4faa6b9ebfff6eb33d4b536f3f18785fc33e82ddf3908735d0fd94f1f09666fa8f2667f876611a8d17d3256ceaa7e3ff3e224a11000a5cacb68e6de4dea84d53bea67c3e8be9a5cc9",
    .input = "80f20152d12b0a5993a2b17d1f55cfc0c078961ed00cd1c21db36d7a92c339691399eafca830621fdef232b06acd5d33108a5fc8c35a6d5b0eb2ff1bb2598c2d91c094a1ca91e4a5268a16f8b38c57a2aeef6de3a619f869df4ff7c5f5ca8f20c10e082a807719543215653f41ba45746350c855c170f85459315f62a13ecaaa",
    .expect = "109ebb4cb2ad746762b6652fc63b99019857ae89acfe9807648c3cfa151fed42"
},
{
    .testname = "NIST CAVP [L=32]: 221",
    .key = "c96c04a3bb0816fc47e05913a715fbac9a3ad09db75b48e8013d9f27bbe8532d7e63dbea88bf968f575602f377552e35987872a4e3155ddb8e5cef30aedd08504d4b2123bd7f3af62bbf",
    .input = "b11389c7dc20ffd0c4a5f887f2576bdc302c7d2af7089a012799c528fa7f2ce23bb10071b31c83d9e58d63e6fbd04670ff1aa6de4ea4dfe94a9986a35032fdb7ea1f44f2452a1202e517257e97ced627a7bcf06e5476c236819f73daad0d96722527fe527891d4d42c0ce658af97428890da04e1efc56c6f337534d7fb57209b",
    .expect = "b53db6bf0c8317586ae6c1a1e2857f241bf55dddd1b423578c6949d4bf014611"
},
{
    .testname = "NIST CAVP [L=32]: 222",
    .key = "9319838432ca096960e2196a06398134ea06e4e8799ba470c54f0512cabb9045f529b6c4e749b6e27626c11df4595bf5b47c04ffcbe218351485f49077405ad96a3f17bcb7b3e21e80ca",
    .input = "57e1d3ff5fc4785f9370df2e5abf454579752ea934d2a9bab568d5aeb22ba43e4bc7df9f31366bb40d91ca822026e4e426cc088081732ef993ff7f676c571704a5b809278b50a3778108f4589fa18caa9f0283b3fad0bd594e406b950329d5242e5e5880b53aaa0eb57c66992055c4ffabc0a72ae712de42add2a321c0ca6808",
    .expect = "4a34bd4dfeef7fa1dc739280f16a3fe1281a51311c10a920ab43d406d4ae3370"
},
{
    .testname = "NIST CAVP [L=32]: 223",
    .key = "2914da23e86a603cda1eede153be2431c2947cdaeed6a1ea801d18e2c218220ca682e40f0a51c4c13a31163cb730f83437bb7a88ecc903160956f0d483137d1d145ce948866ad57f2eca",
    .input = "6b8db9acdfd24150808a92368596557181d445e5a04e91112db2812b58035d72378d8bc00a1ef75ec373b81dc6f1f0a2ed96f302cf2eac8f42ca3df11e6ee678440a28b0dfab2a36eaf35bcbf3c759a71e47120f6c03292a3d6b9b111488a2259bead9a5e7e2a180fcf1c467947f59271cd0e8360035ce8b287fe2b3c3b95822",
    .expect = "4de7bab7fe9a0a9bf7b51a7cdf7d929f2b1c6ff4575fd527baba1efdf4254890"
},
{
    .testname = "NIST CAVP [L=32]: 224",
    .key = "4b7ab133efe99e02fc89a28409ee187d579e774f4cba6fc223e13504e3511bef8d4f638b9aca55d4a43b8fbd64cf9d74dcc8c9e8d52034898c70264ea911a3fd70813fa73b083371289b",
    .input = "138efc832c64513d11b9873c6fd4d8a65dbf367092a826ddd587d141b401580b798c69025ad510cff05fcfbceb6cf0bb03201aaa32e423d5200925bddfadd418d8e30e18050eb4f0618eb9959d9f78c1157d4b3e02cd5961f138afd57459939917d9144c95d8e6a94c8f6d4eef3418c17b1ef0b46c2a7188305d9811dccb3d99",
    .expect = "4f1ee7cb36c58803a8721d4ac8c4cf8cae5d8832392eed2a96dc59694252801b"
},
libmongocrypt-1.19.0/test/data/bulkWrite/000077500000000000000000000000001521103432300203245ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/bulkWrite/bypassQueryAnalysis/000077500000000000000000000000001521103432300243575ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/bulkWrite/bypassQueryAnalysis/payload.json000066400000000000000000000025101521103432300267010ustar00rootroot00000000000000{
   "bulkWrite": {
      "$numberInt": "1"
   },
   "ops": [
      {
         "insert": {
            "$numberInt": "0"
         },
         "document": {
            "plainText": "sample",
            "encrypted": {
               "$numberInt": "123"
            }
         }
      }
   ],
   "$db": "admin",
   "nsInfo": [
      {
         "ns": "db.test",
         "encryptionInformation": {
            "type": {
               "$numberInt": "1"
            },
            "schema": {
               "db.test": {
                  "escCollection": "enxcol_.test.esc",
                  "ecocCollection": "enxcol_.test.ecoc",
                  "fields": [
                     {
                        "keyId": {
                           "$binary": {
                              "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                              "subType": "04"
                           }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                           "queryType": "equality",
                           "contention": {
                              "$numberLong": "0"
                           }
                        }
                     }
                  ]
               }
            }
         }
      }
   ]
}
libmongocrypt-1.19.0/test/data/bulkWrite/jsonSchema/000077500000000000000000000000001521103432300224165ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/bulkWrite/jsonSchema/cmd-to-mongocryptd.json000066400000000000000000000006321521103432300270400ustar00rootroot00000000000000{
    "bulkWrite": {
        "$numberInt": "1"
    },
    "ops": [
        {
            "insert": 0,
            "document": {
                "plainText": "sample",
                "encrypted": {
                    "$numberInt": "123"
                }
            }
        }
    ],
    "nsInfo": [
        {
            "ns": "db.test"
        }
    ],
    "jsonSchema": {},
    "isRemoteSchema": false
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/000077500000000000000000000000001521103432300216155ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/bulkWrite/simple/cmd-to-mongocryptd.json000066400000000000000000000027771521103432300262530ustar00rootroot00000000000000{
    "bulkWrite": {
        "$numberInt": "1"
    },
    "ops": [
        {
            "insert": 0,
            "document": {
                "plainText": "sample",
                "encrypted": {
                    "$numberInt": "123"
                }
            }
        }
    ],
    "nsInfo": [
        {
            "ns": "db.test",
            "encryptionInformation": {
                "type": {
                    "$numberInt": "1"
                },
                "schema": {
                    "db.test": {
                        "escCollection": "enxcol_.test.esc",
                        "ecocCollection": "enxcol_.test.ecoc",
                        "fields": [
                            {
                                "keyId": {
                                    "$binary": {
                                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                        "subType": "04"
                                    }
                                },
                                "path": "encrypted",
                                "bsonType": "int",
                                "queries": {
                                    "queryType": "equality",
                                    "contention": {
                                        "$numberLong": "0"
                                    }
                                }
                            }
                        ]
                    }
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/cmd.json000066400000000000000000000004601521103432300232530ustar00rootroot00000000000000{
   "bulkWrite": 1,
   "ops": [
      {
         "insert": 0,
         "document": {
            "plainText": "sample",
            "encrypted": {
               "$numberInt": "123"
            }
         }
      }
   ],
   "nsInfo": [
      {
         "ns": "db.test"
      }
   ],
   "$db": "admin"
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/collinfo.json000066400000000000000000000021431521103432300243150ustar00rootroot00000000000000{
    "name": "test",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.test.esc",
            "ecocCollection": "enxcol_.test.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "4z9fIUN+Sh2WeH/aySC+OA==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/encrypted-field-map.json000066400000000000000000000010621521103432300263400ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "enxcol_.test.esc",
      "ecocCollection": "enxcol_.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                  "$numberLong": "0"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/encrypted-payload-pattern.json000066400000000000000000000025101521103432300276050ustar00rootroot00000000000000{
   "bulkWrite": {
      "$numberInt": "1"
   },
   "ops": [
      {
         "insert": {
            "$numberInt": "0"
         },
         "document": {
            "plainText": "sample",
            "encrypted": {
               "$$type": "binData"
            }
         }
      }
   ],
   "nsInfo": [
      {
         "ns": "db.test",
         "encryptionInformation": {
            "type": {
               "$numberInt": "1"
            },
            "schema": {
               "db.test": {
                  "escCollection": "enxcol_.test.esc",
                  "ecocCollection": "enxcol_.test.ecoc",
                  "fields": [
                     {
                        "keyId": {
                           "$binary": {
                              "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                              "subType": "04"
                           }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                           "queryType": "equality",
                           "contention": {
                              "$numberLong": "0"
                           }
                        }
                     }
                  ]
               }
            }
         }
      }
   ],
   "$db": "admin"
}
libmongocrypt-1.19.0/test/data/bulkWrite/simple/mongocryptd-reply.json000066400000000000000000000042351521103432300262120ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "bulkWrite": {
            "$numberInt": "1"
        },
        "ops": [
            {
                "insert": {
                    "$numberInt": "0"
                },
                "document": {
                    "plainText": "sample",
                    "encrypted": {
                        "$binary": {
                            "base64": "A30AAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABGFhYWFhYWFhYWFhYWFhYWEFa3UAEAAAAARhYWFhYWFhYWFhYWFhYWFhA3YAHgAAABB2AHsAAAAQbWluAAAAAAAQbWF4AMgAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "nsInfo": [
            {
                "ns": "db.test",
                "encryptionInformation": {
                    "type": {
                        "$numberInt": "1"
                    },
                    "schema": {
                        "db.test": {
                            "escCollection": "enxcol_.test.esc",
                            "ecocCollection": "enxcol_.test.ecoc",
                            "fields": [
                                {
                                    "keyId": {
                                        "$binary": {
                                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                            "subType": "04"
                                        }
                                    },
                                    "path": "encrypted",
                                    "bsonType": "int",
                                    "queries": {
                                        "queryType": "equality",
                                        "contention": {
                                            "$numberLong": "0"
                                        }
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        ]
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/bulkWrite/unencrypted/000077500000000000000000000000001521103432300226645ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/bulkWrite/unencrypted/cmd-to-mongocryptd.json000066400000000000000000000012341521103432300273050ustar00rootroot00000000000000{
    "bulkWrite": {
        "$numberInt": "1"
    },
    "ops": [
        {
            "insert": 0,
            "document": {
                "plainText": "sample"
            }
        }
    ],
    "nsInfo": [
        {
            "ns": "db.test",
            "encryptionInformation": {
                "type": {
                    "$numberInt": "1"
                },
                "schema": {
                    "db.test": {
                        "escCollection": "enxcol_.test.esc",
                        "ecocCollection": "enxcol_.test.ecoc",
                        "fields": []
                    }
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/bulkWrite/unencrypted/cmd.json000066400000000000000000000003431521103432300243220ustar00rootroot00000000000000{
   "bulkWrite": 1,
   "ops": [
      {
         "insert": 0,
         "document": {
            "plainText": "sample"
         }
      }
   ],
   "nsInfo": [
      {
         "ns": "db.test"
      }
   ],
   "$db": "admin"
}
libmongocrypt-1.19.0/test/data/bulkWrite/unencrypted/mongocryptd-reply.json000066400000000000000000000017331521103432300272610ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": false,
    "result": {
        "bulkWrite": {
            "$numberInt": "1"
        },
        "ops": [
            {
                "insert": {
                    "$numberInt": "0"
                },
                "document": {
                    "plainText": "sample"
                }
            }
        ],
        "nsInfo": [
            {
                "ns": "db.test",
                "encryptionInformation": {
                    "type": {
                        "$numberInt": "1"
                    },
                    "schema": {
                        "db.test": {
                            "escCollection": "enxcol_.test.esc",
                            "ecocCollection": "enxcol_.test.ecoc",
                            "fields": []
                        }
                    }
                }
            }
        ]
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/bulkWrite/unencrypted/payload.json000066400000000000000000000004511521103432300252100ustar00rootroot00000000000000{
   "bulkWrite": {
      "$numberInt": "1"
   },
   "ops": [
      {
         "insert": {
            "$numberInt": "0"
         },
         "document": {
            "plainText": "sample"
         }
      }
   ],
   "nsInfo": [
      {
         "ns": "db.test"
      }
   ],
   "$db": "admin"
}
libmongocrypt-1.19.0/test/data/cache-tests.json000066400000000000000000000126241521103432300214570ustar00rootroot00000000000000[
  {
    "description": "basic test, no overwriting",
    "cached": [{ "_id": 0, "keyAltNames": ["alt0", "alt2"] }],
    "requests": [
      { "_id": 0 },
      { "keyAltName": "alt2" },
      { "keyAltName": "alt1" }
    ],
    "replies": [{ "_id": 1, "keyAltNames": ["alt1"] }],
    "expect": {
      "cached": [
        { "_id": 0, "keyAltNames": ["alt0", "alt2"] },
        { "_id": 1, "keyAltNames": ["alt1"] }
      ]
    }
  },
  {
    "description": "single key overwritten",
    "cached": [{ "_id": 0, "keyAltNames": [] }],
    "requests": [{ "keyAltName": "alt0" }],
    "replies": [{ "_id": 0, "keyAltNames": ["alt0"] }],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0"] }]
    }
  },
  {
    "description": "empty cache",
    "cached": [],
    "requests": [{ "keyAltName": "alt0" }],
    "replies": [{ "_id": 0, "keyAltNames": ["alt0"] }],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0"] }]
    }
  },
  {
    "description": "multiple keys overwritten",
    "cached": [{ "_id": 0 }, { "_id": 1 }],
    "requests": [{ "keyAltName": "alt0" }, { "keyAltName": "alt1" }],
    "replies": [
      { "_id": 0, "keyAltNames": ["alt0"] },
      { "_id": 1, "keyAltNames": ["alt1"] }
    ],
    "expect": {
      "cached": [
        { "_id": 0, "keyAltNames": ["alt0"] },
        { "_id": 1, "keyAltNames": ["alt1"] }
      ]
    }
  },
  {
    "description": "multiple keys overwritten by one",
    "cached": [
      { "_id": 0, "keyAltNames": ["alt0"] },
      { "_id": 1, "keyAltNames": ["alt1"] }
    ],
    "requests": [{ "keyAltName": "alt2" }],
    "replies": [{ "_id": 2, "keyAltNames": ["alt0", "alt1", "alt2"] }],
    "expect": {
      "cached": [{ "_id": 2, "keyAltNames": ["alt0", "alt1", "alt2"] }]
    }
  },
  {
    "description": "multiple keys with the same id returned",
    "cached": [],
    "requests": [{ "keyAltName": "alt2" }, { "_id": 2 }],
    "replies": [
        { "_id": 2, "keyAltNames": ["alt2"] },
        { "_id": 2 }
    ],
    "expect": {
      "errmsg": "keys returned have duplicate keyAltNames or _id"
    }
  },
  {
    "description": "multiple keys with the same alt name returned",
    "cached": [],
    "requests": [{ "keyAltName": "alt2" }],
    "replies": [
        { "_id": 0, "keyAltNames": ["alt2"] },
        { "_id": 1, "keyAltNames": ["alt2"] }
    ],
    "expect": {
      "errmsg": "keys returned have duplicate keyAltNames or _id"
    }
  },
  {
    "description": "key returned does not match request",
    "cached": [],
    "requests": [{ "keyAltName": "alt0" }],
    "replies": [
        { "_id": 0, "keyAltNames": ["alt1"] }
    ],
    "expect": {
      "errmsg": "unexpected key returned"
    }
  },
  {
    "description": "not all keys are supplied",
    "cached": [],
    "requests": [
      { "keyAltName": "alt2" },
      { "keyAltName": "alt1" }
    ],
    "replies": [
        { "_id": 0, "keyAltNames": ["alt1"] }
    ],
    "expect": {
      "errmsg": "not all keys requested were satisfied"
    }
  },
  {
    "description": "one key matches multiple requests",
    "cached": [],
    "requests": [
      { "keyAltName": "alt2" },
      { "keyAltName": "alt1" },
      { "keyAltName": "alt0" },
      { "_id": 0 }
    ],
    "replies": [
        { "_id": 0, "keyAltNames": ["alt0", "alt1", "alt2"] }
    ],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0", "alt1", "alt2"] }]
    }
  },
  {
    "description": "extra keys are supplied",
    "cached": [],
    "requests": [{ "keyAltName": "alt0" }],
    "replies": [
        { "_id": 0, "keyAltNames": ["alt0"] },
        { "_id": 1 }
    ],
    "expect": {
      "errmsg": "unexpected key returned"
    }
  },
  {
    "description": "update cache with a conflicting key",
    "cached": [ { "_id": 0, "keyAltNames": ["alt0"] } ],
    "requests": [{ "keyAltName": "alt1" }],
    "replies": [
        { "_id": 1, "keyAltNames": ["alt0", "alt1"] }
    ],
    "expect": {
      "cached": [ { "_id": 1, "keyAltNames": ["alt0", "alt1"] } ]
    }
  },
  {
    "description": "return duplicate key alt names",
    "cached": [],
    "requests": [{ "keyAltName": "alt1" }],
    "replies": [
        { "_id": 1, "keyAltNames": ["alt0", "alt0", "alt1"] }
    ],
    "expect": {
      "errmsg": "unexpected duplicate keyAltNames"
    }
  },
  {
    "description": "everything satisfied by cache",
    "cached": [{ "_id": 0 }, { "_id": 1, "keyAltNames": ["alt0"] }],
    "requests": [{"_id": 0}, {"_id": 1}, { "keyAltName": "alt0" }],
    "expect": {
      "cached": [{ "_id": 0 }, { "_id": 1, "keyAltNames": ["alt0"] }]
    }
  },
  {
    "description": "duplicate requests for _id's",
    "cached": [],
    "requests": [{"_id": 0}, {"_id": 0}],
    "replies": [{"_id": 0, "keyAltNames": ["alt0"]}],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0"] }]
    }
  },
  {
    "description": "duplicate requests for keyAltName's",
    "cached": [],
    "requests": [{"keyAltName": "alt0"}, {"keyAltName": "alt0"}],
    "replies": [{"_id": 0, "keyAltNames": ["alt0"]}],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0"] }]
    }
  },
  {
    "description": "test with both local and non-local keys",
    "cached": [],
    "requests": [{"keyAltName": "alt0"}, {"keyAltName": "alt1"}],
    "replies": [{"_id": 0, "keyAltNames": ["alt0"]}, {"_id": 1, "keyAltNames": ["alt1"], "local": true}],
    "expect": {
      "cached": [{ "_id": 0, "keyAltNames": ["alt0"] }, { "_id": 1, "keyAltNames": ["alt1"], "local": true }]
    }
  }
]
libmongocrypt-1.19.0/test/data/cleanup/000077500000000000000000000000001521103432300200035ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/cleanup/missing-key-id/000077500000000000000000000000001521103432300226345ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/cleanup/missing-key-id/collinfo.json000066400000000000000000000010571521103432300253370ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "fle2.basic.esc",
            "ecocCollection": "fle2.basic.ecoc",
            "fields": [
                {
                    "path": "missingKeyId",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/cleanup/no-fields/000077500000000000000000000000001521103432300216635ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/cleanup/no-fields/collinfo.json000066400000000000000000000002711521103432300243630ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": []
        }
    }
}
libmongocrypt-1.19.0/test/data/cleanup/no-fields/encrypted-payload.json000066400000000000000000000001131521103432300261750ustar00rootroot00000000000000{
    "cleanupStructuredEncryptionData": "test",
    "cleanupTokens": {}
 }libmongocrypt-1.19.0/test/data/cleanup/success/000077500000000000000000000000001521103432300214535ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/cleanup/success/cmd.json000066400000000000000000000000561521103432300231120ustar00rootroot00000000000000{ "cleanupStructuredEncryptionData": "test" }
libmongocrypt-1.19.0/test/data/cleanup/success/collinfo.json000066400000000000000000000040211521103432300241500ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "rangeField",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "range",
                        "contention": 0,
                        "sparsity": 1
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/cleanup/success/encrypted-field-config-map.json000066400000000000000000000033621521103432300274460ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "q83vqxI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "rangeField",
                "bsonType": "int",
                "queries": {
                    "queryType": "range",
                    "contention": 0,
                    "sparsity": 1
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/cleanup/success/encrypted-payload.json000066400000000000000000000017061521103432300257760ustar00rootroot00000000000000{
   "cleanupStructuredEncryptionData": "test",
   "cleanupTokens": {
      "rangeField": {
        "ecoc": {
            "$binary": {
                "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                "subType": "00"
            }
        },
        "anchorPaddingToken": {
            "$binary": {
                "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                "subType": "00"
            }
        }
      },
      "nested.notindexed": {
         "$binary": {
            "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
            "subType": "00"
         }
      },
      "nested.encrypted": {
         "$binary": {
            "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
            "subType": "00"
         }
      },
      "encrypted": {
         "$binary": {
            "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
            "subType": "00"
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/cleanup/text-search/000077500000000000000000000000001521103432300222325ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/cleanup/text-search/cmd.json000066400000000000000000000000561521103432300236710ustar00rootroot00000000000000{ "cleanupStructuredEncryptionData": "test" }
libmongocrypt-1.19.0/test/data/cleanup/text-search/collinfo.json000066400000000000000000000067001521103432300247350ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "textField1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "substringPreview",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMaxLength": 100,
                        "strMinQueryLength": 5,
                        "strMaxQueryLength": 20,
                        "caseSensitive": false,
                        "diacriticSensitive": true
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "textField2",
                    "bsonType": "string",
                    "queries": [{
                        "queryType": "suffix",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMinQueryLength": 1,
                        "strMaxQueryLength": 10,
                        "caseSensitive": true,
                        "diacriticSensitive": false
                    }, 
                    {
                        "queryType": "prefix",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMinQueryLength": 5,
                        "strMaxQueryLength": 15,
                        "caseSensitive": true,
                        "diacriticSensitive": false
                    }]
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/cleanup/text-search/encrypted-field-config-map.json000066400000000000000000000060211521103432300302200ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "q83vqxI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "textField1",
                "bsonType": "string",
                "queries": {
                    "queryType": "substringPreview",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMaxLength": 100,
                    "strMinQueryLength": 5,
                    "strMaxQueryLength": 20,
                    "caseSensitive": false,
                    "diacriticSensitive": true
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "textField2",
                "bsonType": "string",
                "queries": [{
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMinQueryLength": 1,
                    "strMaxQueryLength": 10,
                    "caseSensitive": true,
                    "diacriticSensitive": false
                }, 
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMinQueryLength": 5,
                    "strMaxQueryLength": 15,
                    "caseSensitive": true,
                    "diacriticSensitive": false
                }]
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/cleanup/text-search/encrypted-payload.json000066400000000000000000000025761521103432300265630ustar00rootroot00000000000000{
   "cleanupStructuredEncryptionData": "test",
   "cleanupTokens": {
      "textField2": {
         "ecoc": {
               "$binary": {
                  "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                  "subType": "00"
               }
         },
         "anchorPaddingToken": {
               "$binary": {
                  "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                  "subType": "00"
               }
         }
      },
      "textField1": {
         "ecoc": {
               "$binary": {
                  "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                  "subType": "00"
               }
         },
         "anchorPaddingToken": {
               "$binary": {
                  "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                  "subType": "00"
               }
         }
      },
      "nested.notindexed": {
         "$binary": {
            "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
            "subType": "00"
         }
      },
      "nested.encrypted": {
         "$binary": {
            "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
            "subType": "00"
         }
      },
      "encrypted": {
         "$binary": {
            "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
            "subType": "00"
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/collection-info-no-validator.json000066400000000000000000000003441521103432300247310ustar00rootroot00000000000000{
    "name" : "test",
    "type" : "collection",
    "options" : {},
    "info" : {},
    "idIndex" : {
            "v" : 2,
            "key" : {
                    "_id" : 1
            },
            "name" : "_id_"
    }
}libmongocrypt-1.19.0/test/data/collection-info-view.json000066400000000000000000000002071521103432300233020ustar00rootroot00000000000000{
  "name": "v",
  "type": "view",
  "options": {
    "viewOn": "coll",
    "pipeline": []
  },
  "info": {
    "readOnly": true
  }
}
libmongocrypt-1.19.0/test/data/collinfo-siblings.json000066400000000000000000000021241521103432300226630ustar00rootroot00000000000000{
    "type": "collection", 
    "name": "test", 
    "idIndex": {
        "ns": "test.test", 
        "name": "_id_", 
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        }, 
        "v": {
            "$numberInt": "2"
        }
    }, 
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "ssn": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string", 
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                }, 
                "bsonType": "object"
            },
            "sibling": {}
        }
    }
}libmongocrypt-1.19.0/test/data/compact/000077500000000000000000000000001521103432300200025ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/anchor-pad/000077500000000000000000000000001521103432300220165ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/anchor-pad/cmd.json000066400000000000000000000001131521103432300234470ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test", "anchorPaddingFactor": 0.47 }
libmongocrypt-1.19.0/test/data/compact/anchor-pad/collinfo.json000066400000000000000000000040211521103432300245130ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "rangeField",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "range",
                        "contention": 0,
                        "sparsity": 1
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/anchor-pad/encrypted-payload.json000066400000000000000000000066341521103432300263460ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "test",
    "anchorPaddingFactor": 0.47,
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "q83vqxI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.notindexed",
                        "bsonType": "string"
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "rangeField",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range",
                            "contention": 0,
                            "sparsity": 1
                        }
                    }
                ]
            }
        }
    },
    "compactionTokens": {
        "rangeField": {
            "ecoc": {
                "$binary": {
                    "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                    "subType": "00"
                }
            },
            "anchorPaddingToken": {
                "$binary": {
                    "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                    "subType": "00"
                }
            }
        },
        "nested.notindexed": {
            "$binary": {
                "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                "subType": "00"
            }
        },
        "nested.encrypted": {
            "$binary": {
                "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
                "subType": "00"
            }
        },
        "encrypted": {
            "$binary": {
                "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
                "subType": "00"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/missing-key-id/000077500000000000000000000000001521103432300226335ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/missing-key-id/collinfo.json000066400000000000000000000010571521103432300253360ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "fle2.basic.esc",
            "ecocCollection": "fle2.basic.ecoc",
            "fields": [
                {
                    "path": "missingKeyId",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/no-fields/000077500000000000000000000000001521103432300216625ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/no-fields/collinfo.json000066400000000000000000000002711521103432300243620ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": []
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/no-fields/encrypted-payload.json000066400000000000000000000001161521103432300261770ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "test",
    "compactionTokens": {}
 }libmongocrypt-1.19.0/test/data/compact/no-range/000077500000000000000000000000001521103432300215105ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/no-range/cmd.json000066400000000000000000000000561521103432300231470ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test" }
libmongocrypt-1.19.0/test/data/compact/no-range/collinfo.json000066400000000000000000000030041521103432300242050ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/no-range/encrypted-payload.json000066400000000000000000000011011521103432300260200ustar00rootroot00000000000000{
   "compactStructuredEncryptionData": "test",
   "compactionTokens": {
      "nested.notindexed": {
         "$binary": {
            "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
            "subType": "00"
         }
      },
      "nested.encrypted": {
         "$binary": {
            "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
            "subType": "00"
         }
      },
      "encrypted": {
         "$binary": {
            "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
            "subType": "00"
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/compact/success/000077500000000000000000000000001521103432300214525ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/success/cmd.json000066400000000000000000000000561521103432300231110ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test" }
libmongocrypt-1.19.0/test/data/compact/success/collinfo.json000066400000000000000000000040211521103432300241470ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "rangeField",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "range",
                        "contention": 0,
                        "sparsity": 1
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/success/encrypted-field-config-map.json000066400000000000000000000033621521103432300274450ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "q83vqxI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "rangeField",
                "bsonType": "int",
                "queries": {
                    "queryType": "range",
                    "contention": 0,
                    "sparsity": 1
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/compact/success/encrypted-payload.json000066400000000000000000000065731521103432300260040ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "test",
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "q83vqxI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.notindexed",
                        "bsonType": "string"
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "rangeField",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range",
                            "contention": 0,
                            "sparsity": 1
                        }
                    }
                ]
            }
        }
    },
    "compactionTokens": {
        "rangeField": {
            "ecoc": {
                "$binary": {
                    "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                    "subType": "00"
                }
            },
            "anchorPaddingToken": {
                "$binary": {
                    "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                    "subType": "00"
                }
            }
        },
        "nested.notindexed": {
            "$binary": {
                "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                "subType": "00"
            }
        },
        "nested.encrypted": {
            "$binary": {
                "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
                "subType": "00"
            }
        },
        "encrypted": {
            "$binary": {
                "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
                "subType": "00"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/text-search/000077500000000000000000000000001521103432300222315ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/compact/text-search/cmd.json000066400000000000000000000000561521103432300236700ustar00rootroot00000000000000{ "compactStructuredEncryptionData": "test" }
libmongocrypt-1.19.0/test/data/compact/text-search/collinfo.json000066400000000000000000000067001521103432300247340ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "textField1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "substringPreview",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMaxLength": 100,
                        "strMinQueryLength": 5,
                        "strMaxQueryLength": 20,
                        "caseSensitive": false,
                        "diacriticSensitive": true
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "textField2",
                    "bsonType": "string",
                    "queries": [{
                        "queryType": "suffix",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMinQueryLength": 1,
                        "strMaxQueryLength": 10,
                        "caseSensitive": true,
                        "diacriticSensitive": false
                    }, 
                    {
                        "queryType": "prefix",
                        "contention": {
                            "$numberLong": "0"
                        },
                        "strMinQueryLength": 5,
                        "strMaxQueryLength": 15,
                        "caseSensitive": true,
                        "diacriticSensitive": false
                    }]
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/compact/text-search/encrypted-field-config-map.json000066400000000000000000000060211521103432300302170ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "q83vqxI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "textField1",
                "bsonType": "string",
                "queries": {
                    "queryType": "substringPreview",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMaxLength": 100,
                    "strMinQueryLength": 5,
                    "strMaxQueryLength": 20,
                    "caseSensitive": false,
                    "diacriticSensitive": true
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "textField2",
                "bsonType": "string",
                "queries": [{
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMinQueryLength": 1,
                    "strMaxQueryLength": 10,
                    "caseSensitive": true,
                    "diacriticSensitive": false
                }, 
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    },
                    "strMinQueryLength": 5,
                    "strMaxQueryLength": 15,
                    "caseSensitive": true,
                    "diacriticSensitive": false
                }]
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/compact/text-search/encrypted-payload.json000066400000000000000000000127251521103432300265570ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "test",
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "q83vqxI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.encrypted",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "nested.notindexed",
                        "bsonType": "string"
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "textField1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "substringPreview",
                            "contention": {
                                "$numberLong": "0"
                            },
                            "strMaxLength": 100,
                            "strMinQueryLength": 5,
                            "strMaxQueryLength": 20,
                            "caseSensitive": false,
                            "diacriticSensitive": true
                        }
                    },
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                                "subType": "04"
                            }
                        },
                        "path": "textField2",
                        "bsonType": "string",
                        "queries": [{
                            "queryType": "suffix",
                            "contention": {
                                "$numberLong": "0"
                            },
                            "strMinQueryLength": 1,
                            "strMaxQueryLength": 10,
                            "caseSensitive": true,
                            "diacriticSensitive": false
                        }, 
                        {
                            "queryType": "prefix",
                            "contention": {
                                "$numberLong": "0"
                            },
                            "strMinQueryLength": 5,
                            "strMaxQueryLength": 15,
                            "caseSensitive": true,
                            "diacriticSensitive": false
                        }]
                    }
                ],
                "strEncodeVersion": {
                    "$numberInt": "1"
                }
            }
        }
    },
    "compactionTokens": {
        "textField2": {
            "ecoc": {
                "$binary": {
                    "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                    "subType": "00"
                }
            },
            "anchorPaddingToken": {
                "$binary": {
                    "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                    "subType": "00"
                }
            }
        },
        "textField1": {
            "ecoc": {
                "$binary": {
                    "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                    "subType": "00"
                }
            },
            "anchorPaddingToken": {
                "$binary": {
                    "base64": "hjezd/cwUfInCg0WjvFlzdn9/BQa8upEyogsU5pMWMU=",
                    "subType": "00"
                }
            }
        },
        "nested.notindexed": {
            "$binary": {
                "base64": "27J6DZqcjkRzZ3lWEsxH7CsQHr4CZirrGmuPS8ZkRO0=",
                "subType": "00"
            }
        },
        "nested.encrypted": {
            "$binary": {
                "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=",
                "subType": "00"
            }
        },
        "encrypted": {
            "$binary": {
                "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=",
                "subType": "00"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/000077500000000000000000000000001521103432300201375ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/dollardb/omitted/000077500000000000000000000000001521103432300216045ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/dollardb/omitted/cmd-to-mongocryptd.json000066400000000000000000000016601521103432300262300ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "value": 123456
    },
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "value",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberLong": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/omitted/cmd.json000066400000000000000000000001001521103432300232310ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   }
}libmongocrypt-1.19.0/test/data/dollardb/omitted/collinfo.json000066400000000000000000000013621521103432300243060ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "value",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/omitted/encrypted-payload.json000066400000000000000000000020661521103432300261270ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": {
         "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
            "subType": "06"
         }
      }
   },
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "value",
                  "bsonType": "int",
                  "queries": {
                     "queryType": "equality",
                     "contention": {
                        "$numberLong": "0"
                     }
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/dollardb/omitted/mongocryptd-reply.json000066400000000000000000000023041521103432300261740ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "find": "test",
      "filter": {
         "value": {
            "$binary": {
               "base64": "A1gAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASEHYAQOIBABJjbQAAAAAAAAAAAAA=",
               "subType": "06"
            }
         }
      },
      "encryptionInformation": {
         "type": 1,
         "schema": {
            "db.test": {
               "escCollection": "esc",
               "ecocCollection": "ecoc",
               "fields": [
                  {
                     "keyId": {
                        "$binary": {
                           "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                           "subType": "04"
                        }
                     },
                     "path": "value",
                     "bsonType": "int",
                     "queries": {
                        "queryType": "equality",
                        "contention": {
                           "$numberLong": "0"
                        }
                     }
                  }
               ]
            }
         }
      }
   },
   "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/dollardb/preserved/000077500000000000000000000000001521103432300221365ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/dollardb/preserved/cmd-to-mongocryptd.json000066400000000000000000000016601521103432300265620ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "value": 123456
    },
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "value",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberLong": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/preserved/cmd.json000066400000000000000000000001201521103432300235650ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   },
   "$db": "db"
}libmongocrypt-1.19.0/test/data/dollardb/preserved/collinfo.json000066400000000000000000000013621521103432300246400ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "value",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/preserved/encrypted-payload.json000066400000000000000000000021061521103432300264540ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": {
         "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
            "subType": "06"
         }
      }
   },
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "value",
                  "bsonType": "int",
                  "queries": {
                     "queryType": "equality",
                     "contention": {
                        "$numberLong": "0"
                     }
                  }
               }
            ]
         }
      }
   },
   "$db": "db"
}
libmongocrypt-1.19.0/test/data/dollardb/preserved/mongocryptd-reply.json000066400000000000000000000023041521103432300265260ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "find": "test",
      "filter": {
         "value": {
            "$binary": {
               "base64": "A1gAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASEHYAQOIBABJjbQAAAAAAAAAAAAA=",
               "subType": "06"
            }
         }
      },
      "encryptionInformation": {
         "type": 1,
         "schema": {
            "db.test": {
               "escCollection": "esc",
               "ecocCollection": "ecoc",
               "fields": [
                  {
                     "keyId": {
                        "$binary": {
                           "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                           "subType": "04"
                        }
                     },
                     "path": "value",
                     "bsonType": "int",
                     "queries": {
                        "queryType": "equality",
                        "contention": {
                           "$numberLong": "0"
                        }
                     }
                  }
               ]
            }
         }
      }
   },
   "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/000077500000000000000000000000001521103432300233545ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/cmd-to-mongocryptd.json000066400000000000000000000004631521103432300300000ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "value": 123456
    },
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": []
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/cmd.json000066400000000000000000000001201521103432300250030ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   },
   "$db": "db"
}libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/collinfo.json000066400000000000000000000002711521103432300260540ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": []
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/encrypted-payload.json000066400000000000000000000001201521103432300276640ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   },
   "$db": "db"
}libmongocrypt-1.19.0/test/data/dollardb/preserved_empty/mongocryptd-reply.json000066400000000000000000000006411521103432300277460ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "find": "test",
      "filter": {
         "value": 123456
      },
      "encryptionInformation": {
         "type": 1,
         "schema": {
            "db.test": {
               "escCollection": "esc",
               "ecocCollection": "ecoc",
               "fields": []
            }
         }
      }
   },
   "hasEncryptedPlaceholders": false
}
libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/000077500000000000000000000000001521103432300230455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/cmd-to-mongocryptd.json000066400000000000000000000012551521103432300274710ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "value": 123456
    },
    "jsonSchema": {
        "properties": {
            "value": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "int",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        },
        "bsonType": "object"
    },
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/cmd.json000066400000000000000000000001201521103432300244740ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   },
   "$db": "db"
}libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/collinfo.json000066400000000000000000000014641521103432300255520ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "value": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "int",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/encrypted-payload.json000066400000000000000000000005031521103432300273620ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": {
         "$eq": {
            "$binary": {
               "base64": "ARI0VngSNJh2EjQSNFZ4kBIQl2bQhMRGH/FbUJH1jGuUSHGQS6UYOSv5kqZrzd4hKx1erD8rDjS/c50CQ6nQb1S2WKlgWL3SeoyT23g//BNWfA==",
               "subType": "06"
            }
         }
      }
   },
   "$db": "db"
}libmongocrypt-1.19.0/test/data/dollardb/preserved_fle1/mongocryptd-reply.json000066400000000000000000000005551521103432300274430ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "find": "test",
        "filter": {
            "value": {
                "$eq": {
                    "$binary": "ACwAAAAQYQABAAAABWtpABAAAAAEEjRWeBI0mHYSNBI0VniQEhB2AEDiAQAA",
                    "$type": "06"
                }
            }
        }
    }
}libmongocrypt-1.19.0/test/data/efc/000077500000000000000000000000001521103432300171115ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/efc/efc-duplicateKeyAltName.json000066400000000000000000000012771521103432300244330ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyAltName": "myKeyAltName",
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        },
        {
            "keyAltName": "myKeyAltName",
            "path": "lastName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ]
}

libmongocrypt-1.19.0/test/data/efc/efc-extraField.json000066400000000000000000000011031521103432300226210ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            },
            "extraField": "ignoreMe"
        }
    ]
}
libmongocrypt-1.19.0/test/data/efc/efc-missingKeyId.json000066400000000000000000000005551521103432300231430ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "path": "missingKeyId",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/efc/efc-oneField-badVersionSet.json000066400000000000000000000010711521103432300250310ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ],
    "strEncodeVersion": 99
}
libmongocrypt-1.19.0/test/data/efc/efc-oneField-goodVersionSet.json000066400000000000000000000010701521103432300252320ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ],
    "strEncodeVersion": 1
}
libmongocrypt-1.19.0/test/data/efc/efc-oneField.json000066400000000000000000000010351521103432300222630ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/efc/efc-textSearchFields-badVersionSet.json000066400000000000000000000024011521103432300265430ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "eccCollection": "fle2.basic.ecc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "substringPreview",
                "contention": {
                    "$numberLong": "0"
                }
            }
        },
        {
            "keyId": {
                "$binary": {
                    "base64": "q83vqxI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "lastName",
            "bsonType": "string",
            "queries": [
                {
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    }
                },
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            ]
        }
    ],
    "strEncodeVersion": 99
}libmongocrypt-1.19.0/test/data/efc/efc-textSearchFields-goodVersionSet.json000066400000000000000000000024001521103432300267440ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "eccCollection": "fle2.basic.ecc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "substringPreview",
                "contention": {
                    "$numberLong": "0"
                }
            }
        },
        {
            "keyId": {
                "$binary": {
                    "base64": "q83vqxI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "lastName",
            "bsonType": "string",
            "queries": [
                {
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    }
                },
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            ]
        }
    ],
    "strEncodeVersion": 1
}libmongocrypt-1.19.0/test/data/efc/efc-textSearchFields.json000066400000000000000000000036011521103432300240000ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "eccCollection": "fle2.basic.ecc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "substringPreview",
                "contention": {
                    "$numberLong": "0"
                }
            }
        },
        {
            "keyId": {
                "$binary": {
                    "base64": "q83vqxI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "lastName",
            "bsonType": "string",
            "queries": [
                {
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    }
                },
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            ]
        },
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VqvN7w==",
                    "subType": "04"
                }
            },
            "path": "middleName",
            "bsonType": "string",
            "queries": [
                {
                    "queryType": "suffix",
                    "contention": {
                        "$numberLong": "0"
                    }
                },
                {
                    "queryType": "prefix",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            ]
        }
    ]
}libmongocrypt-1.19.0/test/data/efc/efc-twoFields.json000066400000000000000000000017201521103432300224770ustar00rootroot00000000000000{
    "escCollection": "fle2.basic.esc",
    "ecocCollection": "fle2.basic.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "firstName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        },
        {
            "keyId": {
                "$binary": {
                    "base64": "q83vqxI0mHYSNBI0VniQEg==",
                    "subType": "04"
                }
            },
            "path": "lastName",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": {
                    "$numberLong": "0"
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/encrypted-cmd.json000066400000000000000000000004101521103432300220000ustar00rootroot00000000000000{
  "filter": {
    "ssn": {
      "$binary": {
        "base64": "AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=",
        "subType": "06"
      }
    }
  },
  "find": "test"
}
libmongocrypt-1.19.0/test/data/encrypted-field-config-map.json000066400000000000000000000024121521103432300243420ustar00rootroot00000000000000{
    "test.test": {
        "escCollection": "fle2.basic.esc",
        "ecocCollection": "fle2.basic.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "KEY1+AAAAAAAAAAAAAAAAA==",
                        "subType": "04"
                    }
                },
                "path": "firstName",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            }
        ]
    },
    "test.test2": {
        "escCollection": "fle2.basic.esc",
        "ecocCollection": "fle2.basic.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "KEY2+AAAAAAAAAAAAAAAAA==",
                        "subType": "04"
                    }
                },
                "path": "firstName",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": {
                        "$numberLong": "0"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/000077500000000000000000000000001521103432300216455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE1DeterministicEncryptedValue.json000066400000000000000000000003321521103432300306240ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "AavN76sSNJh2EjQSNFZ4kBICWqrPwniNou6js6HYziih7aohQYE5RKpYxnRtdQ6in5s3qv9mG/5lkEDIPRGcEPNKXwBz+iYB1mB1rgGZEQHikg==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE1EncryptionPlaceholder.json000066400000000000000000000002661521103432300274510ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE1RandomEncryptedValue.json000066400000000000000000000003321521103432300272410ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "AqvN76sSNJh2EjQSNFZ4kBIC6BSCq8jmrdY68Q0LB4ZVCxehoOHbyehgjllrluoTz0kOjm+6ppKu5a7XG0mMcZFRxZ6yZK/6GhSCQFN1H/Bp7A==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2EncryptionPlaceholder.json000066400000000000000000000003561521103432300274520ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "A2EAAAAQdAABAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2FindEqualityPayload.json000066400000000000000000000005311521103432300270600ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BbEAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefIFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2FindEqualityPayloadV2.json000066400000000000000000000004421521103432300272710ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2IndexedEqualityEncryptedValue.json000066400000000000000000000006261521103432300311260ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BxI0VngSNJh2EjQSNFZ4kBICQ7uhTd9C2oI8M1afRon0ZaYG0s6oTmt0aBZ9kO4S4mm5vId01BsW7tBHytA8pDJ2IiWBCmah3OGH2M4ET7PSqekQD4gkUCo4JeEttx4yj05Ou4D6yZUmYfVKmEljge16NCxKm7Ir9gvmQsp8x1wqGBzpndA6gkqFxsxfvQ/cIqOwMW9dGTTWsfKge+jYkCUIFMfms+XyC/8evQhjjA+qR6eEmV+N/kwpR7Q7TJe0lwU5kw2kSe3/KiPKRZZTbn8znadvycfJ0cCWGad9SQ==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2IndexedEqualityEncryptedValueV2.json000066400000000000000000000006021521103432300313300ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "DqvN76sSNJh2EjQSNFZ4kBICjqZv6Pljqv2xNVm5MLXcKSRYXy+Q5frRjpTAPx0hCr+daRXntN5pv6qMh0MXY+IPnH8XOG3xXZcllswF0N3EfyZsHii2am4GdIAkxsqu6MmuAIT5x+7BZqcHIu0n7hG3mxZKPQvs8MjoBQZ2lC/dgj15+bR9OT2rLi4XHwi7kjrhV1BtSk560yuV7W1RPVQtZfW7uibsXigZQduDro7IgXoJXDTGekdyZYTZVQZlZwHAJZCdr+1sLPRS9PtkKSAE",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2IndexedRangeEncryptedValue.json000066400000000000000000000063561521103432300303730ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "CRI0VngSNJh2EjQSNFZ4kBIQsPF0Ii0Hfv7ZMhnNt/yt+mviydF8EUw0YlO+amC3IF8dX2J/GmRZRnihW3VqJYoLMk0BIit0x9YQiQEEQPxPcTXnCx4t1fquOY7cRGqIAWTDHuQ9AdUw94EY1J55mq9UhwD7flh0ySR/SbkTwjIU32U1iM6Bv4AriE4smI87Yd0V7Z7kDoDx7afx9vM/+h9NWZvpfYcZ+P32sfILb3BdXT5zLrkyc5Xb3myDxE9abTrR8ePG0YuEmeqwGE4bZ6QHKzd/RLmHciWstKOtER5uRpo3p570wGO8QE9QtQoJp/7N7Su30dK/bk59hVvlNO6i3nUPwqMd13DePobNGn84q3Fag5O4Kw8P4EGfomFWzxydlVQ0SppGVfan9tIj1CFu5doYYT4adzX7L9HinKsTWE5ctD9Qxhhzb2cVs5JO96j4mpwOaloF/4qJhyqlzTEpoCXGQ0X9aeEplibFxQ7FJkaFfYzIDIxA2d6lzwVel3j+VwQ7zOP/bCnaFu6EP1OQw3ZarsaWGENf45DFuK5RsKX198vZTlqtH24YhAL1+noQTMtpTOp/6vrczOXkr7dJGQ6RAfliq1maD18PN5yjdyNhr7BXsrK6f01DX2Xr4s51AARxQ/0U5hmb4HjKg8Sbno4Th+Wza3I0RMgM0YzhRUJz+BXzx2l9NPdeyuECdQ1Q2wP1MNOCBy/QBJmc+RWYK57oWCuv5vmkpN8IlriyRv0PGRhYr4ZcLkDdzmuyfK6SGAvIPD4veDJRj3cXEazyMh6g+rvwt70laxo2IOhvUXDc89WvarthTOzlFt5FNrA8uXhUYyL1q1XSWYCiCu5vRv77BvRUJjf2B65kaIKUAGDhYuhch2yU6O9VsHLik3xOGSwIUZJbdMyHY+eA8ZlWZeKJbpjz7a/TyBQU6VNG4+Z5SXJjURogkODNkx21QS0Z1+b7ZnCSXf1OQceomkDrREB7vyD2HX5rN2/KIBMgH3J7DnG2VpNhYJ9Ve1hMDGcrQggjkCpdP7lloc6QiH837tD/81gYmr95IbuIHe/x20oHh9heGHUELnCQ6hXYWOBvSFlGcqZs/f0qxn5Fe2OfQPRzEstxoW99IdPvgotDnL2vaz2JNFvFqiofc2pIP7XvpFKIoQO7q8LX9z7ah1Yh8cbi6us8g5y9WzOfh882jU7vQT+31a1ZaeDMbFV1Cemc+/d0HNksi1qMJtcjrH2MQgXJ3BTyAuJH9OFK8iGqSzHhop9hp5z8mvx834PPjgBfZGt4w/7qeie+T4sooGVVqA6F3jl8YfFdIUAwkxe5GBQVVvaRLYm/4SLGBf54Dexi7e0+rL2sG5DeKygNdFzMc6lRO+gvmAMmDucRm4bxmu7ycNZCQUcuSKoMUWWu6A6eUiyBCQUxrrlX/3CkRXkQQ6JCwZZMvTgBokYx3WQR6LpW70xWLXyQhav4ZnHKzgITSOe7mUkMJ35NDMD+qsxXY7sWbGz+b60DWF7yaMVzDPzIGjWLpckMRMxgN3bQ5SE/mFxdjoZD5yYb84q/O7EjwGA9MSTp9MFEZt7VV3f5TDWiNUZKmjUgOdjBoTSAkVzAO2nqqQNg23x6Z6FQDBeefRkfc9FoUiBqHuN4fU/zc4Hkthp1McwIkYwRdlgPceD/BSNbkNRNAnzghBhCquIqpXd8AptBX2qO67rfT7COhpn/fzVo3ueCRTaM2DtjD3uuH4rMNb3LDjyJFX1DZ5eEWkGq9UE/AFivfeia4cA0a8Z1LzZcW7WvE5Y1WIZN4gy9SZcNgHEQq8Ad8Q4fAbxe8XJ6/tNvG+AvAuLEvNJtbhC/4Ei/JoXplvutDlW5d6g4KEWj4GICqggM5ZSv0TCkbfFkLdaJrOHrn+oI++krv1U4yQk6P18Mg2bE18ibe+LdWNsqn01V7yDmS+VAvqQF8f2p4rOOyWsGc7CoyXSrq9LCuGq9eMPR6auo+tyS1Nek2t6SgpOpzBBDdQnC5sHC1OWTW3ui7w4H0NKCuZOiMncbSDOlegn8C0zZa6Z5iYAce8a8Ow3jryBEnKBaguhjjOMG8iX/eka8XP+UTxvso4fKVVOXQwobZMdYbf/sXNJbMbWrFc1S9rdlXL/nnYvYrRMnOBJ27Mz5vvtOpd4fyQ+wi1q+5VvuLDM8u51B4oaYqpGUZZ3qVS5BBYm9cDxgMtcdoXjOSopHasdAhron+NdbGFBxyrUGKnnVXYocEuvsvwhBEA3HUUVV94m3C0agh2eVpmCIyWrs+grkpAaNLZwXVuzegttJ0GoTxzQnDIWkvlvkS3ZGo25spfPp+/Nda4SZAYRNmtnGfB2TRl0Wx/o/V2vx+9qnGyDq52CSkMftpfnsMXAnAv6ps7U+mgbgNPUFjv1Y0xKaeJdshu1HyEmq5aYqHJSfF2EzvPfH4d0Ijz1lsxMxL4IsqB7kufcOR4FFnaYXKIXLjRwM5VZNAK/3dvCb3l9H7QMOiJPbdoxAd123aymjz9N/2O33wMaG6OE8pXp0iYEaW7DOr0FfT913JeUnPNPcqqsA9YXod2UuNWZElTW/saL32v9akNwA4Jd7Y5VgI4y+XyDH3kAU0Uc8g6YCx/hqcn4pd2+ryH+/5nVQhnCE0KNOjjrFS92RNLD71GUhWR+VXMw2tKXBUSKnt9Ai4LLJrdvFbwrdqK+AjBUVqI3MgylNxRw2395ppAbheE1pAcoqLoDOGyOs66Y8kJGpaqs0AmdmZHw2OA26btw+ceBN+UgScsB5P5wNIup1AvU5J7h1vlFBNygg3WO/MJGCz48xgJ/klg9wCLQ+vXtrhYJz15RgguADFLBrTcV/Miel20KulnprI+/lXtRvEAoGJSc0UZ8J7UVTf8kvYzT3hF7XzZzlhKxPYebjdnp2la4o2PkyZXcc/gFLa7ickR28ZPUigwpW0lK5sJIwWbnZmP5wbQNhiGO8QL9gVpFOnu0xHpu8MqBvfZGf2HiE+qBUSR89v88gz6u/TVP9zVH1dnk9PE54Uw3yPdxL/feukvF71sEI6WWd2fdupgRlDGzASrKSAsFbaZobwUViEIFbWo7zPYVZyMglCrD1Xoxdd6EBeUSJDkS1nhiHOR/7FpIhae8fggAD+StXR7725vzcwIOX21ozRcE2iWw6OP99vDoqLQ8VYzYS0/f3WMME6b5ndYz25uC0AiULXYI=",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2IndexedRangeEncryptedValueV2.json000066400000000000000000000024061521103432300305730ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "D6vN76sSNJh2EjQSNFZ4kBIQCKONPDzoIPJR6V3HiKEKizlmRU6wZ6BdgJWukp2SMCmnw1/ELG4J47sD0GavvaCbFwIxVipDqB0uGsY8VQZCKq/cWAE8YX90+1POSLR+ABjGqfWO8M7x78VrDWiHmSc/0b7olL3nZD8ozT01iUqxI3dOQVvJLVW72IhfZbFkvJRvGH0bBaIuwfyk6wit1RIUAzWFDWtGMlo4lZ08kpzhe0KUjyG7UKLg6yxf7DfdBV3bmiuFt+Xj8l1jx2kcYSyITXrB1Qqgkkumu6672WRLabi2SloAxuCfXmnvRuqLF5DEQ7fvAeE8tl8aSJ2pKKRVAbP3bwRsoJm91ErM7WVcJcTrTCR/lnAW8MmNjJCjSB56cgi91Q1s/XfeCgvNsiAexzTBKdNJSqw7RtSlsCwXhYSYSpbA9ohG5Z4Hx9eK5Eyi2+HEgqjq/8RPaVcqljVRuUxnIMxkBwu64H1UsoSxnQRGCs6sjO3UqhHupmckY2hOaod0gHsqRDUxOj6gYLzqx/egyCTMI+FKG8hN19MnKE2H3yv/PahY3uFM9FOnn9RXcKYtqixaj95WS4/nyAaVTDdrsJ8sWjZeq0EziVk31wYCAsZwgYp9Pu6Ur1JAhNwRkSs4O2CSbePsY8VZkWEG54ONaP9JZG3yidA3McUoyMvaNWlYTqWZc8nZMYjSqgfxgjrM0S8tEyyuwK4EEV6ez8pOxE+0JTgtyZdhrSztcljVITX6p3I/nuD6yWNpdRYdkK6QmVlMAm+OX7T6igIpu9dzYxJpeBD3b+ioXAXDSy6cYOamVyO+d+RgWSXT+ficAY2eUicHrsXZBgfReZiTXVqpQGNAi5HnI6f+UjeKqK7YsAw7TVLDFvNzgsFKH/OgoUIH3m2z6JFD8OcqF1ufFXsH5H54JLAzIIQZOBVztxuMqEkteFUxUIWOLw3uw3K8mZCv43QZYFvZcPTrN37qY6fTVxl375vFpJXCCpd4R8Dg4Q+JGrTk3UKcVoA4WkoQq9UTNMnOhesLhOt4M1igJwWjlZNM4E2iduXoP+gJ9l9tckybx0yjxJ2qCJyzz8sFv5m2tSHDELv6PXGxphhQnRYznnDsgJeAGh6KXqtuLJP4sUf8VjxFXaGD0TYx3WvRvmnQLCWfKnG+fr0slKl83Q==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2InsertUpdatePayload-RangeV1.json000066400000000000000000000154451521103432300303440ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BAwUAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVjACAAAAAAcbn1OIyvGiS6W/5leV27c3oDoTgAztFUyVRUBlIxC+UFcABQAAAAAJ3yl9lkq96PEts2moPIDCJnQjXxK6fAZMVxZW9WjZD3MMg7ki5u4CF85T/fdloyha74DDPjn1Os5ser2/D9hdpMRQWVtHJA8ZmjgousnQzvBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAEAAAAAV2AEQAAAAAq83vqxI0mHYSNBI0VniQEtG3g+8K9ns3yzddqS3RKsr6MaNc+IWACwxRIwkr+2l4I9DNIjltuvtmbOTj/WvyjOwhHuYFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBGcAoRIAAAMwANUAAAAFZAAgAAAAAEXyPll8pDgONFsvw5VnzyCe0TNwEePYnQZeRFNW0fTqBXMAIAAAAADNAYCK38aHlQp6Vb784Eip1hnuvLCHL+EKg/cqOkSFfQVjACAAAAAAoXykEqXZ1AkJfX3MDyNouG1Q6TgmlhnFAMABVsUhKvIFcABQAAAAAPTG+RBbMcNo1xxYOPFqmone18YO/x8SWFkyTUiX0Xgfqd1Qo5oRFyX8rJrHzGewFfUQTJBI84TJcpeN96KDdmwNHBN+wbnHqhax1JdFIBSVAAMxANUAAAAFZAAgAAAAAFP67MLPvLGnu43f7PbTa/FDk05X3zkDzTMCrHzrfXuIBXMAIAAAAACi0k9AqpAY4ZSQxZpZb0aVuoakd3Hxj2ZJfu0GFTPxgwVjACAAAAAA4j6rpV8TuAI93NTrRBJ8anTCzhf1nCjZ8lY/CYhxlU8FcABQAAAAAOD6SY9UmJPLh8W9rY+WOcGROFQBbv74nKxs/RZo5vz4keuC1bxi8oW30mXlZZOefKn3DcCnGc+wcZu+HhM2oR3xmeNmUNoZ//zXGg6a+qgkAAMyANUAAAAFZAAgAAAAAICoxcn9LaNlkM9Ak4YTDkg9xmcW9c1W6sKiuMw/Xb85BXMAIAAAAAAbywcEbo+80MFUNGgd7BCenXYJvF+wwXnSj8adVq4UjQVjACAAAAAAsnVl8o25cgDc2D3GXaZcalRLg5U5LT93XUWZhiswx3sFcABQAAAAAIf4laNtTAxemuN2gVDnb6F2M7LeyzBwN2bTF8FJ8Uke3sFFCEdBnC+63U9YJwNBMTrn+HRbWipUubgeHLzhzPlQ/dVppK8aQIFMBz/GMUP/AAMzANUAAAAFZAAgAAAAABa91Qu2KZFQjidDvZdxtbiJgcgk5T81y3pdlHtXY0xcBXMAIAAAAAB+3EKOsVfcR0d87sZwagcu2biF+eZ6Fb7+fhIR2BSaCwVjACAAAAAAri5RnYYbOuGV02gJVninpStPMfrKWo9qQRSKQ3UUhcEFcABQAAAAAMfekKz3qTMgEkc9mWWzoSt2BiSVFe1rku7ROF416pJfXeIDTSHhD5FuGMZ92BxlZl6LYlPhqY6VjrVAM+hMhIN3eC/JQGff+f8TwMrsKm0LAAM0ANUAAAAFZAAgAAAAAFL4aHYc/8mpwuAcBxcPcShnLqV74mFex6P0zv2G6qZNBXMAIAAAAAALtlteiH0O+hP/CQV59KCyPE4AFlbbdXCrQYkyUu1bowVjACAAAAAAgE8p4vJm3X2KseHhM9eRGBMaBIYc0AMZ9GBLIB2KAYsFcABQAAAAAH5K57wo1v6F3PVPdumG6cODI8r3jWY8FWxZk1RAw7BaF36boaMrsgOmklWTacBjckwz267Qdhnp5QhZ+15cTII7SmUBMiutESYer3z8TF76AAM1ANUAAAAFZAAgAAAAAB2icZVZfz2wRrrQ/2xAZU+fVk//9fCDMQl0UvqwXuMSBXMAIAAAAAC8M1zqSVhPqWqlH4Tlctn7yzv641vsNoxeX4BItnKbNwVjACAAAAAAdA8ua5LVZB7r+d10pV4q0GXPSgmNbaE5Y3mWwMihr+oFcABQAAAAAL8fSDOmnKg/FmESSGy/m8JUZUQ6fsqXhGxcXxTBIm2HhCmKRI52VotIZnZ77cO3/Lxl21dnHUg3743UDK1PwXMB50vak4o6k4JyrQuM99lMAAM2ANUAAAAFZAAgAAAAAJiezAizo5OYttRYE9ywvbfKDaGxX6hP/4B1CvWoKxX4BXMAIAAAAACYJbdQKs0dHVK3A5308Q21fYD6htM3arFOPa0OFWHFpQVjACAAAAAAI1NMx/WKK5VL1xY824jIklsksHgonlzIWD7CN3w/MIwFcABQAAAAAOrrGqfH+Bu8pTCDouxkQ9rqjQB7Nz59n+e7BBTgxcr+1dksN/LhAitmq/eYLbmz1P96RtWdk+cBlDQLFnqsmn41b/j2ABs+4X7bb20S3yJKAAM3ANUAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVjACAAAAAAQICwcJmriXc4AQIaNS40F0lLZqx3wvJLRt20PXr3f9cFcABQAAAAAIarDydCpSFo/NqlzaMC6E+CKXLeM6GK/crAB7B0DuDw/BC/Pr8JivuFuw8KQ5KPVmuD9lR+UlgsEhmex8SHcMBr0FSgxgDDEVnhrJ9RLqSJAAM4ANUAAAAFZAAgAAAAAOGTIi2oH64nUUX5oSm85A8OU+VdEuZMoanwb2Z+XT5WBXMAIAAAAADaPRu2UYTb7bXYGOvr4OPV09U11n+VBOgwBZV/wD/NkQVjACAAAAAAWBmWpKZl7Wk4CxJSLX0Vl4q8H/6qK+tYm5EnFph+VX8FcABQAAAAAFxMgKSCgWu4v44quSEjsAGeZ8/5hjczCAEe4jxW0ZuGcr505o6tB/kEcV/uaUTV/rGxYoml0QSHUoUv+Z4QS+movoxqvVZBLHk307ZbwlCcAAM5ANUAAAAFZAAgAAAAAKFKEINemcMfzCAv6lVctfqI87n9eRwDJUg1LA8kdYwQBXMAIAAAAABo00sYyBR3ZxuRjiXM98saXonlBzYiXgf3RANye7jRZQVjACAAAAAA0AArQZukaIcCh0oF55wyCDF5+R6PO/lR2FZ2Y4BFJakFcABQAAAAAGZ8Ofypt2EEsmGPX+tv0vK1RsoklEPqysHOCzVkC5eTaFo2BGF1jW1foVXyEgM6w5tHtgcK+k9FdFOpYzzb3lfkQBkWfb5NgpXUyTo/HBYmAAMxMADVAAAABWQAIAAAAADvt5mVfNvJXYC4cEi/R+z0r5iZb+1q7wchwAABkqH+CwVzACAAAAAA/xC56Vn95f0jaFDT9Et9/JCiKeRNVft2ffRP9xT14dUFYwAgAAAAADfEAX4qKIueyJeHerFXH/nUphIbk49SXdWN+DbpnX5WBXAAUAAAAADLBVQCW9s1i387ZRgAamYr1rqy0ouCiYhfGSW1/U4OyUTxWDUTsZ88WcdNX69D7m7kxgVlgO4cwtFtWd9uoTburkrel2hHG6+ale0n2nq/9QADMTEA1QAAAAVkACAAAAAAE+06hBFHl4f7CHQFGGj2ZZuT3WZEMqTj4CzUM2e9PeoFcwAgAAAAAIIlGRvBtZCaXT4OWFMXF+3VC8jzuJFe3mR5RpM/T47OBWMAIAAAAAAlxGwgJ4opwcXsuQz8Zg0DT+BsaCqmHZPmLl3oA49W6wVwAFAAAAAA+IyMVEEZ+TPjN2hAfMvIEga0+pIOL/lFmJe8P6j9H/LLhA15C80/qcQQsYfVz7l5VUuPZ9G+22Vx3TJtK2dnt9DAqc2w1LYeUQnnNqBcCQIAAzEyANUAAAAFZAAgAAAAAK8IiTdn87RqrKys7Cb7BKa82lkWwwkFcGFIgEEuJPnYBXMAIAAAAAD11EA7J1kxw2jXI2H1c/slLVwDgQQEP3nA3e3o1nyk1QVjACAAAAAAKYlbcijR4iBbLQs+Q39c8KXcm2V2QII4h7bWmxBS9RkFcABQAAAAAGP4YMNi53E9IIpG+l3YkPhVzzLtjgFEJsCuRMXornEHZl5FyhbW8UOv2eS5uxnIg/CboBkKwZhMnr+26SRCfiBrSZT0XVnDwEShWaqt1F1LAAMxMwDVAAAABWQAIAAAAABiid+a+1N9DG1AUCL3XIHEFuzBvCm2JXTeDwna0EUXEQVzACAAAAAAsMJjIl8B7w+wRM4QYHw4+C9AGo8kof0Ibc+NouaIJGwFYwAgAAAAAOxJdVZQA/NJuL94O2pqI5bYXJIft+RUXjlbWz0dGhkEBXAAUAAAAADpphQq0q3J5VDQffMiAdBMlnlo6r+Q7wuuPrB0BAIESsdbqaZHnettNV3sLKGuFu2L8ELHIWgD1vjvxjg+mN3b8yhwycpMarUBDM9JtDEnjQADMTQA1QAAAAVkACAAAAAAYLNccfL4s3DMHDChNpne5is7B1RfWUpctFbO/r2JTAMFcwAgAAAAAPOdU0Owvp1pFujAWWt7nKFVJxcXTMoVw3ZZiheO1DB2BWMAIAAAAAB0DabCvUfAGj6K6Ry6WaWsVlaZeslmA9OWTl5kgvchnwVwAFAAAAAA2gD10Jcq7LP5rIFzwo02HAvdL6JJB5p5TAFUw3Nt0vczPj/BByYBLpAiL8S+kgzWlio8tgt8SQ6CL0TmJfIVUFty7E5zUh7Z1ZIJPtjPjYYAAzE1ANUAAAAFZAAgAAAAAEumusJlA1iuUJrW2KS2QWaAtWKoSfYITPYZh8KjMQzEBXMAIAAAAAAj2X8l6KLHNOvaadrafChrFonWgd76J/QAmu2+JqnLeAVjACAAAAAAiSplqMUi/3btwQp4dNvnfvwEVfOMefpEgxZiJLcwf2oFcABQAAAAABSzLqHq6xOFAbc/3u0wFKh+vxpCRNiQEbT70ZdOsXZQRO58mBkdZs4aE0l6p4rCT/XDkHFnKxSABaJSwYrX7+ok7rXLCPcdts0jHoGWt2ubAAMxNgDVAAAABWQAIAAAAADUq7UIfHJv/mGbsXxGaSnrk1w2OaF1XIQOdL3Ibv7jAQVzACAAAAAAVYSgjDnwcfQ937v4LtelZhXljZRVaPPKpZ72Eg6Qp5wFYwAgAAAAAIULgFieXwksSKKSQ9E38lNj5TNqwuc9iMiH8EWn3AcuBXAAUAAAAACcTE3N5TGx8bkORR/Ia0y4uhYovi207Txb3LlHV3oiy9569XaF6DqC2N/uE2c104pmICu3tm22Q//PSxZK8jpnNmwQX9xpkhpKkyTbg+sIRwADMTcA1QAAAAVkACAAAAAACM+sDq0KgV1gDpnq20HJEcuHyyOQAvZOQ4QSd5ByWCYFcwAgAAAAAOHNpbofTDusUjan32aB+EsCNfYt333SfYsgGhoraGcZBWMAIAAAAAC9mttUKF7kto9nk7cCYVJSrNb0KORWWG+WEcHGb0bUlAVwAFAAAAAAeSVEkfNb3GOskZMsvHSnXfej9PFNop6OW3cd5v6oxUNXi6vOZnpMoU3iaB6yxyFZoD8tqflWMilQ0Wz7xG5qsfraV6RQvEZtZvuTYzaKmAIAAzE4ANUAAAAFZAAgAAAAAHLoTZKJZy5YVQVkXu7X72rfoGJ13twHQ8zDE3ipSb+JBXMAIAAAAAB9ddE/wdJ72pMLgyGnCb+ca3LSzAy92XcvLtnCBDmOMgVjACAAAAAAYHLbyOeKwYLum4Mh2GlrGBvuHsDCY1p/NwT6hiT5o+oFcABQAAAAAMtGR3nVaR9mUvg5STOnphZFMPWv1NTKcHR5/YpliLLP3aXCpkoZQbR2lnbI4AZGQW6VbBZcYN+7t7GOUoBElFxEpUdNkW7Ih56YoEg7PQkFAAMxOQDVAAAABWQAIAAAAADdcutpKLPVhdR6vTKi+/9b/ngd8WF/gvYzoSOKPGwhLgVzACAAAAAAWm36BOmL6gbNykGY7pHDbJZZEHDxt+WMyNRCYL6dKX0FYwAgAAAAAP4bzR6xzN8nVkz4TD0L15Q9stgAYpjaAWnWe4GUfaFeBXAAUAAAAADeGXtHUgcY58PiPYhPz3cNhByYMp97SrjKHNll6rE9SAazGhLNMEVsBYl9TWuUUDqM2GFynVJsPOTMVuqRZS+0hLs1/0ZKUZN7mymtAFZP7AADMjAA1QAAAAVkACAAAAAATL5kiorlfbTce6SioSjxAW+EZV5abS6FAul886sGTP8FcwAgAAAAAEDafNOssnpZAmH95ppH5ugmYGnoCCaJNAURt0+MIoUlBWMAIAAAAAB0MUvEoSdgGXnBu1+mfTcOiQ3A1eE4FH1nj0Tylm2stQVwAFAAAAAAVtWeCh+hrH6DmWWvm3YxpANb9hhaQv7wDesCAKRCR34sjLV7x2iW2cmANDak09+M1LdPmpYsReD5WbZBi61RgpW2v28BVU3pTiSVwSpwoUQAAzIxANUAAAAFZAAgAAAAAJYJgvUdGrv/W84FtEVqOOqk3F/d9W1g2swofQJYzGWpBXMAIAAAAACCsGtwUCaNOu68ZJF9O5Oe4Xb65fP9W2veDrRGqQbhtgVjACAAAAAA9F+4LBoDXc8tAQCe20b7X5/IRnA/vzN4XDsEbk2tfFMFcABQAAAAABmjwCYvmGFcjINULL1HMaOOH+OUNQzJbXlwSymyfII5FgbNkdj94Sm4Mk2K+At1OgZPgS2dNYiPYegxAFyE3PGlCDuDPOJ3i3Gf5ZXmyK0uAAAA",
            "subType": "6"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2InsertUpdatePayload.json000066400000000000000000000011321521103432300270670ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BHEBAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVjACAAAAAAsZXWOWA+UiCBbrJNB6bHflB/cn7pWSvwWN2jw4FPeIUFcABQAAAAAMdD1nV2nqeI1eXEQNskDflCy8I7/HvvqDKJ6XxjhrPQWdLqjz+8GosGUsB7A8ee/uG9/guENuL25XD+Fxxkv1LLXtavHOlLF7iW0u9yabqqBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AE0AAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSFtVvkUcnHWj/rfPW7iJ0G3UJ8zpuBmUM/VjOMJCY4+eDqdTiPIwX+/vNXegc8FZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2-RangeV1.json000066400000000000000000000135621521103432300305520ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C0MRAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVwADAAAAAAnfKX2WSr3o8S2zaag8gMImdCNfErp8BkxXFlb1aNkPcwyDuSLm7gIXzlP992WjKFBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAEAAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEtG3g+8K9ns3yzddqS3RKsq6kgPgWw9BNVlehfV8GQOeTSKt8AtDMc632S93dm6vPNlcIk99BOVkiWr4waIbeDEFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAACU7foxpUtVEMVbrGsj7RROTJB0V9IIQE+RmDywXO5MlhJrAAAAAAAAAAAABGcA4Q8AAAMwALUAAAAFZAAgAAAAAEXyPll8pDgONFsvw5VnzyCe0TNwEePYnQZeRFNW0fTqBXMAIAAAAADNAYCK38aHlQp6Vb784Eip1hnuvLCHL+EKg/cqOkSFfQVsACAAAAAA6QK+s92A82PEceXdQwfnu4Ru+VN+cHdIixK/EuNIQDcFcAAwAAAAAPTG+RBbMcNo1xxYOPFqmone18YO/x8SWFkyTUiX0Xgfqd1Qo5oRFyX8rJrHzGewFQADMQC1AAAABWQAIAAAAABT+uzCz7yxp7uN3+z202vxQ5NOV985A80zAqx86317iAVzACAAAAAAotJPQKqQGOGUkMWaWW9GlbqGpHdx8Y9mSX7tBhUz8YMFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMAAAAADg+kmPVJiTy4fFva2PljnBkThUAW7++JysbP0WaOb8+JHrgtW8YvKFt9Jl5WWTnnwAAzIAtQAAAAVkACAAAAAAgKjFyf0to2WQz0CThhMOSD3GZxb1zVbqwqK4zD9dvzkFcwAgAAAAABvLBwRuj7zQwVQ0aB3sEJ6ddgm8X7DBedKPxp1WrhSNBWwAIAAAAADXLtOFHx7zuX+RYrMYlOG0itP6L7gjr3SD28l56+GTdwVwADAAAAAAh/iVo21MDF6a43aBUOdvoXYzst7LMHA3ZtMXwUnxSR7ewUUIR0GcL7rdT1gnA0ExAAMzALUAAAAFZAAgAAAAABa91Qu2KZFQjidDvZdxtbiJgcgk5T81y3pdlHtXY0xcBXMAIAAAAAB+3EKOsVfcR0d87sZwagcu2biF+eZ6Fb7+fhIR2BSaCwVsACAAAAAARwealJh30VkdUvqpNoPSBdU6HuZqhWfrsGKkxSrZYX0FcAAwAAAAAMfekKz3qTMgEkc9mWWzoSt2BiSVFe1rku7ROF416pJfXeIDTSHhD5FuGMZ92BxlZgADNAC1AAAABWQAIAAAAABS+Gh2HP/JqcLgHAcXD3EoZy6le+JhXsej9M79huqmTQVzACAAAAAAC7ZbXoh9DvoT/wkFefSgsjxOABZW23Vwq0GJMlLtW6MFbAAgAAAAAP41Kdnpj6XQ3/D6IO7/EpHRStFFeMXKm9ABRnMeW8xxBXAAMAAAAAB+Sue8KNb+hdz1T3bphunDgyPK941mPBVsWZNUQMOwWhd+m6GjK7IDppJVk2nAY3IAAzUAtQAAAAVkACAAAAAAHaJxlVl/PbBGutD/bEBlT59WT//18IMxCXRS+rBe4xIFcwAgAAAAALwzXOpJWE+paqUfhOVy2fvLO/rjW+w2jF5fgEi2cps3BWwAIAAAAAAdWR7td8s9KkhnyXNcgSIaygRclVLxCBAw9QADJqiFSgVwADAAAAAAvx9IM6acqD8WYRJIbL+bwlRlRDp+ypeEbFxfFMEibYeEKYpEjnZWi0hmdnvtw7f8AAM2ALUAAAAFZAAgAAAAAJiezAizo5OYttRYE9ywvbfKDaGxX6hP/4B1CvWoKxX4BXMAIAAAAACYJbdQKs0dHVK3A5308Q21fYD6htM3arFOPa0OFWHFpQVsACAAAAAAIHYXP8RLcCboUmHN3+OlnEw1DxaLSnbTB9PdF228fFAFcAAwAAAAAOrrGqfH+Bu8pTCDouxkQ9rqjQB7Nz59n+e7BBTgxcr+1dksN/LhAitmq/eYLbmz1AADNwC1AAAABWQAIAAAAAAurr5Hc1qtwzXHy6p3Qqo5g9Y2pxS3P68Z8JrDk5XEFAVzACAAAAAArOwPN+XapA+LMq57JwNq7o1lufkPXv9RC6JT4IkFRKAFbAAgAAAAAJe0bDhUH1sZldnDGWn0xMa1CQuN6cgv/i/6XqnpPS39BXAAMAAAAACGqw8nQqUhaPzapc2jAuhPgily3jOhiv3KwAewdA7g8PwQvz6/CYr7hbsPCkOSj1YAAzgAtQAAAAVkACAAAAAA4ZMiLagfridRRfmhKbzkDw5T5V0S5kyhqfBvZn5dPlYFcwAgAAAAANo9G7ZRhNvttdgY6+vg49XT1TXWf5UE6DAFlX/AP82RBWwAIAAAAACHR4WfBFBn/Zv0d5gD1QJv1LAe7z5dfOKsdcEJaup2rgVwADAAAAAAXEyApIKBa7i/jiq5ISOwAZ5nz/mGNzMIAR7iPFbRm4ZyvnTmjq0H+QRxX+5pRNX+AAM5ALUAAAAFZAAgAAAAAKFKEINemcMfzCAv6lVctfqI87n9eRwDJUg1LA8kdYwQBXMAIAAAAABo00sYyBR3ZxuRjiXM98saXonlBzYiXgf3RANye7jRZQVsACAAAAAAOc0fTpxuPV6594K1O8wQ6J/qI/v9RVCDqbS78HrFlL0FcAAwAAAAAGZ8Ofypt2EEsmGPX+tv0vK1RsoklEPqysHOCzVkC5eTaFo2BGF1jW1foVXyEgM6wwADMTAAtQAAAAVkACAAAAAA77eZlXzbyV2AuHBIv0fs9K+YmW/tau8HIcAAAZKh/gsFcwAgAAAAAP8QuelZ/eX9I2hQ0/RLffyQoinkTVX7dn30T/cU9eHVBWwAIAAAAAAQL6NCvBkhQ9ulCrwaNULOgv7Uhfp3ZhsiTHSOF5GySgVwADAAAAAAywVUAlvbNYt/O2UYAGpmK9a6stKLgomIXxkltf1ODslE8Vg1E7GfPFnHTV+vQ+5uAAMxMQC1AAAABWQAIAAAAAAT7TqEEUeXh/sIdAUYaPZlm5PdZkQypOPgLNQzZ7096gVzACAAAAAAgiUZG8G1kJpdPg5YUxcX7dULyPO4kV7eZHlGkz9Pjs4FbAAgAAAAAIFEk9LC+wpdnt+iy3OTuzvc/W/4y7GWyhLN8YIFWpBZBXAAMAAAAAD4jIxUQRn5M+M3aEB8y8gSBrT6kg4v+UWYl7w/qP0f8suEDXkLzT+pxBCxh9XPuXkAAzEyALUAAAAFZAAgAAAAAK8IiTdn87RqrKys7Cb7BKa82lkWwwkFcGFIgEEuJPnYBXMAIAAAAAD11EA7J1kxw2jXI2H1c/slLVwDgQQEP3nA3e3o1nyk1QVsACAAAAAA5X1X8OUn8xyl999HEkY+2A3kiBF5kxbmGM88ILnB+HwFcAAwAAAAAGP4YMNi53E9IIpG+l3YkPhVzzLtjgFEJsCuRMXornEHZl5FyhbW8UOv2eS5uxnIgwADMTMAtQAAAAVkACAAAAAAYonfmvtTfQxtQFAi91yBxBbswbwptiV03g8J2tBFFxEFcwAgAAAAALDCYyJfAe8PsETOEGB8OPgvQBqPJKH9CG3PjaLmiCRsBWwAIAAAAAAFybb61uzRVIYWf2sO9bK+G4hFUN6lF2gi81rpNG/fOQVwADAAAAAA6aYUKtKtyeVQ0H3zIgHQTJZ5aOq/kO8Lrj6wdAQCBErHW6mmR53rbTVd7CyhrhbtAAMxNAC1AAAABWQAIAAAAABgs1xx8vizcMwcMKE2md7mKzsHVF9ZSly0Vs7+vYlMAwVzACAAAAAA851TQ7C+nWkW6MBZa3ucoVUnFxdMyhXDdlmKF47UMHYFbAAgAAAAAHVsIUd93Jho+eG8Sc6Z81BkhBFWqHENLCPv0io1d2WaBXAAMAAAAADaAPXQlyrss/msgXPCjTYcC90vokkHmnlMAVTDc23S9zM+P8EHJgEukCIvxL6SDNYAAzE1ALUAAAAFZAAgAAAAAEumusJlA1iuUJrW2KS2QWaAtWKoSfYITPYZh8KjMQzEBXMAIAAAAAAj2X8l6KLHNOvaadrafChrFonWgd76J/QAmu2+JqnLeAVsACAAAAAAiwhaLuXgveHtqUVzYyfKD5BDUQgxncPXmT+yr4G9pggFcAAwAAAAABSzLqHq6xOFAbc/3u0wFKh+vxpCRNiQEbT70ZdOsXZQRO58mBkdZs4aE0l6p4rCTwADMTYAtQAAAAVkACAAAAAA1Ku1CHxyb/5hm7F8Rmkp65NcNjmhdVyEDnS9yG7+4wEFcwAgAAAAAFWEoIw58HH0Pd+7+C7XpWYV5Y2UVWjzyqWe9hIOkKecBWwAIAAAAACZ6gTTH4KLa9AbPN1bpzpJUgwFZ6DvBQALNpo1C36D9gVwADAAAAAAnExNzeUxsfG5DkUfyGtMuLoWKL4ttO08W9y5R1d6IsveevV2heg6gtjf7hNnNdOKAAMxNwC1AAAABWQAIAAAAAAIz6wOrQqBXWAOmerbQckRy4fLI5AC9k5DhBJ3kHJYJgVzACAAAAAA4c2luh9MO6xSNqffZoH4SwI19i3ffdJ9iyAaGitoZxkFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMAAAAAB5JUSR81vcY6yRkyy8dKdd96P08U2ino5bdx3m/qjFQ1eLq85mekyhTeJoHrLHIVkAAzE4ALUAAAAFZAAgAAAAAHLoTZKJZy5YVQVkXu7X72rfoGJ13twHQ8zDE3ipSb+JBXMAIAAAAAB9ddE/wdJ72pMLgyGnCb+ca3LSzAy92XcvLtnCBDmOMgVsACAAAAAAM+4NqkHv2gGmxQoteH2h4RJfrbbvEEwhvckdaalYJy8FcAAwAAAAAMtGR3nVaR9mUvg5STOnphZFMPWv1NTKcHR5/YpliLLP3aXCpkoZQbR2lnbI4AZGQQADMTkAtQAAAAVkACAAAAAA3XLraSiz1YXUer0yovv/W/54HfFhf4L2M6EjijxsIS4FcwAgAAAAAFpt+gTpi+oGzcpBmO6Rw2yWWRBw8bfljMjUQmC+nSl9BWwAIAAAAADHdvfQaJIOoYoaJuxWQLTA8S0t1SfmiJ4L+ZHSHQ9+igVwADAAAAAA3hl7R1IHGOfD4j2IT893DYQcmDKfe0q4yhzZZeqxPUgGsxoSzTBFbAWJfU1rlFA6AAMyMAC1AAAABWQAIAAAAABMvmSKiuV9tNx7pKKhKPEBb4RlXlptLoUC6XzzqwZM/wVzACAAAAAAQNp806yyelkCYf3mmkfm6CZgaegIJok0BRG3T4wihSUFbAAgAAAAAPsI3ldHLkKkInpttNugj3cAyjtn8+aYWSppd6eF4IP6BXAAMAAAAABW1Z4KH6GsfoOZZa+bdjGkA1v2GFpC/vAN6wIApEJHfiyMtXvHaJbZyYA0NqTT34wAAzIxALUAAAAFZAAgAAAAAJYJgvUdGrv/W84FtEVqOOqk3F/d9W1g2swofQJYzGWpBXMAIAAAAACCsGtwUCaNOu68ZJF9O5Oe4Xb65fP9W2veDrRGqQbhtgVsACAAAAAAhAQfZrFPepWTck9jq8vCgTXH8B1GXsOJ7gDBc6VNJE8FcAAwAAAAABmjwCYvmGFcjINULL1HMaOOH+OUNQzJbXlwSymyfII5FgbNkdj94Sm4Mk2K+At1OgAAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2-RangeV2.json000066400000000000000000000107561521103432300305550ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "CyINAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVwADEAAAAAnfKX2WSr3o8S2zaag8gMImdCNfErp8BkxXFlb1aNkPcwyDuSLm7gIXzlP992WjKF3wV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBLRt4PvCvZ7N8s3Xakt0SrKupID4FsPQTVZXoX1fBkDnk0irfALQzHOt9kvd3ZurzzZXCJPfQTlZIlq+MGiG3gxBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAAAAAAAAAAAARnAJsLAAADMAC2AAAABWQAIAAAAABT+uzCz7yxp7uN3+z202vxQ5NOV985A80zAqx86317iAVzACAAAAAAotJPQKqQGOGUkMWaWW9GlbqGpHdx8Y9mSX7tBhUz8YMFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAAD0xvkQWzHDaNccWDjxapqJsQQJxIpJjSzH2N1sMl52I8VCGmhbZ7eiv1GA6+MQxOtVAAMxALYAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAOD6SY9UmJPLh8W9rY+WOcGfBhR2IbREcrPOlvcWitCDpgifW8LNgrL1DtsD+aUrX0sAAzIAtgAAAAVkACAAAAAA4ZMiLagfridRRfmhKbzkDw5T5V0S5kyhqfBvZn5dPlYFcwAgAAAAANo9G7ZRhNvttdgY6+vg49XT1TXWf5UE6DAFlX/AP82RBWwAIAAAAACHR4WfBFBn/Zv0d5gD1QJv1LAe7z5dfOKsdcEJaup2rgVwADEAAAAAh/iVo21MDF6a43aBUOdvobfFrmz0OxcKEl87Qr/9ulWQYnliZ2RZvlhXHLqxkpgtiAADMwC2AAAABWQAIAAAAAChShCDXpnDH8wgL+pVXLX6iPO5/XkcAyVINSwPJHWMEAVzACAAAAAAaNNLGMgUd2cbkY4lzPfLGl6J5Qc2Il4H90QDcnu40WUFbAAgAAAAADnNH06cbj1eufeCtTvMEOif6iP7/UVQg6m0u/B6xZS9BXAAMQAAAADH3pCs96kzIBJHPZlls6ErYAktA2yuwLKyPFi9iXdea9rTY7PxuUQoZyLXHnuwLgjwAAM0ALYAAAAFZAAgAAAAAO+3mZV828ldgLhwSL9H7PSvmJlv7WrvByHAAAGSof4LBXMAIAAAAAD/ELnpWf3l/SNoUNP0S338kKIp5E1V+3Z99E/3FPXh1QVsACAAAAAAEC+jQrwZIUPbpQq8GjVCzoL+1IX6d2YbIkx0jheRskoFcAAxAAAAAH5K57wo1v6F3PVPdumG6cN3hShAXObXElzOyoLNfG0Uu5KyU7ilPAVwJ5NWL9jZBMwAAzUAtgAAAAVkACAAAAAAE+06hBFHl4f7CHQFGGj2ZZuT3WZEMqTj4CzUM2e9PeoFcwAgAAAAAIIlGRvBtZCaXT4OWFMXF+3VC8jzuJFe3mR5RpM/T47OBWwAIAAAAACBRJPSwvsKXZ7fostzk7s73P1v+MuxlsoSzfGCBVqQWQVwADEAAAAAvx9IM6acqD8WYRJIbL+bwmpzAcv2J0i3W8dOyHdHo5GaGbhUbQs+2XJAsKBk/qIFyAADNgC2AAAABWQAIAAAAACvCIk3Z/O0aqysrOwm+wSmvNpZFsMJBXBhSIBBLiT52AVzACAAAAAA9dRAOydZMcNo1yNh9XP7JS1cA4EEBD95wN3t6NZ8pNUFbAAgAAAAAOV9V/DlJ/McpfffRxJGPtgN5IgReZMW5hjPPCC5wfh8BXAAMQAAAADq6xqnx/gbvKUwg6LsZEPah3z3EDqqUUHd2yTo4Uc8boUF1TAl0lfj6Eu3fu6k0qTcAAM3ALYAAAAFZAAgAAAAAGKJ35r7U30MbUBQIvdcgcQW7MG8KbYldN4PCdrQRRcRBXMAIAAAAACwwmMiXwHvD7BEzhBgfDj4L0AajySh/Qhtz42i5ogkbAVsACAAAAAABcm2+tbs0VSGFn9rDvWyvhuIRVDepRdoIvNa6TRv3zkFcAAxAAAAAIarDydCpSFo/NqlzaMC6E+eBx7LiXrB/fG2Z9szcbLmXjUcSJT2iKLj1tFILB/vmisAAzgAtgAAAAVkACAAAAAAYLNccfL4s3DMHDChNpne5is7B1RfWUpctFbO/r2JTAMFcwAgAAAAAPOdU0Owvp1pFujAWWt7nKFVJxcXTMoVw3ZZiheO1DB2BWwAIAAAAAB1bCFHfdyYaPnhvEnOmfNQZIQRVqhxDSwj79IqNXdlmgVwADEAAAAAXEyApIKBa7i/jiq5ISOwAbfHhwxnDXWMoi46jtZK5PL0TFYnvfIW0kItQIYnrygZ6QADOQC2AAAABWQAIAAAAABLprrCZQNYrlCa1tiktkFmgLViqEn2CEz2GYfCozEMxAVzACAAAAAAI9l/JeiixzTr2mna2nwoaxaJ1oHe+if0AJrtviapy3gFbAAgAAAAAIsIWi7l4L3h7alFc2Mnyg+QQ1EIMZ3D15k/sq+BvaYIBXAAMQAAAABmfDn8qbdhBLJhj1/rb9Ly/kz+GbT1WpkxhezKcoB04iBaBYKJrfSeqH+7Pk8SIN5LAAMxMAC2AAAABWQAIAAAAADUq7UIfHJv/mGbsXxGaSnrk1w2OaF1XIQOdL3Ibv7jAQVzACAAAAAAVYSgjDnwcfQ937v4LtelZhXljZRVaPPKpZ72Eg6Qp5wFbAAgAAAAAJnqBNMfgotr0Bs83VunOklSDAVnoO8FAAs2mjULfoP2BXAAMQAAAADLBVQCW9s1i387ZRgAamYrfC6rt+uPHYFBrs6eJ9LWU8G2/EULjJeAga30urUmqCfTAAMxMQC2AAAABWQAIAAAAAAIz6wOrQqBXWAOmerbQckRy4fLI5AC9k5DhBJ3kHJYJgVzACAAAAAA4c2luh9MO6xSNqffZoH4SwI19i3ffdJ9iyAaGitoZxkFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMQAAAAD4jIxUQRn5M+M3aEB8y8gSZVxGM9DWUnOXnxW4nWvwVBy6M6dsIbMKK0ntDsHoUK5wAAMxMgC2AAAABWQAIAAAAABy6E2SiWcuWFUFZF7u1+9q36Bidd7cB0PMwxN4qUm/iQVzACAAAAAAfXXRP8HSe9qTC4Mhpwm/nGty0swMvdl3Ly7ZwgQ5jjIFbAAgAAAAADPuDapB79oBpsUKLXh9oeESX6227xBMIb3JHWmpWCcvBXAAMQAAAABj+GDDYudxPSCKRvpd2JD43W6j6WiKDj87cuSFutQ1viBwlIcebxdNQCrQk2lc4mTZAAMxMwC2AAAABWQAIAAAAADdcutpKLPVhdR6vTKi+/9b/ngd8WF/gvYzoSOKPGwhLgVzACAAAAAAWm36BOmL6gbNykGY7pHDbJZZEHDxt+WMyNRCYL6dKX0FbAAgAAAAAMd299Bokg6hihom7FZAtMDxLS3VJ+aIngv5kdIdD36KBXAAMQAAAADpphQq0q3J5VDQffMiAdBMfNbxzAka6gLTsD/8iu//3n5Co1mSi/PpkEYj7vm7G/xnAAMxNAC2AAAABWQAIAAAAABMvmSKiuV9tNx7pKKhKPEBb4RlXlptLoUC6XzzqwZM/wVzACAAAAAAQNp806yyelkCYf3mmkfm6CZgaegIJok0BRG3T4wihSUFbAAgAAAAAPsI3ldHLkKkInpttNugj3cAyjtn8+aYWSppd6eF4IP6BXAAMQAAAADaAPXQlyrss/msgXPCjTYcuJoAMlULfUlYiGl8glGovkB5QT5Dyp3Z42oSnLxkuYXiAAMxNQC2AAAABWQAIAAAAACWCYL1HRq7/1vOBbRFajjqpNxf3fVtYNrMKH0CWMxlqQVzACAAAAAAgrBrcFAmjTruvGSRfTuTnuF2+uXz/Vtr3g60RqkG4bYFbAAgAAAAAIQEH2axT3qVk3JPY6vLwoE1x/AdRl7Die4AwXOlTSRPBXAAMQAAAAAUsy6h6usThQG3P97tMBSo39YOF/xc2h+xndzc6fbNpbMRUPw0GhpRxIcQgigl6IF8AAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAAAAEG14AIfWEgAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2.json000066400000000000000000000011021521103432300272740ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2UnindexedEncryptedValue.json000066400000000000000000000003261521103432300277500ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decrypt/FLE2UnindexedEncryptedValueV2.json000066400000000000000000000003321521103432300301550ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "EKvN76sSNJh2EjQSNFZ4kBICUH8j3oVCIjE3koejjIwxHF/nVP95ymiPNUPKskdjx55vK0SasqFyS4LxjZ6fQbzZSoMHPwdmR5otQl9HcMjAcg==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/explicit-decryption-input.json000066400000000000000000000003551521103432300244060ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "AQAAAAAAAAAAAAAAAAAAAAACaWlpaWlpaWlpaWlpaWlpabxsgJndMBCMERBAi/fCAOAQu6/LKU2mu5X7FLmEJlhkHsRr7TxMoz/2akbqBBzCVn1FXiIe9esJCgNhan6EY8g=",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/find-with-encryptionInformation.json000066400000000000000000000005061521103432300255370ustar00rootroot00000000000000{
    "find": "coll",
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.coll": {
                "fields": [],
                "escCollection": "enxcol_.coll.esc",
                "ecocCollection": "enxcol_.coll.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle1-collMod/000077500000000000000000000000001521103432300205725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-collMod/cmd-to-mongocryptd.json000066400000000000000000000003321521103432300252110ustar00rootroot00000000000000{
    "collMod": "encryptedCollection",
    "validator": {
        "$jsonSchema": {
            "bsonType": "object"
        }
    },
    "jsonSchema": {
        "bsonType": "object"
    },
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/fle1-collMod/cmd.json000066400000000000000000000002061521103432300222260ustar00rootroot00000000000000{
    "collMod": "encryptedCollection",
    "validator": {
        "$jsonSchema": {
            "bsonType": "object"
        }
    }
}libmongocrypt-1.19.0/test/data/fle1-collMod/mongocryptd-reply.json000066400000000000000000000004101521103432300251560ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "collMod": "encryptedCollection",
        "validator": {
            "$jsonSchema": {
                "bsonType": "object"
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle1-create/000077500000000000000000000000001521103432300204445ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-create/old-mongocryptd/000077500000000000000000000000001521103432300235655ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-create/old-mongocryptd/cmd.json000066400000000000000000000000271521103432300252220ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/old-mongocryptd/encrypted-payload.json000066400000000000000000000000271521103432300301030ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/old-mongocryptd/ismaster-to-mongocryptd.json000066400000000000000000000000251521103432300312670ustar00rootroot00000000000000{
    "isMaster": 1
}libmongocrypt-1.19.0/test/data/fle1-create/old-mongocryptd/mongocryptd-ismaster.json000066400000000000000000000003611521103432300306520ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1653058237588
    },
    "maxWireVersion": 13,
    "minWireVersion": 0,
    "ok": 1.0
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/000077500000000000000000000000001521103432300234165ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/cmd-to-mongocryptd.json000066400000000000000000000005601521103432300300400ustar00rootroot00000000000000{
    "create": "coll",
    "validator": {
        "$jsonSchema": {
            "properties": {
                "a": {
                    "bsonType": "number"
                }
            }
        }
    },
    "jsonSchema": {
        "properties": {
            "a": {
                "bsonType": "number"
            }
        }
    },
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/cmd.json000066400000000000000000000002671521103432300250610ustar00rootroot00000000000000{
   "create": "coll",
   "validator": {
      "$jsonSchema": {
         "properties": {
            "a": {
               "bsonType": "number"
            }
         }
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/encrypted-payload.json000066400000000000000000000002671521103432300277420ustar00rootroot00000000000000{
   "create": "coll",
   "validator": {
      "$jsonSchema": {
         "properties": {
            "a": {
               "bsonType": "number"
            }
         }
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/ismaster-to-mongocryptd.json000066400000000000000000000000251521103432300311200ustar00rootroot00000000000000{
    "isMaster": 1
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/mongocryptd-ismaster.json000066400000000000000000000003611521103432300305030ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1653058237588
    },
    "maxWireVersion": 17,
    "minWireVersion": 0,
    "ok": 1.0
}libmongocrypt-1.19.0/test/data/fle1-create/with-cmd-schema/mongocryptd-reply.json000066400000000000000000000002061521103432300300050ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll"
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/000077500000000000000000000000001521103432300226555ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-create/with-schema/cmd-to-mongocryptd.json000066400000000000000000000001551521103432300272770ustar00rootroot00000000000000{
    "create": "coll",
    "jsonSchema": {
        "bsonType": "object"
    },
    "isRemoteSchema": false
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/cmd.json000066400000000000000000000000271521103432300243120ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/encrypted-payload.json000066400000000000000000000000271521103432300271730ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/ismaster-to-mongocryptd.json000066400000000000000000000000251521103432300303570ustar00rootroot00000000000000{
    "isMaster": 1
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/mongocryptd-ismaster.json000066400000000000000000000003611521103432300277420ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1653058237588
    },
    "maxWireVersion": 17,
    "minWireVersion": 0,
    "ok": 1.0
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/mongocryptd-reply.json000066400000000000000000000002061521103432300272440ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll"
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle1-create/with-schema/schema-map.json000066400000000000000000000000671521103432300255660ustar00rootroot00000000000000{
    "db.coll": {
        "bsonType": "object"
    }
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/000077500000000000000000000000001521103432300234055ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-create/without-schema/cmd-to-mongocryptd.json000066400000000000000000000001121521103432300300200ustar00rootroot00000000000000{
    "create": "coll",
    "jsonSchema": {},
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/cmd.json000066400000000000000000000000271521103432300250420ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/encrypted-payload.json000066400000000000000000000000271521103432300277230ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/ismaster-to-mongocryptd.json000066400000000000000000000000251521103432300311070ustar00rootroot00000000000000{
    "isMaster": 1
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/mongocryptd-ismaster.json000066400000000000000000000003611521103432300304720ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1653058237588
    },
    "maxWireVersion": 17,
    "minWireVersion": 0,
    "ok": 1.0
}libmongocrypt-1.19.0/test/data/fle1-create/without-schema/mongocryptd-reply.json000066400000000000000000000002061521103432300277740ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll"
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle1-explain/000077500000000000000000000000001521103432300206415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-explain/with-csfle/000077500000000000000000000000001521103432300227065ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-explain/with-csfle/cmd.json000066400000000000000000000001411521103432300243400ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-explain/with-csfle/collinfo.json000066400000000000000000000001561521103432300254100ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "validator": {
            "$jsonSchema": {}
        }
    }
}
libmongocrypt-1.19.0/test/data/fle1-explain/with-csfle/encrypted-payload.json000066400000000000000000000001411521103432300272210ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/000077500000000000000000000000001521103432300241575ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/cmd-to-mongocryptd.json000066400000000000000000000002361521103432300306010ustar00rootroot00000000000000{
    "explain": {
        "find": "test",
        "filter": {
            "value": 123456
        }
    },
    "jsonSchema": {},
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/cmd.json000066400000000000000000000001411521103432300256110ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/collinfo.json000066400000000000000000000001561521103432300266610ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "validator": {
            "$jsonSchema": {}
        }
    }
}
libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/encrypted-payload.json000066400000000000000000000001411521103432300304720ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle1-explain/with-mongocryptd/mongocryptd-reply.json000066400000000000000000000003561521103432300305540ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "explain": {
            "find": "test",
            "filter": {
                "value": 123456
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/000077500000000000000000000000001521103432300234545ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/bad-collinfo.json000066400000000000000000000012001521103432300266710ustar00rootroot00000000000000{
   "name": "test",
   "options": {
      "encryptedFields": {
         "escCollection": "fle2.test.esc",
         "ecocCollection": "fle2.test.ecoc",
         "fields": [
            {
               "keyId": {
                  "$binary": {
                     "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                     "subType": "04"
                  }
               },
               "path": "ssn",
               "bsonType": "string",
               "queries": {
                  "queryType": "equality",
                  "contention": 0
               }
            }
         ],
         "strEncodeVersion": 99
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/bad-create-cmd-mongocryptd-reply.json000066400000000000000000000027661521103432300326060ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
            "fields": [
                {
                    "path": "encrypted",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                                "$numberLong": "0"
                        }
                    }
                }
            ],
            "strEncodeVersion": 1
        },
        "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                        "$numberLong": "0"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/bad-create-cmd-to-mongocryptd.json000066400000000000000000000023351521103432300320650ustar00rootroot00000000000000{
    "create": "coll",
    "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ],
      "strEncodeVersion": 1
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                    "$numberLong": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/bad-create-cmd.json000066400000000000000000000005521521103432300271010ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ],
      "strEncodeVersion": 99
   }
}libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/bad-encrypted-field-config-map.json000066400000000000000000000010201521103432300321600ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "ssn",
            "bsonType": "string",
            "queries": {
               "queryType": "equality",
               "contention": 0
            }
         }
      ],
      "strEncodeVersion": 99
   }
}libmongocrypt-1.19.0/test/data/fle2-bad-str-encode-version/encrypted-payload.json000066400000000000000000000026041521103432300277750ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": {
            "$binary": {
               "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
               "subType": "06"
            }
         }
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "fle2.test.esc",
            "ecocCollection": "fle2.test.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "ssn",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ],
            "strEncodeVersion": 1
         }
      }
   }
}
fle2-create-encrypted-collection-encrypted-fields-unset-str-encode-version/000077500000000000000000000000001521103432300346315ustar00rootroot00000000000000libmongocrypt-1.19.0/test/datacmd-to-mongocryptd.json000066400000000000000000000023471521103432300412600ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-encrypted-fields-unset-str-encode-version{
    "create": "coll",
    "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ]
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                    "$numberLong": "0"
                            }
                        }
                    }
                ],
                "strEncodeVersion": 1
            }
        }
    }
}
mongocryptd-reply.json000066400000000000000000000027761521103432300412360ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-encrypted-fields-unset-str-encode-version{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
            "fields": [
                {
                    "path": "encrypted",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                                "$numberLong": "0"
                        }
                    }
                }
            ]
        },
        "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                        "$numberLong": "0"
                                }
                            }
                        }
                    ],
                    "strEncodeVersion": 1
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version/000077500000000000000000000000001521103432300312665ustar00rootroot00000000000000cmd-to-mongocryptd.json000066400000000000000000000024041521103432300356300ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version{
    "create": "coll",
    "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ],
      "strEncodeVersion": 1
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                    "$numberLong": "0"
                            }
                        }
                    }
                ],
                "strEncodeVersion": 1
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version/cmd.json000066400000000000000000000005511521103432300327250ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ],
      "strEncodeVersion": 1
   }
}encrypted-field-config-map.json000066400000000000000000000012161521103432300371760ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version{
    "db.coll": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "int",
                "queries": {
                    "queryType": "equality",
                    "contention": {
                            "$numberLong": "0"
                    }
                }
            }
        ],
        "strEncodeVersion": 1
    }
}
encrypted-payload.json000066400000000000000000000005511521103432300355270ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ],
      "strEncodeVersion": 1
   }
}mongocryptd-reply.json000066400000000000000000000030411521103432300355760ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection-with-str-encode-version{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
            "fields": [
                {
                    "path": "encrypted",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                                "$numberLong": "0"
                        }
                    }
                }
            ],
            "strEncodeVersion": 1
        },
        "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                        "$numberLong": "0"
                                }
                            }
                        }
                    ],
                    "strEncodeVersion": 1
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/000077500000000000000000000000001521103432300245715ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/cmd-to-mongocryptd.json000066400000000000000000000023001521103432300312050ustar00rootroot00000000000000{
    "create": "coll",
    "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ]
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                    "$numberLong": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/cmd.json000066400000000000000000000005141521103432300262270ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ]
   }
}libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/encrypted-field-config-map.json000066400000000000000000000011571521103432300325640ustar00rootroot00000000000000{
    "db.coll": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "int",
                "queries": {
                    "queryType": "equality",
                    "contention": {
                            "$numberLong": "0"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/encrypted-payload.json000066400000000000000000000005141521103432300311100ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "equality",
               "contention": {
                     "$numberLong": "0"
               }
            }
         }
      ]
   }
}libmongocrypt-1.19.0/test/data/fle2-create-encrypted-collection/mongocryptd-reply.json000066400000000000000000000027231521103432300311660ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
            "fields": [
                {
                    "path": "encrypted",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                                "$numberLong": "0"
                        }
                    }
                }
            ]
        },
        "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                        "$numberLong": "0"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-create/000077500000000000000000000000001521103432300204455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-create/cmd-to-mongocryptd.json000066400000000000000000000004061521103432300250660ustar00rootroot00000000000000{
    "create": "coll",
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": []
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-create/cmd.json000066400000000000000000000000271521103432300221020ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle2-create/encrypted-field-config-map.json000066400000000000000000000001621521103432300264330ustar00rootroot00000000000000{
    "db.coll": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": []
    }
}
libmongocrypt-1.19.0/test/data/fle2-create/encrypted-payload.json000066400000000000000000000000271521103432300247630ustar00rootroot00000000000000{
   "create": "coll"
}libmongocrypt-1.19.0/test/data/fle2-create/mongocryptd-reply.json000066400000000000000000000002061521103432300250340ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll"
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-decrypt-ieev/000077500000000000000000000000001521103432300216025ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-decrypt-ieev/first-filter.json000066400000000000000000000006541521103432300251140ustar00rootroot00000000000000{
    "$or": [
        {
            "_id": {
                "$in": [
                    {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    }
                ]
            }
        },
        {
            "keyAltNames": {
                "$in": []
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-decrypt-ieev/second-filter.json000066400000000000000000000006541521103432300252400ustar00rootroot00000000000000{
    "$or": [
        {
            "_id": {
                "$in": [
                    {
                        "$binary": {
                            "base64": "q83vqxI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    }
                ]
            }
        },
        {
            "keyAltNames": {
                "$in": []
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-delete/000077500000000000000000000000001521103432300204445ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-delete/empty/000077500000000000000000000000001521103432300216025ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-delete/empty/cmd.json000066400000000000000000000001141521103432300232340ustar00rootroot00000000000000{
   "delete": "test",
   "deletes": [
      { "q": { }, "limit": 1}
   ]
}
libmongocrypt-1.19.0/test/data/fle2-delete/empty/collinfo.json000066400000000000000000000030041521103432300242770ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-delete/empty/encrypted-field-config-map.json000066400000000000000000000024411521103432300275720ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-delete/empty/encrypted-payload-v2.json000066400000000000000000000030461521103432300264510ustar00rootroot00000000000000{
   "delete": "test",
   "deletes": [
      {
         "q": {},
         "limit": 1
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.notindexed",
                  "bsonType": "string"
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-delete/empty/mongocryptd-reply.json000066400000000000000000000032631521103432300261770ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "delete": "test",
      "deletes": [
         {
            "q": {},
            "limit": 1
         }
      ],
      "$db": "test"
   },
   "hasEncryptedPlaceholders": false,
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.notindexed",
                  "bsonType": "string"
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-delete/success/000077500000000000000000000000001521103432300221145ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-delete/success/cmd.json000066400000000000000000000001341521103432300235500ustar00rootroot00000000000000{
   "delete": "test",
   "deletes": [
      { "q": { "value": 123456 }, "limit": 1}
   ]
}
libmongocrypt-1.19.0/test/data/fle2-delete/success/collinfo.json000066400000000000000000000030041521103432300246110ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.encrypted",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                },
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                            "subType": "04"
                        }
                    },
                    "path": "nested.notindexed",
                    "bsonType": "string"
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-delete/success/encrypted-field-config-map.json000066400000000000000000000024411521103432300301040ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                    }
                },
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                    }
                },
                "path": "nested.notindexed",
                "bsonType": "string"
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-delete/success/encrypted-payload-v2.json000066400000000000000000000035741521103432300267710ustar00rootroot00000000000000{
   "delete": "test",
   "deletes": [
      {
         "q": {
            "value": {
               "$binary": {
                  "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
                  "subType": "06"
               }
            }
         },
         "limit": 1
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.notindexed",
                  "bsonType": "string"
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-delete/success/mongocryptd-reply.json000066400000000000000000000037101521103432300265060ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "delete": "test",
      "deletes": [
         {
            "q": {
               "value": {
                  "$binary": {
                     "base64": "A1gAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASEHYAQOIBABJjbQAAAAAAAAAAAAA=",
                     "subType": "06"
                  }
               }
            },
            "limit": 1
         }
      ]
   },
   "hasEncryptedPlaceholders": true,
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEw==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.encrypted",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               },
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQFA==",
                        "subType": "04"
                     }
                  },
                  "path": "nested.notindexed",
                  "bsonType": "string"
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-duplicate-keyaltname/000077500000000000000000000000001521103432300233045ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-duplicate-keyaltname/encrypted-field-config-map.json000066400000000000000000000012561521103432300312770ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyAltName": "duplicateKey",
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            },
            {
                "keyAltName": "duplicateKey",
                "path": "nested.encrypted",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            }
        ]
    }
}

libmongocrypt-1.19.0/test/data/fle2-explain/000077500000000000000000000000001521103432300206425ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-explain/with-csfle/000077500000000000000000000000001521103432300227075ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-explain/with-csfle/cmd.json000066400000000000000000000001411521103432300243410ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle2-explain/with-csfle/collinfo.json000066400000000000000000000014141521103432300254070ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.test.esc",
            "ecocCollection": "enxcol_.test.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "value",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explain/with-csfle/encrypted-payload.json000066400000000000000000000025161521103432300272320ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": {
            "$eq": {
               "$binary": {
                  "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
                  "subType": "06"
               }
            }
         }
      },
      "encryptionInformation": {
         "type": {
            "$numberInt": "1"
         },
         "schema": {
            "db.test": {
               "escCollection": "enxcol_.test.esc",
               "ecocCollection": "enxcol_.test.ecoc",
               "fields": [
                  {
                     "keyId": {
                        "$binary": {
                           "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                           "subType": "04"
                        }
                     },
                     "path": "value",
                     "bsonType": "int",
                     "queries": {
                        "queryType": "equality",
                        "contention": {
                           "$numberLong": "0"
                        }
                     }
                  }
               ]
            }
         }
      }
   },
   "verbosity": "allPlansExecution"
}
libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/000077500000000000000000000000001521103432300241605ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/cmd-to-mongocryptd.json000066400000000000000000000017271521103432300306100ustar00rootroot00000000000000{
    "explain": {
        "find": "test",
        "filter": {
            "value": 123456
        }
    },
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.test": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "value",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberLong": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/cmd.json000066400000000000000000000001411521103432300256120ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": 123456
      }
   }
}libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/collinfo.json000066400000000000000000000013621521103432300266620ustar00rootroot00000000000000{
    "name": "test",
    "options": {
        "encryptedFields": {
            "escCollection": "esc",
            "ecocCollection": "ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                            "subType": "04"
                        }
                    },
                    "path": "value",
                    "bsonType": "int",
                    "queries": {
                        "queryType": "equality",
                        "contention": {
                            "$numberLong": "0"
                        }
                    }
                }
            ]
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/encrypted-payload.json000066400000000000000000000022671521103432300305060ustar00rootroot00000000000000{
   "explain": {
      "find": "test",
      "filter": {
         "value": {
            "$binary": {
               "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
               "subType": "06"
            }
         }
      },
      "encryptionInformation": {
         "type": 1,
         "schema": {
            "db.test": {
               "escCollection": "esc",
               "ecocCollection": "ecoc",
               "fields": [
                  {
                     "keyId": {
                        "$binary": {
                           "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                           "subType": "04"
                        }
                     },
                     "path": "value",
                     "bsonType": "int",
                     "queries": {
                        "queryType": "equality",
                        "contention": {
                           "$numberLong": "0"
                        }
                     }
                  }
               ]
            }
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-explain/with-mongocryptd/mongocryptd-reply.json000066400000000000000000000025131521103432300305520ustar00rootroot00000000000000{
   "ok": {
      "$numberInt": "1"
   },
   "result": {
      "explain": {
         "find": "test",
         "filter": {
            "value": {
               "$binary": {
                  "base64": "A1gAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASEHYAQOIBABJjbQAAAAAAAAAAAAA=",
                  "subType": "06"
               }
            }
         },
         "encryptionInformation": {
            "type": 1,
            "schema": {
               "db.test": {
                  "escCollection": "esc",
                  "ecocCollection": "ecoc",
                  "fields": [
                     {
                        "keyId": {
                           "$binary": {
                              "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                              "subType": "04"
                           }
                        },
                        "path": "value",
                        "bsonType": "int",
                        "queries": {
                           "queryType": "equality",
                           "contention": {
                              "$numberLong": "0"
                           }
                        }
                     }
                  ]
               }
            }
         }
      }
   },
   "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-explicit/000077500000000000000000000000001521103432300210235ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-explicit/find-indexed-contentionFactor1-v2.json000066400000000000000000000004421521103432300301770ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAQAAAAAAAAAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explicit/find-indexed-v2.json000066400000000000000000000004421521103432300246010ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explicit/find-prefix.json000066400000000000000000000005511521103432300241320ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "Er0AAAADdHMAhQAAAANwAH0AAAAFZAAgAAAAACL5f/dBh7K1gY396gD8HKP3XDi/FSAGp4ffKOD/FbprBXMAIAAAAAB1007DUNaflVn1BainFSvT65Y402yCTYyj59IWNCbpsQVsACAAAAAAqdStZEVKBgZbB0lWO/3SphxOMF8uPRQWlN82uVI2LsIAABJjbQABAAAAAAAAAAhjZgAACGRmAAADcHMAFQAAABB1YgBkAAAAEGxiAAEAAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/find-substring.json000066400000000000000000000005651521103432300246620ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "EscAAAADdHMAhQAAAANzAH0AAAAFZAAgAAAAAJ9LspiNsBY6xthbbTxdISLmF5Zm3ZvSr2ThQ3yETOuiBXMAIAAAAADamyhF6iclTbyM5BSqcEvbaCvIC73e48kS2GEwTkuJfwVsACAAAAAA/3xSivgmFjREXrAkEfZ3ryqhIm+io8iP2jBktzNI1T0AABJjbQABAAAAAAAAAAhjZgAACGRmAAADc3MAHwAAABBtbGVuAGQAAAAQdWIAZAAAABBsYgABAAAAAAA=",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/find-suffix.json000066400000000000000000000005511521103432300241410ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "Er0AAAADdHMAhQAAAAN1AH0AAAAFZAAgAAAAAHEjIXCAoaQV2eoWo6Khd3algUp373wD3XmRtEhiBBOABXMAIAAAAADT9JW1LuNyR1iI4pG8Uwp5itS8T8UExpTfcEyiRP8N6gVsACAAAAAAFK8lnQCIRs6zUFqsPSPwW3xPn9yvwtDi+sDgpcVrEnQAABJjbQABAAAAAAAAAAhjZgAACGRmAAADZnMAFQAAABB1YgBkAAAAEGxiAAEAAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/insert-casef-diacf.json000066400000000000000000000070411521103432300253470ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C0cKAAAFZAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXMAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVwADMAAAAAawypOS/aaIjABSYj+6eFHhsQfplrQnmUCuKRTNT+kP/RLUqcT4GfaRGj7s6AIgwhlWYxBXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEviVTCP+EyZyZ6MZ4bcsJO93e43aiqLi17Bp6I4a4GlbHiB24BsghlfCH3DVmi9YyVuTOHhqgvh5O9fCNJi5UKYFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJrAAEAAAAAAAAAA2IA4ggAAANlALgAAAAFZAAgAAAAAEYd4ktgJg/MsJj9N+mjV8HPZD1w7rynNQX40FtLeRjMBXMAIAAAAACoE4BozVfq23FC5PILDNQqG284A0ogt7qOTd9RMtWyVgVsACAAAAAAWgzO7I0Sgkhf4Qs9OI0oNhlv49YhJuCT3VcYSAMCVFAFcAAzAAAAADagFBbIV/Duye4cpyWOB+Exrs11XvnywQlgzXqcywex2/UcF4iV0t5+wTfyAT1+x81PWAAEcwAFAAAAAAR1AAUAAAAABHAADwgAAAMwALgAAAAFZAAgAAAAAARHg82tsJIHU7nHwmJDNJymKSjEJ6exeVbTiUOO2N7wBXMAIAAAAAAA31TlLEzrhsAofNu6uWPM1JrgMPv60EPg4q0M5VCVZAVsACAAAAAAbJUWdJ1lnKJMUEkYh4MgE13Mj1CA66s1tpSt8+K0zzIFcAAzAAAAAEclcgMycHqaHJ2cPwy94xNr+kAzaB6CacNg2EdBIZ0eIymF3xK8IpLR29A291/EYMXscwADMQC4AAAABWQAIAAAAABNsyDojV5crtUPxIGLaXEJR8XXlqsF61sHLXRbvpPAcwVzACAAAAAA2fbcd68XzHv6qam3iz+yUh14myGStOswQYqO87hX4yQFbAAgAAAAAMqAvSL/gVjli8ImAbUszOd9SbehEDmgRknyzRT0BFcOBXAAMwAAAAD/J7rdqowIpGsvrV3OX8qv3odkl84mFvyuOmEAWWAk2lICyGXymPwwdt8owQLJ2/jwhAYAAzIAuAAAAAVkACAAAAAAk5+cF+4e0UWd+VoKUhOu18R+PdftXRaWgGLIq204/2gFcwAgAAAAAMK7xBBzXD20mxT0dJ7qUpTHCOqt725wfw8KQDJb8lPjBWwAIAAAAACp1K1kRUoGBlsHSVY7/dKmHE4wXy49FBaU3za5UjYuwgVwADMAAAAAdcPZ/+RcNrkC2clu7kNJKMW1iMjL6FoKohNAMQnXHqMjS/TebHfJrL3gB1yZJZEEEYX+AAMzALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADNAC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGEQ8wicWbhhTQbLPyJuchM0lpYWhXcOVaLuDWZ4e+a1PzGksAAzUAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAM2ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADNwC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGEQ8wicWbhhTQbLPyJuchM0lpYWhXcOVaLuDWZ4e+a1PzGksAAzgAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAM5ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADMTAAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/insert-indexed-contentionFactor1-v2.json000066400000000000000000000011021521103432300305550ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C18BAAAFZAAgAAAAAK8vKf5uqsCsWXfVhVEn9Jg0vWDIh4wtGqtUkG5wKE/CBXMAIAAAAACckqDJyQ9Po3BPcRqUNT2F2wlvLVpSmQrIeIF/PhJC5QVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ct+wWQob1cxkm5UHJpuEoqEzTTETyU88bAxgupUdmQJBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAEAAAAAAAAAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explicit/insert-indexed-same-user-and-index-key-v2.json000066400000000000000000000011021521103432300315110ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C18BAAAFZAAgAAAAAPgBTEb6RugdmNK6uwbgw/F3XW/QIeohkfnfSNx9sxt2BXMAIAAAAABaOvNbHZrQIOc3iClKudHruW80XUvQNJL/wmI0My6S1gVwADAAAAAAx0PWdXaep4jV5cRA2yQN+cXcTw/4UdiqCRfvw4gIGS29ZXCDmYfJX4aT3XrZ8bq0BXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAACfXTZ61xxjHKYYkkdPgPUKv/oyVTfiLGwIOHysMDZPjBJrAAAAAAAAAAAAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explicit/insert-indexed-v2.json000066400000000000000000000011021521103432300251570ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-explicit/insert-prefix-suffix.json000066400000000000000000000143311521103432300260210ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C1ESAAAFZAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXMAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVwADMAAAAAawypOS/aaIjABSYj+6eFHhsQfplrQnmUCuKRTNT+kP/RLUqcT4GfaRGj7s6AIgwhlWYxBXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEviVTCP+EyZyZ6MZ4bcsJO93e43aiqLi17Bp6I4a4GlbHiB24BsghlfCH3DVmi9YyVuTOHhqgvh5O9fCNJi5UKYFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJrAAEAAAAAAAAAA2IA7BAAAANlALgAAAAFZAAgAAAAAEYd4ktgJg/MsJj9N+mjV8HPZD1w7rynNQX40FtLeRjMBXMAIAAAAACoE4BozVfq23FC5PILDNQqG284A0ogt7qOTd9RMtWyVgVsACAAAAAAWgzO7I0Sgkhf4Qs9OI0oNhlv49YhJuCT3VcYSAMCVFAFcAAzAAAAADagFBbIV/Duye4cpyWOB+Exrs11XvnywQlgzXqcywex2/UcF4iV0t5+wTfyAT1+x9ZPWAAEcwAFAAAAAAR1AA8IAAADMAC4AAAABWQAIAAAAADBOr99J8ZdEJs3Q2oTW6huN3BM3k1Ii83y6rQoqrsWTAVzACAAAAAAJ2idfKvrSH2CQGYdipk8t/z4JIDADr0j3fZ66uKyFbcFbAAgAAAAAHdM0bMtbpFhjTfwACjOmkD9zfYJe8ZR0U2XgAL5dhNZBXAAMwAAAABHJXIDMnB6mhydnD8MveMTTE2Jqu+5IZKBCMKBcQHCZQtLQW8pSE/y7M8H0PC9RLPF7HMAAzEAuAAAAAVkACAAAAAA0DW7/GWHJ2g/2EGfbnHAkzAdRC79qoR8UbBQHVHV04MFcwAgAAAAAOp/6qCfmlqH9DW7m64CPi2FBDEZW5tsnRYifgV3zUO6BWwAIAAAAABaw8Bf04y+OgnQHJ17nK074J5+zLIYd6pXY1wOyn+8sgVwADMAAAAA/ye63aqMCKRrL61dzl/Kr+0OUkD+q4AAoKZzLHxdqKXKfmJdO7d7nSF32DfNU3tm8IQGAAMyALgAAAAFZAAgAAAAABXufbjoS/ORDFF7rBO0ZNGPvU3WyinRSc2uaXw297s2BXMAIAAAAAD0L9zk5Ul/jBVcBWE81X26fDZZaNMLc1DdF5wGaBFqlwVsACAAAAAAFK8lnQCIRs6zUFqsPSPwW3xPn9yvwtDi+sDgpcVrEnQFcAAzAAAAAHXD2f/kXDa5AtnJbu5DSSjzIZA8Xf0YMixbsSSr6DGNmHVHG1ASyoNv/dtoqsaocBGF/gADMwC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzQAuAAAAAVkACAAAAAAb68eGlPfIV11Jx1y9ZAbBWK7zLMTo1EpJOhtSx7CB8QFcwAgAAAAADIwgsGPbY4vwDSun78baoLZi+P5d2nF7kQ75/vf3L3ZBWwAIAAAAAD8zBpULgUVnDObcZ7COraS/z6svZ17BWyenVbqWNikSwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxohKijFQqY0oHD+zILfjGT/DRUVk3pfBoa987N2z6GiZ8xpLAAM1ALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwADNgC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzcAuAAAAAVkACAAAAAAb68eGlPfIV11Jx1y9ZAbBWK7zLMTo1EpJOhtSx7CB8QFcwAgAAAAADIwgsGPbY4vwDSun78baoLZi+P5d2nF7kQ75/vf3L3ZBWwAIAAAAAD8zBpULgUVnDObcZ7COraS/z6svZ17BWyenVbqWNikSwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxohKijFQqY0oHD+zILfjGT/DRUVk3pfBoa987N2z6GiZ8xpLAAM4ALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwADOQC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzEwALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwAABHAADwgAAAMwALgAAAAFZAAgAAAAAARHg82tsJIHU7nHwmJDNJymKSjEJ6exeVbTiUOO2N7wBXMAIAAAAAAA31TlLEzrhsAofNu6uWPM1JrgMPv60EPg4q0M5VCVZAVsACAAAAAAbJUWdJ1lnKJMUEkYh4MgE13Mj1CA66s1tpSt8+K0zzIFcAAzAAAAAN3JgiYA0dZn4YlUMSScsT/isroWf4kwpr2XrWknXXL/dgWDCbtYR4IVQ0UFJzXbK0ajOwADMQC4AAAABWQAIAAAAABNsyDojV5crtUPxIGLaXEJR8XXlqsF61sHLXRbvpPAcwVzACAAAAAA2fbcd68XzHv6qam3iz+yUh14myGStOswQYqO87hX4yQFbAAgAAAAAMqAvSL/gVjli8ImAbUszOd9SbehEDmgRknyzRT0BFcOBXAAMwAAAADQ2d7kY9me1Vq3ICYwq0i9njRmB3s81ViBmSFz6+BrVWkRR3bgrg0AwXnizisDRTPkEHMAAzIAuAAAAAVkACAAAAAAk5+cF+4e0UWd+VoKUhOu18R+PdftXRaWgGLIq204/2gFcwAgAAAAAMK7xBBzXD20mxT0dJ7qUpTHCOqt725wfw8KQDJb8lPjBWwAIAAAAACp1K1kRUoGBlsHSVY7/dKmHE4wXy49FBaU3za5UjYuwgVwADMAAAAArCNflUuWF0/Vl03cJNdG2AFJwhJpYvSH8kPVCosYlflD1fLAQaLNCE1h1IWF8hC4MsdfAAMzALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAHNtlT8KUdtg52D8aFt4jgKZ71NveenoUOhXFaCLiNogJkm+jvF+5H/IoodSk71AoxPmvQADNAC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABzbZU/ClHbYOdg/GhbeI4Cme9Tb3np6FDoVxWgi4jaICZJvo7xfuR/yKKHUpO9QKMT5r0AAzUAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAAc22VPwpR22DnYPxoW3iOApnvU2956ehQ6FcVoIuI2iAmSb6O8X7kf8iih1KTvUCjE+a9AAM2ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAHNtlT8KUdtg52D8aFt4jgKZ71NveenoUOhXFaCLiNogJkm+jvF+5H/IoodSk71AoxPmvQADNwC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABzbZU/ClHbYOdg/GhbeI4Cme9Tb3np6FDoVxWgi4jaICZJvo7xfuR/yKKHUpO9QKMT5r0AAzgAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAAc22VPwpR22DnYPxoW3iOApnvU2956ehQ6FcVoIuI2iAmSb6O8X7kf8iih1KTvUCjE+a9AAM5ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAHNtlT8KUdtg52D8aFt4jgKZ71NveenoUOhXFaCLiNogJkm+jvF+5H/IoodSk71AoxPmvQADMTAAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAAc22VPwpR22DnYPxoW3iOApnvU2956ehQ6FcVoIuI2iAmSb6O8X7kf8iih1KTvUCjE+a9AAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/insert-prefix.json000066400000000000000000000070411521103432300245170ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C0cKAAAFZAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXMAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVwADMAAAAAawypOS/aaIjABSYj+6eFHhsQfplrQnmUCuKRTNT+kP/RLUqcT4GfaRGj7s6AIgwhlWYxBXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEviVTCP+EyZyZ6MZ4bcsJO93e43aiqLi17Bp6I4a4GlbHiB24BsghlfCH3DVmi9YyVuTOHhqgvh5O9fCNJi5UKYFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJrAAEAAAAAAAAAA2IA4ggAAANlALgAAAAFZAAgAAAAAEYd4ktgJg/MsJj9N+mjV8HPZD1w7rynNQX40FtLeRjMBXMAIAAAAACoE4BozVfq23FC5PILDNQqG284A0ogt7qOTd9RMtWyVgVsACAAAAAAWgzO7I0Sgkhf4Qs9OI0oNhlv49YhJuCT3VcYSAMCVFAFcAAzAAAAADagFBbIV/Duye4cpyWOB+Exrs11XvnywQlgzXqcywex2/UcF4iV0t5+wTfyAT1+x81PWAAEcwAFAAAAAAR1AAUAAAAABHAADwgAAAMwALgAAAAFZAAgAAAAAARHg82tsJIHU7nHwmJDNJymKSjEJ6exeVbTiUOO2N7wBXMAIAAAAAAA31TlLEzrhsAofNu6uWPM1JrgMPv60EPg4q0M5VCVZAVsACAAAAAAbJUWdJ1lnKJMUEkYh4MgE13Mj1CA66s1tpSt8+K0zzIFcAAzAAAAAEclcgMycHqaHJ2cPwy94xNr+kAzaB6CacNg2EdBIZ0eIymF3xK8IpLR29A291/EYMXscwADMQC4AAAABWQAIAAAAABNsyDojV5crtUPxIGLaXEJR8XXlqsF61sHLXRbvpPAcwVzACAAAAAA2fbcd68XzHv6qam3iz+yUh14myGStOswQYqO87hX4yQFbAAgAAAAAMqAvSL/gVjli8ImAbUszOd9SbehEDmgRknyzRT0BFcOBXAAMwAAAAD/J7rdqowIpGsvrV3OX8qv3odkl84mFvyuOmEAWWAk2lICyGXymPwwdt8owQLJ2/jwhAYAAzIAuAAAAAVkACAAAAAAk5+cF+4e0UWd+VoKUhOu18R+PdftXRaWgGLIq204/2gFcwAgAAAAAMK7xBBzXD20mxT0dJ7qUpTHCOqt725wfw8KQDJb8lPjBWwAIAAAAACp1K1kRUoGBlsHSVY7/dKmHE4wXy49FBaU3za5UjYuwgVwADMAAAAAdcPZ/+RcNrkC2clu7kNJKMW1iMjL6FoKohNAMQnXHqMjS/TebHfJrL3gB1yZJZEEEYX+AAMzALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADNAC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGEQ8wicWbhhTQbLPyJuchM0lpYWhXcOVaLuDWZ4e+a1PzGksAAzUAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAM2ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADNwC4AAAABWQAIAAAAACN8M2zrnaA+So6FBAuA80ZPXv1I3pgLgbDcwzUO/XQnQVzACAAAAAAq3U4eRpfhRMMZ65NLh9SjlOnx/X+juEVxafdQeuKvhMFbAAgAAAAAN689uDJadMkhEC9UEF2wVPSvszWnpWRSt4t5vZSqbInBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGEQ8wicWbhhTQbLPyJuchM0lpYWhXcOVaLuDWZ4e+a1PzGksAAzgAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAM5ALgAAAAFZAAgAAAAAI3wzbOudoD5KjoUEC4DzRk9e/UjemAuBsNzDNQ79dCdBXMAIAAAAACrdTh5Gl+FEwxnrk0uH1KOU6fH9f6O4RXFp91B64q+EwVsACAAAAAA3rz24Mlp0ySEQL1QQXbBU9K+zNaelZFK3i3m9lKpsicFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsYRDzCJxZuGFNBss/Im5yEzSWlhaFdw5Vou4NZnh75rU/MaSwADMTAAuAAAAAVkACAAAAAAjfDNs652gPkqOhQQLgPNGT179SN6YC4Gw3MM1Dv10J0FcwAgAAAAAKt1OHkaX4UTDGeuTS4fUo5Tp8f1/o7hFcWn3UHrir4TBWwAIAAAAADevPbgyWnTJIRAvVBBdsFT0r7M1p6VkUreLeb2UqmyJwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxhEPMInFm4YU0Gyz8ibnITNJaWFoV3DlWi7g1meHvmtT8xpLAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/insert-substring.json000066400000000000000000000417711521103432300252520ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C6syAAAFZAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXMAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVwADMAAAAAawypOS/aaIjABSYj+6eFHhsQfplrQnmUCuKRTNT+kP/RLUqcT4GfaRGj7s6AIgwhlWYxBXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEviVTCP+EyZyZ6MZ4bcsJO93e43aiqLi17Bp6I4a4GlbHiB24BsghlfCH3DVmi9YyVuTOHhqgvh5O9fCNJi5UKYFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJrAAEAAAAAAAAAA2IARjEAAANlALgAAAAFZAAgAAAAAEYd4ktgJg/MsJj9N+mjV8HPZD1w7rynNQX40FtLeRjMBXMAIAAAAACoE4BozVfq23FC5PILDNQqG284A0ogt7qOTd9RMtWyVgVsACAAAAAAWgzO7I0Sgkhf4Qs9OI0oNhlv49YhJuCT3VcYSAMCVFAFcAAzAAAAADagFBbIV/Duye4cpyWOB+Exrs11XvnywQlgzXqcywex2/UcF4iV0t5+wTfyAT1+x4JPWAAEcwBzMAAAAzAAuAAAAAVkACAAAAAAlsZdVdH1EyIJyAzuuy8NZ2e3a25yCuBkfReGmpV21aEFcwAgAAAAAIYJ1ujjjnZ6S/VjY+QcDgdrfPI0ujMBEmY5sUVSDnTUBWwAIAAAAAAjCDIFif2+bNvSV8fVnGlyi7HzMb9mfRkC1f22lsMHVAVwADMAAAAARyVyAzJwepocnZw/DL3jE+0swj6n3B+VSL3H/x+E8NWcz5fbU3Xzw1cAzH9AASXQxexzAAMxALgAAAAFZAAgAAAAACXXRvbP1qIVKFVIWlzcYPvNQ4TC2TEEiOYBXM0ifJ/EBXMAIAAAAACp4BhYs7fuSZnZBNYL/a2+kjGzFLmreNRPuqPgfCtVOwVsACAAAAAA/3xSivgmFjREXrAkEfZ3ryqhIm+io8iP2jBktzNI1T0FcAAzAAAAAP8nut2qjAikay+tXc5fyq+ukaC40oY0zs1KzGHZojs23UvgUNmHb9R47wXSxrVt5/CEBgADMgC4AAAABWQAIAAAAAAnPbdCZRpcWAfTw4MQ+e2R4cZxSVkKZuXg0oYflWNgLwVzACAAAAAAdb97wQA8pjvL3cblHiXnbxJVU1qrKP1tgjIaS8v8tgIFbAAgAAAAAGg8X3MZy31fmJnEc6mvAHfMKgYLqxy1l//v2In2MgbgBXAAMwAAAAB1w9n/5Fw2uQLZyW7uQ0kocrE3GbiIwYXy2nKgiRirWPYWTSkoMUS+MNhdJQkrdOURhf4AAzMAuAAAAAVkACAAAAAA2T1Jt7lysNTtl6NaRkgmzx/PVhuAPhJfuesuj0U//JcFcwAgAAAAAOFldt0BRHhYH1glarYgGO8UayxE0lwSLpuxIo9O0SrqBWwAIAAAAAD0+SY7d95P4ttBicHOTTET2jCVGyp665Ta8mSk+o3ARAVwADMAAAAATnaU6Vq5RrV84rdnH/wCxlsffi3egHtfw1M41b7Ya1IOpYrZe6IWYXD2Kaki5f+q8xpLAAM0ALgAAAAFZAAgAAAAAFFtA+DD/gX5QjuveZEBKsOUeQtr5qiRKsEXgUoa9Re4BXMAIAAAAACbm9gDnPtGq8ljl/6nfxmj4bo41R47sbkxq8H+dh7pNwVsACAAAAAAtYPZ8581LlCQzbxcwEf7pF8B1dKFqqRD9jjxq9dBHP0FcAAzAAAAAN3JgiYA0dZn4YlUMSScsT959jbwzz6di7TcRkw6mwiQQyVb7F6ZJnjECin3tHuneEajOwADNQC4AAAABWQAIAAAAABG+4weYawLxWAmuCk3VrJGLME/vM5COa61PpjLzEsShQVzACAAAAAAo6AZN+pRJN64QzQCOHaOyzIxmlfqWS6SRrhrko4ot9kFbAAgAAAAAHqVETtH0lb+npVfYHNmEnnKVD7UrvIbR1LIPprN7VIXBXAAMwAAAADQ2d7kY9me1Vq3ICYwq0i95GKjRz56Pf3Dc7zGWKlXzEZYRgCYQ8iixksHrx18Ec7kEHMAAzYAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM3ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADOAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzkAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMxMAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzExALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMTIAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMxMwC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzE0ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMTUAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMxNgC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzE3ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMTgAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMxOQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzIwALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMjEAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMyMgC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzIzALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMjQAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMyNQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzI2ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMjcAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMyOAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzI5ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMzAAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMzMQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzMyALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMzMAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMzNAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzM1ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMzYAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAMzNwC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzM4ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADMzkAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM0MAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzQxALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNDIAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM0MwC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzQ0ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNDUAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM0NgC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzQ3ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNDgAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM0OQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzUwALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNTEAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM1MgC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzUzALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNTQAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM1NQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzU2ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNTcAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM1OAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzU5ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNjAAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM2MQC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzYyALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwADNjMAuAAAAAVkACAAAAAAZpQKQEQ4cuGFSVMQhDqviERoTEjJgcFW2L/J0GAjzIwFcwAgAAAAACj2KiodPDH5SqmzofcJELEiH6BsqPM7RDzi77c01nssBWwAIAAAAACIiqNLeyGhitrl7gKP/XCC6h0/SffRSV2bnl9ibzRifgVwADMAAAAArCNflUuWF0/Vl03cJNdG2OsELCgHAvjKI/6S3+L719ymwrgBBj+GM36JewDq1jh3MsdfAAM2NAC4AAAABWQAIAAAAABmlApARDhy4YVJUxCEOq+IRGhMSMmBwVbYv8nQYCPMjAVzACAAAAAAKPYqKh08MflKqbOh9wkQsSIfoGyo8ztEPOLvtzTWeywFbAAgAAAAAIiKo0t7IaGK2uXuAo/9cILqHT9J99FJXZueX2JvNGJ+BXAAMwAAAACsI1+VS5YXT9WXTdwk10bY6wQsKAcC+Moj/pLf4vvX3KbCuAEGP4Yzfol7AOrWOHcyx18AAzY1ALgAAAAFZAAgAAAAAGaUCkBEOHLhhUlTEIQ6r4hEaExIyYHBVti/ydBgI8yMBXMAIAAAAAAo9ioqHTwx+Uqps6H3CRCxIh+gbKjzO0Q84u+3NNZ7LAVsACAAAAAAiIqjS3shoYra5e4Cj/1wguodP0n30Uldm55fYm80Yn4FcAAzAAAAAKwjX5VLlhdP1ZdN3CTXRtjrBCwoBwL4yiP+kt/i+9fcpsK4AQY/hjN+iXsA6tY4dzLHXwAABHUABQAAAAAEcAAFAAAAAAAA",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-explicit/insert-suffix.json000066400000000000000000000070411521103432300245260ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C0cKAAAFZAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXMAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVwADMAAAAAawypOS/aaIjABSYj+6eFHhsQfplrQnmUCuKRTNT+kP/RLUqcT4GfaRGj7s6AIgwhlWYxBXUAEAAAAASrze+rEjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEviVTCP+EyZyZ6MZ4bcsJO93e43aiqLi17Bp6I4a4GlbHiB24BsghlfCH3DVmi9YyVuTOHhqgvh5O9fCNJi5UKYFZQAgAAAAAL7iv5ju6p02+CadotQZUkgqtSIYD2HaywGsizUpIBYMBWwAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJrAAEAAAAAAAAAA2IA4ggAAANlALgAAAAFZAAgAAAAAEYd4ktgJg/MsJj9N+mjV8HPZD1w7rynNQX40FtLeRjMBXMAIAAAAACoE4BozVfq23FC5PILDNQqG284A0ogt7qOTd9RMtWyVgVsACAAAAAAWgzO7I0Sgkhf4Qs9OI0oNhlv49YhJuCT3VcYSAMCVFAFcAAzAAAAADagFBbIV/Duye4cpyWOB+Exrs11XvnywQlgzXqcywex2/UcF4iV0t5+wTfyAT1+x81PWAAEcwAFAAAAAAR1AA8IAAADMAC4AAAABWQAIAAAAADBOr99J8ZdEJs3Q2oTW6huN3BM3k1Ii83y6rQoqrsWTAVzACAAAAAAJ2idfKvrSH2CQGYdipk8t/z4JIDADr0j3fZ66uKyFbcFbAAgAAAAAHdM0bMtbpFhjTfwACjOmkD9zfYJe8ZR0U2XgAL5dhNZBXAAMwAAAABHJXIDMnB6mhydnD8MveMTTE2Jqu+5IZKBCMKBcQHCZQtLQW8pSE/y7M8H0PC9RLPF7HMAAzEAuAAAAAVkACAAAAAA0DW7/GWHJ2g/2EGfbnHAkzAdRC79qoR8UbBQHVHV04MFcwAgAAAAAOp/6qCfmlqH9DW7m64CPi2FBDEZW5tsnRYifgV3zUO6BWwAIAAAAABaw8Bf04y+OgnQHJ17nK074J5+zLIYd6pXY1wOyn+8sgVwADMAAAAA/ye63aqMCKRrL61dzl/Kr+0OUkD+q4AAoKZzLHxdqKXKfmJdO7d7nSF32DfNU3tm8IQGAAMyALgAAAAFZAAgAAAAABXufbjoS/ORDFF7rBO0ZNGPvU3WyinRSc2uaXw297s2BXMAIAAAAAD0L9zk5Ul/jBVcBWE81X26fDZZaNMLc1DdF5wGaBFqlwVsACAAAAAAFK8lnQCIRs6zUFqsPSPwW3xPn9yvwtDi+sDgpcVrEnQFcAAzAAAAAHXD2f/kXDa5AtnJbu5DSSjzIZA8Xf0YMixbsSSr6DGNmHVHG1ASyoNv/dtoqsaocBGF/gADMwC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzQAuAAAAAVkACAAAAAAb68eGlPfIV11Jx1y9ZAbBWK7zLMTo1EpJOhtSx7CB8QFcwAgAAAAADIwgsGPbY4vwDSun78baoLZi+P5d2nF7kQ75/vf3L3ZBWwAIAAAAAD8zBpULgUVnDObcZ7COraS/z6svZ17BWyenVbqWNikSwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxohKijFQqY0oHD+zILfjGT/DRUVk3pfBoa987N2z6GiZ8xpLAAM1ALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwADNgC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzcAuAAAAAVkACAAAAAAb68eGlPfIV11Jx1y9ZAbBWK7zLMTo1EpJOhtSx7CB8QFcwAgAAAAADIwgsGPbY4vwDSun78baoLZi+P5d2nF7kQ75/vf3L3ZBWwAIAAAAAD8zBpULgUVnDObcZ7COraS/z6svZ17BWyenVbqWNikSwVwADMAAAAATnaU6Vq5RrV84rdnH/wCxohKijFQqY0oHD+zILfjGT/DRUVk3pfBoa987N2z6GiZ8xpLAAM4ALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwADOQC4AAAABWQAIAAAAABvrx4aU98hXXUnHXL1kBsFYrvMsxOjUSkk6G1LHsIHxAVzACAAAAAAMjCCwY9tji/ANK6fvxtqgtmL4/l3acXuRDvn+9/cvdkFbAAgAAAAAPzMGlQuBRWcM5txnsI6tpL/Pqy9nXsFbJ6dVupY2KRLBXAAMwAAAABOdpTpWrlGtXzit2cf/ALGiEqKMVCpjSgcP7Mgt+MZP8NFRWTel8Ghr3zs3bPoaJnzGksAAzEwALgAAAAFZAAgAAAAAG+vHhpT3yFddScdcvWQGwViu8yzE6NRKSTobUsewgfEBXMAIAAAAAAyMILBj22OL8A0rp+/G2qC2Yvj+Xdpxe5EO+f739y92QVsACAAAAAA/MwaVC4FFZwzm3Gewjq2kv8+rL2dewVsnp1W6ljYpEsFcAAzAAAAAE52lOlauUa1fOK3Zx/8AsaISooxUKmNKBw/syC34xk/w0VFZN6XwaGvfOzds+homfMaSwAABHAABQAAAAAAAA==",
            "subType": "06"
        }
    }
}libmongocrypt-1.19.0/test/data/fle2-find-equality-v2/000077500000000000000000000000001521103432300223025ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-equality-v2/cmd.json000066400000000000000000000001001521103432300237270ustar00rootroot00000000000000{
   "find": "test",
   "filter": {
      "value": 123456
   }
}libmongocrypt-1.19.0/test/data/fle2-find-equality-v2/encrypted-field-map.json000066400000000000000000000010741521103432300270300ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "fle2.test.esc",
        "ecocCollection": "fle2.test.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "value",
                "bsonType": "int32",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-equality-v2/encrypted-payload.json000066400000000000000000000022071521103432300266220ustar00rootroot00000000000000{
   "documents": [
      {
         "_id": 1,
         "value": {
            "$binary": {
               "base64": "DIkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSY20AAAAAAAAAAAAA",
               "subType": "06"
            }
         }
      }
   ],
   "find": "test",
   "$db": "test",
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
             "escCollection": "fle2.test.esc",
             "ecocCollection": "fle2.test.ecoc",
             "fields": [
                 {
                     "keyId": {
                         "$binary": {
                             "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                             "subType": "04"
                         }
                     },
                     "path": "value",
                     "bsonType": "int32",
                     "queries": {
                         "queryType": "equality",
                         "contention": 0
                     }
                 }
             ]
         }
     }
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-equality-v2/mongocryptd-reply.json000066400000000000000000000007461521103432300267020ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "documents": [{
            "_id": 1,
            "value": {
                "$binary": {
                    "base64": "A1gAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASEHYAQOIBABJjbQAAAAAAAAAAAAA=",
                    "subType": "06"
                }
            }
        }],
        "find": "test",
        "$db": "test"
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-find-explicit/000077500000000000000000000000001521103432300217415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-explicit/cmd-to-mongocryptd.json000066400000000000000000000012421521103432300263610ustar00rootroot00000000000000{
    "find": "coll",
    "filter": {
        "encryptedIndexed": {
            "$binary": {
                "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.coll": {
                "fields": [],
                "escCollection": "enxcol_.coll.esc",
                "ecocCollection": "enxcol_.coll.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-explicit/cmd-to-mongod.json000066400000000000000000000012421521103432300252770ustar00rootroot00000000000000{
    "find": "coll",
    "filter": {
        "encryptedIndexed": {
            "$binary": {
                "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.coll": {
                "fields": [],
                "escCollection": "enxcol_.coll.esc",
                "ecocCollection": "enxcol_.coll.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-explicit/cmd.json000066400000000000000000000005631521103432300234030ustar00rootroot00000000000000{
    "find": "coll",
    "filter": {
        "encryptedIndexed": {
            "$binary": {
                "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA",
                "subType": "06"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-explicit/reply-from-mongocryptd.json000066400000000000000000000014641521103432300273000ustar00rootroot00000000000000{
    "result": {
        "find": "coll",
        "filter": {
            "encryptedIndexed": {
                "$binary": {
                    "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA",
                    "subType": "06"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.coll": {
                    "fields": [],
                    "escCollection": "enxcol_.coll.esc",
                    "ecocCollection": "enxcol_.coll.ecoc"
                }
            }
        }
    },
    "hasEncryptionPlaceholders": false
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/000077500000000000000000000000001521103432300230335ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double-precision/000077500000000000000000000000001521103432300262765ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double-precision/encrypted-payload-v2.json000066400000000000000000000050221521103432300331410ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DZEFAAADcGF5bG9hZAAZBQAABGcABQUAAAMwAH0AAAAFZAAgAAAAAA5O5YOmDnNEzt4VzOdPJoBsorBvi4P38NSXFCQXBT7lBXMAIAAAAAAlCbJrLprhv7PZAWfpe1J77kmxxrTm5d+VIAzQ1t0f9wVsACAAAAAAJsEPn/IWj5o4yK+N8amVX9KXnsELZG2J1c6Wa2AfBE4AAzEAfQAAAAVkACAAAAAAwVdX7uREzGJG5YAlsRvkOq5eddbRwIMXoUyBxtTOWyMFcwAgAAAAAH7tQpElOGJxNW3nBA/KvOwRjEttzL4Yj7QxAKdseG83BWwAIAAAAAD3pf9mr2hOP/do52J6tCm1L6EW8dcAnjl1AX56PsqCrwADMgB9AAAABWQAIAAAAAB/r0rye8dC3m4mih9d9oBcoQ6Zz99MQLXO5RvpPQ0CxAVzACAAAAAAyAOYAbhDJ52goQxGqSffPOA68Z5ST6MHehKnzrW4NxgFbAAgAAAAABKOFQ4VoYzcavMdAgvpAc9SMLWpJAI/XabuetDojHI3AAMzAH0AAAAFZAAgAAAAAO2Hv6Qd4XbjLmDbLe2/dufbhh+Bc1myy7VrNWQswguHBXMAIAAAAACskbeGL2j0dhv3JUYGLto/8kBwO/CS6CSy2Fz01Qd/oAVsACAAAAAAQD5HxofvNYPA/WXGFJBJ7Aqn4TTxvNEpsZX7QIi8NQEAAzQAfQAAAAVkACAAAAAAtTHP+RbNcKHQQitmLFjax5jU3lSvUFioevRqvngmVrgFcwAgAAAAAJ1FzPG89m3duUQlzzhr10u2bHkrcVrXSqTdRTQRwbuIBWwAIAAAAACDwj7HWLNQncRny0bvqljLDfvtYmGov28d2B/JEHf9sAADNQB9AAAABWQAIAAAAACx7BgZhTiIgBbhrGj1nd8jicXeBh2kUEYz4icmc9ujpAVzACAAAAAAWAUKWWlejF5rygbpZmfnmFlKKi605/AAgxC6jhE+MdgFbAAgAAAAABaV7W7xzxS0nb1Ai7Cfelb3p8bNwTf2fgT/PmQghd9GAAM2AH0AAAAFZAAgAAAAAEyLWJxrA6BmuzegYLP4kUmvxoT1dvr7RttUcJ9+VL81BXMAIAAAAAAUA36oeKJKKyAvckEDg3qz6JDJmyWJzL/PXzUYhrJMUQVsACAAAAAADjxtljJ8f/mlqv2w/jCWJo1UmAzwqtcgQhiqkWBiOQoAAzcAfQAAAAVkACAAAAAAxwM1EBD2yYprO6DgSL/hf7nNswGChKMzNRrdcVAX0QEFcwAgAAAAAPq9JZtWNg4/ZGHjkzpMQx0mwyp0WQQE8lp0l33dC1dzBWwAIAAAAAC42oIAFJNPW5m2erwYVSZPmhQoKD7nxjvqZNuj+lFrhAADOAB9AAAABWQAIAAAAADgeS5YwHbBJYHjnE9rdIFOXCWa4MZgksnq9BCKEBNCQgVzACAAAAAA0ShFMpOeEFx3gMLQEIMhvlkYQJ6DXk/ryqYFXSt0oMAFbAAgAAAAAA4vTg5+jjmTJUB+xkO8YEnaA7fwOph35M9WSK26hQ3nAAM5AH0AAAAFZAAgAAAAAKOQKnHWdirTeER0VaLHcH9Gam5QOhil1vpk11GqLVYsBXMAIAAAAACvxg5olIv89aG/3AhBg4yw2ZJqPMYppPJ8eY1dCIlbIAVsACAAAAAAYmeB3mfKkJlkTw9gbIl2aHKs9G+jxagWBwjubQOomhAAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAQAAAAAAAAAQcG4AAgAAABB0ZgAGAAAAAW1uAAAAAAAAAAAAAW14AAAAAAAAAGlAAA==",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double-precision/rangeopts.json000066400000000000000000000003231521103432300311710ustar00rootroot00000000000000{
    "min": {
        "$numberDouble": "0.0"
    },
    "max": {
        "$numberDouble": "200.0"
    },
    "sparsity": {
        "$numberLong": "1"
    },
    "precision": {
        "$numberInt": "2"
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double-precision/value-to-encrypt.json000066400000000000000000000006141521103432300324100ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberDouble": "123.456"
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$numberDouble": "130.0"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double/000077500000000000000000000000001521103432300243055ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double/encrypted-payload-v2.json000066400000000000000000000102761521103432300311570ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DZMKAAADcGF5bG9hZAAjCgAABGcADwoAAAMwAH0AAAAFZAAgAAAAAKjyYbddQjM3unhlf2quKIb351iO6gUouYlPwZEbRRIvBXMAIAAAAACcNOimZxgC3MriUQZbaB5aB7fOk/csood/dR9v1DZwagVsACAAAAAASAzDUk2yI9Ra119jVNoFHB7mPwu/0oorac3CuvicPYsAAzEAfQAAAAVkACAAAAAAFpbm1UI6g17tS9XfxfiIoQKpeait/v0c24ZdJCQ9YHgFcwAgAAAAAMIqaYbV0SchLBxNVqxGE1abwaPsH2x6JWrTow3iPocYBWwAIAAAAAA6q4R+N3QhcLoKHLePHI8/YDnOHUldNwLRWtinsBPloQADMgB9AAAABWQAIAAAAADl9z37bSa8ku8FizKteHXa0nhjuJb3cW7/JzhE7YJpvgVzACAAAAAAA7JVTRajm4a3JJXW1yMKNbN143mLg262zznf+HK+RGgFbAAgAAAAAFauP6uRimjEB8D3ckVV42bnvfwmFhL2aR14PoUEE9XMAAMzAH0AAAAFZAAgAAAAAAqb/AUDlZJLgswHri9bpxQNsmaRdt/wr/lSwgpSkAC2BXMAIAAAAABMPrNEf/hlt6codWN/FS4vMj56Pn8Wbu/3CxHVv489RAVsACAAAAAAjh/gNmHsfT3gO8aQChZoOoFASwsdoyQC7DH1Hdhfa0AAAzQAfQAAAAVkACAAAAAAQEvEOdf3Zq01VPZmgdo4esmzCn4z+ek23++yY/fxO74FcwAgAAAAAK5mdkvy8gJfXgvLHHwSH3t4NUbR7Si4yloXSkxadxs5BWwAIAAAAADUnl5e38sHh6iBtmXNr0FxHOf+n2TOO/a/j4lvrgVzeQADNQB9AAAABWQAIAAAAABQ4AGfQopOuDX4DyMRad03MiknwSj0hSSRd7QX2EEyDwVzACAAAAAAJ1cCUgDsUz8HKA8f9CaRAsp6jDI0lMhyDRMeU6atI7UFbAAgAAAAAG0LCkWYRoVoHyV/QvyeNFJ7U9RxTCLilJ9psxal+NVsAAM2AH0AAAAFZAAgAAAAADX6syUAnSa9FHTtSFAJul97Dg6re+caP7VBb4LJ849XBXMAIAAAAACqAfrbJNrpdiTOqhFb4Vo1p+sstWDEBVm0lJSVbzhY0AVsACAAAAAAMfa9ZDial3MgafkOWUBTbW/kE+DOBf6qv42Il3x+J84AAzcAfQAAAAVkACAAAAAAkL1is/V49itFRCxU5y2AS03aFRJooLgnCWasB1YBayIFcwAgAAAAAFCpRTpotv8guvvZHZoPaxImoEmZ8TQN0nGBltBiKex5BWwAIAAAAADP+MIa8zyqRDPOU7lqLjGO/pig3+HAohoWGYBb6Qm58gADOAB9AAAABWQAIAAAAAAwYH++XsPut4t7WGvRILU/KpmG5/EIgquCROSlO8/6kQVzACAAAAAAW08l2xPMm3nIkrJV1h+6PZXknO2TiSE9nVfISZPMz74FbAAgAAAAAJNdm+UNcj2Y2rMlbJqppANP2vmydp86I1fAct4p6UMMAAM5AH0AAAAFZAAgAAAAAIMiv50gCX3g5gyZgV20O1DICwbROm/Nr0ysP/U/baiJBXMAIAAAAACsdkuxrEGu5o0sb4bpKjZ0zcwYf4vc5bqbxQT7IJMgyAVsACAAAAAA9dBDGeA4vF6sUp73LJDojzRlhdU9xslDP2ijKTPwYqQAAzEwAH0AAAAFZAAgAAAAAHO0Kr5J2pI2jkWh699+mD9sY84G+IMpToBzuwGCGxokBXMAIAAAAABBSJQXgm7HFvIWmg3tQZqJkRDcRDFdeMipxeK5u4VHLAVsACAAAAAAZPF+xetVVnky41O+lXHU2Z+OsHn3kWpwlpIgH0FBpGgAAzExAH0AAAAFZAAgAAAAAFjL68XhdEfTPdwIAdG1qBga1Y4yutqnHZVyxxpbX2O1BXMAIAAAAABV2u1hZNVDCLUoNJryqKeC3e5nyGewuJUSJSghZvivlgVsACAAAAAAl9dPSHv/HUMnqkX8pdxxgtZFBY1lupYOg/QVNN/noPIAAzEyAH0AAAAFZAAgAAAAANR4bVfACRREURXbp96tNAn+3WU3YsIZQIZVqJpl+8b9BXMAIAAAAADBv2UvqRD6wGwbId0ol35fgyvI94ERtCZX/QFKUa5ABQVsACAAAAAA5W8cYjudCrcM7U+9zzEJ7cXTNRVsAa+gILheWktK1tkAAzEzAH0AAAAFZAAgAAAAADoF20NQtyrvwwClRPm2clA1Y9E9naEqU9auyPUkUdBwBXMAIAAAAADi1Lk6jGXj/jG0WJUEIOEWsdok0xp2BiDDtBurxHZQ6wVsACAAAAAARd07c4ojB7ngK8yGoXvUYTQk7NZHvK119x69QurmaQ8AAzE0AH0AAAAFZAAgAAAAALaE09ewNg6CKPx9CTJxvXSJFXrWnx+Y8MRFDx0RbycoBXMAIAAAAABcgY6uTuOYfIYndYLgJHkN4KprImZdouvhhv4JGv9AEQVsACAAAAAA71DouqMsMNtehvVaDsT5tEbZel5YViuKP3zcaV1Rr2oAAzE1AH0AAAAFZAAgAAAAAHzqlVzP3vapF4qQIPpjdopGV4ItxjKuE41/vUGNrC+LBXMAIAAAAABq3xrm2yy4m6JLkV4XwItK6yY2fLfQTYrjn6K8fmL7awVsACAAAAAAi/XddAk+F2g6PKScPVchHJpbjJup5K8WkEmoJ0ZvAcsAAzE2AH0AAAAFZAAgAAAAAJpglO5I9J9OgUmEb7cO9QpyAcEIpWWkpxae+SV0iMyOBXMAIAAAAADIFoPbs61iLkuY6Ye44trcQB9BYIwZeu8/2DlEnzph2wVsACAAAAAAJPx9VvASbG2yKe7Bk02l3jStxdckXzGAs8VylkHBUkQAAzE3AH0AAAAFZAAgAAAAAPpIg6THnglKjr2IcFqza0aQGMIWWvTv3RBFH0z8Pz2tBXMAIAAAAACXgVfeCWY7MksU4nKUS5gHYMbNsJ7GcGndM/Dz9/c/ngVsACAAAAAA/b9LBSTUXRJXLh4bueQaRtYSJ9xzqLhq0Nk1PokNhCkAAzE4AH0AAAAFZAAgAAAAABetpMcGVSig0WWocP4xTDOW4BLowBgDNxKKR9w+HK1kBXMAIAAAAADlix9y3sfL4jnU5AnZlNFn6KfH0iT8SH8eh4Qn6CBrKwVsACAAAAAAwsUIiS1aqDaR+cnDsjtXdlJiuuxB3DrEiw4CEIVOqeAAAzE5AH0AAAAFZAAgAAAAANg0Cm+7k0RwY1XtTCSTemZ57SLJL+w0ZBcopiTUn0gYBXMAIAAAAADLoG6/csHYF0MGOAmJE0sBuVlOo5i2o4qa5EymICO1DgVsACAAAAAALkIW+qb8TE9ju+D28k+eM7hOA/kw6FHmcxkmniISITEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAQAAAAAAAAAQdGYABgAAAAFtbgD////////v/wFteAD////////vfwA=",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double/rangeopts.json000066400000000000000000000000671521103432300272050ustar00rootroot00000000000000{
    "sparsity": {
        "$numberLong": "1"
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/double/value-to-encrypt.json000066400000000000000000000006141521103432300304170ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberDouble": "123.456"
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$numberDouble": "130.0"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32-openinterval/000077500000000000000000000000001521103432300264765ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32-openinterval/encrypted-payload-v2.json000066400000000000000000000202611521103432300333430ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DZEXAAADcGF5bG9hZAA9FwAABGcAKRcAAAMwAH0AAAAFZAAgAAAAAInd0noBhIiJMv8QTjcfgRqnnVhxRJRRACLfvgT+CTR/BXMAIAAAAADm0EjqF/T4EmR6Dw6NaPLrL0OuzS4AFvm90czFluAAygVsACAAAAAA5MXcYWjYlzhPFUDebBEa17B5z2bupmaW9uCdtLjc7RkAAzEAfQAAAAVkACAAAAAALyvwdd//Hc4qzpSaMFwsLHWOHVnvmw554fbJ47tYpY0FcwAgAAAAAG8rFusBCnyJnbwXJ+8aQ1WM+XBbFCF1rUmcXAZxQWkkBWwAIAAAAACN9YgQN69DpjbYlqV5j+d3Wxw0i4Xyv3a4l8MBSS7qAQADMgB9AAAABWQAIAAAAADLNQvw103YajKpjkNf6J2NdUtiwHFqSefCh5ZWTFHn5gVzACAAAAAAEhDT0LHoNKXVVHkV/TK03iRGiWnHip/oRoYSFvsAeM0FbAAgAAAAADyzWOWBxsAJkQm0lUsW+iHa+ecwl3xr1GAoKxnQQ8XZAAMzAH0AAAAFZAAgAAAAAJwxZVNtkLijHlV2NZ5gouB/igGOk+VtNMCS5yb0k8rTBXMAIAAAAADe0jDDhlPK2TEWxQrIltzfPBDbDsOGQ6po29f2DymLeAVsACAAAAAAaz1dHdDUmnFX4c9bw/Ya8K4IEXlsZnwqXXMerNlx3n8AAzQAfQAAAAVkACAAAAAAzLMRHuYrFaIX6s0la/CsFDdX+FLJmq4d+OS4pNaDiHkFcwAgAAAAAI5nPCVcpanyJ5Hw9/EOgvZhPKohf6BwhyD/s36DDpPOBWwAIAAAAADFJ6mi7lUuNU4g26wwKpBBf1IxllcjYwGVmgAzjvL7WgADNQB9AAAABWQAIAAAAABW8jusc2cKBOhX3EN3Vh9zDDmvZ9r1CcPW6/Zst2uvlQVzACAAAAAAsG53OiiEQrB3w56ZHLMQrUenu22geMONvnwwCj3nAlQFbAAgAAAAAFtAw2gWo2uZAaP18WoDW0ZZ5sR/UF6/IfugiQ7b/N1BAAM2AH0AAAAFZAAgAAAAAKRc8jZ28SV7AuuK0MeWqgMggyLdPuxF3/rlf3X0mU13BXMAIAAAAACo282zLo9WoQNFzsuXf3IMIAhd3t6QflAVHYTsnr2IbwVsACAAAAAACuYunwoc3oRvO9SFMurwpWwQPvTapRBKgMKsJ/wWT6kAAzcAfQAAAAVkACAAAAAAU5SL/FpIkw47miBxzfXPxfZ9gw/yxlN+KF3lx/AxtpEFcwAgAAAAAKLlZgiEi6ld8ceRfMk1w38r675jkpXqLLvzK8LfyG7KBWwAIAAAAAA4GkQC4W8xC4cXc/P2DvVqaykTi+3mBF6KP4FOb6zSwgADOAB9AAAABWQAIAAAAACgLGNpsQsA2Iu7h7E30gI2PsZavxRJD4EPWrCHPZW4EgVzACAAAAAAp6BqiuTFbfaN7+8oSbzI7+DzakDXHpAu+C0/iaHigl0FbAAgAAAAAC9gHbbBvZ7Ux1HjJQ9OzjXFkIdQqf3wIlfd/Sem+6GkAAM5AH0AAAAFZAAgAAAAADdHEzvletK/3vJVX0K2MT8/riWZoBT9Hw/BnkLwV7ydBXMAIAAAAAD6hDlrDU+L+Z4LsthxfKe1+h47TxkPC8qrWMUpSQkD/wVsACAAAAAAn7ChjAU+/nS4NQbvqDOOW6biykdHb79wBhfKDHpZb2UAAzEwAH0AAAAFZAAgAAAAALrHX25CnEqzurp+XAnu/Q2H2gYKI35hakERcBhFnsi6BXMAIAAAAAA7Ap5i5dYKTGR1n+ohsaqk0dBw+Fc+ia6QrJfX9ETExQVsACAAAAAA7M68ZVXqSP9Zo/URE/em80cewk/+1tl4YrFaKyB6B/wAAzExAH0AAAAFZAAgAAAAAFTwiX1CgfLVPFDkZTeLCjWQIK8goK1NhyT/EAsbveQeBXMAIAAAAADs43EuAeSeygtgmB0RUtQeyExMKpFBaTE2MwpEJ+kN8QVsACAAAAAAmBe0eRnzrh2XZeTS33DpLXMmhC1u5DMYAer773/BjxAAAzEyAH0AAAAFZAAgAAAAAKHMYyDjU3hIO8VXvwW4obI9PgargD1v8Pvhmcq6q9KbBXMAIAAAAAA75VwySrv6vAQHMg8cUTcdERrq3sGoTgNotapCBZbjJgVsACAAAAAAV2IphJJO6INmL88126znYqBw2+UFh9/5QujrqbowBekAAzEzAH0AAAAFZAAgAAAAAIwUb9LxEOqMSy5PbMUvDYe2DXfPkBt2jZNuQ1vWt34sBXMAIAAAAADpug62bsh5X971IFh36ffwcvkZHcFo64nTlmKDTXj9fwVsACAAAAAA5azoCr9lR6Wg4MVq5lFRGTkbGZrXR6dKR509HStZ/PkAAzE0AH0AAAAFZAAgAAAAAMkya0CntLSbnc7YudOLDANZAQsI4j3KxkcpOH2xXZUmBXMAIAAAAAD5Z9BvbWz4CmmopapwAkYpGSKiuKx55p4Kbq7A443SYgVsACAAAAAAYA+raAoXZ9REqYogRABFa1SkZeKEvm9ykAnW+tawuxIAAzE1AH0AAAAFZAAgAAAAAN0tw5q8y+uKeLH9LffX6iv2Ep3zaugh6VqPf5zsMh6QBXMAIAAAAADrBqV+1+nP+AqNdzsYfMIQoOsPB7haFuW9Cz+hVw1kJgVsACAAAAAAgxM1YTC2vP9N4OoLvLdkpYZWwoIbGPrJ2TGPmZ5+zgkAAzE2AH0AAAAFZAAgAAAAAA3hI1bZtn7MvJ8lcxHqiTNQ+zIPaaYRecZSCFYgwLDGBXMAIAAAAABhyyafPQB2ingd2pJ0yG81mxHi8AanHY4SYjTFmpplrQVsACAAAAAAKhrFm+j57U2V8CN/P373kz9MAKI8BVPXy/aN7IK0NxwAAzE3AH0AAAAFZAAgAAAAAH9eJtGtNTRpyP31S81/wWab52d2lDwU923EbGTJ7GJLBXMAIAAAAAAxlCrGb17I/IlgxiCQ1kh30N2JKZ4K4vh5bXwC7W40gQVsACAAAAAAQQqLWF/a3cGKySoFWnBakSjnP2TuSvTLfLyYiqpS29YAAzE4AH0AAAAFZAAgAAAAAJy7m47KdUSO9EVYL6s2NyhH2lQ3wVsuMoRGXrM5Vx0RBXMAIAAAAAA7Wn7Ob9ZJ8EpSwV0BzDjmWO/xxJPrZySQRtYfikH7RAVsACAAAAAAQpwD4qlREqoB1oxzJMUIbf3NcmdmkKwvUxkYadhJA98AAzE5AH0AAAAFZAAgAAAAABlvuMGit34OYm0W4MiiYtZUYth7P6UaRFz5L6+8HrIhBXMAIAAAAAD5cBDm2WQIy5K7UJkTuapRKIThXfxQQ2DgeQqGL8eAZgVsACAAAAAAS7Q2FC01yXniB4j1l4iz8dStzogGaR68tPXjxY73Qj8AAzIwAH0AAAAFZAAgAAAAAM5+ULCS+tZVCY1TcCj67z7LnE7DXmKUuOGPIIlHKiV5BXMAIAAAAABtkKOAjDGfDUXO3mtdR2m5QZ5TZf6v26mBaYXyxCgXgAVsACAAAAAAFs5V1xhAdQ8MtruWZTI1gF4+3bQzW74fsUutMYiJKdIAAzIxAH0AAAAFZAAgAAAAAP1uaQ/z8vBsgYL3zea8rgnLmaftW1V+tVN+rHw8XR6XBXMAIAAAAADVYyQZ3v5GviRcEF4CcRS10oi1h09S+aQv7DQAODsVxgVsACAAAAAAuGbnH6XgQ44XUddIazz6ZUAPSxTremgrVrrOYi99TKIAAzIyAH0AAAAFZAAgAAAAAJdLPk9FRHI96E3H4AmRHTP43Pzk7AGD6F4oILnZ8QmPBXMAIAAAAACl15ntjvOXYNjrlZrwfNxcz2FuFCKa3P69gWFcFAhErAVsACAAAAAABkVIK1VtEM+IiOiZfkEgV7OUCZ235RJOLcPf2fhOAusAAzIzAH0AAAAFZAAgAAAAAAGM9XFjl8FDsh/GRNrk3perVUbKJ6jBgtML8bBWT/6eBXMAIAAAAACp3yIkpGnuNZ7gvTSOyU6v+EHUwLgAKZaWsXR4HDrb1wVsACAAAAAA1AjC/4NYj+SOQhK0HqpzOQ7QmB/8A4lzXj7sUeaw9MgAAzI0AH0AAAAFZAAgAAAAACPVD2lc0ikljZUflhEHpImcvYko2yQRd863EdxyMuO0BXMAIAAAAAA6y5Q1Vj1SJxcQfS9BbMYM872VzX/CFneLwB7Hg0MvXwVsACAAAAAA2JH63oc0aBIJu1uY16i2E7xVXBsUJxlpRBhb6ntsO7wAAzI1AH0AAAAFZAAgAAAAAPD+SrBFZSP3imUmTv7fN0Zqrq4jFOoxt5ypaKic9QlABXMAIAAAAABERhmkOV4sG7SICl7iE231fPIZh7PYljIPOGCVIHGU8AVsACAAAAAAR338GLS+yNeC9SQHRFE9DjJMl9v67RantQO+nBeFRKMAAzI2AH0AAAAFZAAgAAAAABuQOb37Lx9pvf3IaHutVBsQKQQfNW2Bqp6+/0JVRVLFBXMAIAAAAAD4+imhwhhqtbONWar+uPv5jU3potjRut7b2e6nEuyxowVsACAAAAAA01amNmTVC1NSn4fmMRReEhkhVEHuKvxUwqrN/Es7YT8AAzI3AH0AAAAFZAAgAAAAAHWosGoXEUyBxn4mK7AWj+VBdFXSr4Fl6/6crg5So/aMBXMAIAAAAABMr2weo9+MNfxJFfRry1kdyG8anSt7z9CiCn/FLkc45wVsACAAAAAAtOGgUbHYndgKoCwklt9+/twPJsweY8gQWYTa/XpN2jcAAzI4AH0AAAAFZAAgAAAAANvnue8NN2uuzSUnQZkxdWytWooi661JwM/zWIRo8IVhBXMAIAAAAACya+VauoLcpuug/5CDtPg+SFGN8ECR3nJGvgqzcEHuIAVsACAAAAAAtRbQArlfd+KRx/2d7A822EdjrlPZCG8M9y63x7P/v/IAAzI5AH0AAAAFZAAgAAAAAC+CAyPJW+1JeSE0x0agfN4h3+wXSaCaI3lbKjhhdQ0GBXMAIAAAAABSKC9WBhYjtXa1VkSUiCoI/S0iw7hbw/R0iQ+ise5FWAVsACAAAAAAaqKL9s+A8KHV3KRMGjdTiQN3uHRwnZdLTK0CUv3MfwMAAzMwAH0AAAAFZAAgAAAAAFAZlSkLVRZ3EI43EBR2G6xBSriZhicrCOdI/LSCBPV/BXMAIAAAAACrKMevNiPDlRivxlaIiuMwKgL9YCI71gUd126MJr3wEAVsACAAAAAATdCtjO74kKZaP6yxIvVl+P/LXSAXkKNd1jRhB5OZl7oAAzMxAH0AAAAFZAAgAAAAALnYnOfvsJNF93GVqYioDLRgMblW10PW3hv7MjZqmcPIBXMAIAAAAAC/X49D6fChPib8MfSIfuZRH2ZmxbvTuN4VITpAfj/BNwVsACAAAAAAb2LvgDDSRxPyr5fbu73+kmjx9OSVJXtBu3lJXHSdk5UAAzMyAH0AAAAFZAAgAAAAAOnCLTfO4/9CCFtZSGPXo1MM+xsfRmZF8dHZ9S4ggUYiBXMAIAAAAABU5DBvJaOvHVgd0lMxYwMdOyRpz4tUSyJqHdE7lvjmtgVsACAAAAAAWaavGrG5upaVdbHrV/EQ7hDoYMvN0uwcTwGObxm+aa4AAzMzAH0AAAAFZAAgAAAAABH0elS9/TptburB/p8mSwaqop4rpnrEBA2Z+6YrsKmkBXMAIAAAAAA4CLaiqQxdxtHNXmnJ5iyJJfwJOQssotw2Ulv1Bcvv/QVsACAAAAAAMNz3ZZ3UgcKfHkSrDEYC9/WwNMze0SphONjsd/m77yUAAzM0AH0AAAAFZAAgAAAAAJgClKD2PCoYTSNA5+H4Gi2GX0ScFtbm7R3HaxY5uVWHBXMAIAAAAAArkzhgryx7WYqqb4lYX26Zr7MyoZ6mQO5yJElmfHD7RAVsACAAAAAAmbDDyP1OB9EpMV+lBQ9pNKqcf/94hPkFd5o2WdiwAcIAAzM1AH0AAAAFZAAgAAAAAKHsc/1sZFjAVsi3+Ul/e5QfNJh7P4X8z+YmgYIdktyaBXMAIAAAAAClCQUeGV2VJd9jYtIaDLbO+8Zt9KQqZQ69pIHO4vC4oAVsACAAAAAAr/woWm4vkkTBeVE7RQLNZOsT3yzdIPYNlsnMQGP4WJYAAzM2AH0AAAAFZAAgAAAAAFBiBeaDl8xn95BibfDk8e1RhYl/leoX6plmbkmK6IRLBXMAIAAAAABpqD31GA3kB7QfegWX2u1bTdGo9MF0qe1KIH9ACTwu7gVsACAAAAAAKnPKMWuHRN2kYUYnfASXcliwjXNj4ZmFAkBnauOs1HUAAzM3AH0AAAAFZAAgAAAAAN+5q9+pSoOoShOODZwcIzAB1O6KWoPqNvBb5aFvbBJtBXMAIAAAAACgI69qTtOIGsh3dH3xIUbhiepb5WB5/EkMkMqykvv2FwVsACAAAAAAS+v6mTLYdlXCikKNfW8OaZEBTbx6CwDd6AD9AFwdk2sAAzM4AH0AAAAFZAAgAAAAACfcl9bA9yTdCo/TuHwXMS3J/yhLMPQ2iFWuyBcD/e8CBXMAIAAAAAALHzY3S6/uZCK97paVv4W5BwQWFUGgqAoplzFt8HRFwAVsACAAAAAA2KhE7fRajzyj/4Ps5u+jaz9debHBRvkcCh0+gjN/QT8AAzM5AH0AAAAFZAAgAAAAAPpPgPcPdrgvsdzwk0NqN3IRCq4xLc6o+W3p90UaLHLpBXMAIAAAAAAINsGNfRHp1dJ1JucrhRfbW528PhuvTY2Fi+iJO0VabQVsACAAAAAAswzsUY7E1BVxnpy+6BSyOc/nexU4Apcla1LFiWhYAB0AAzQwAH0AAAAFZAAgAAAAAF7Q2braYNEQCqjars+GipAvOIw+KT6X0h4L9CcTKN8wBXMAIAAAAABf9+d/bQjNEkTjrqCrmGAOakfQZRok1UGiR9Yyk8NycQVsACAAAAAAqtfJWYxvb1djP9i+yNfjTlSMyZVUgRBIYluf720PBFIAAzQxAH0AAAAFZAAgAAAAAKKdJ/T3HZAP3wWDXMZiOK0d843RduAWkLZtp+ZBIkkeBXMAIAAAAADyHGTQJcie/vlQiXH/I8RSQwyW4Z0EqFNPvNmMm2ABjQVsACAAAAAA+8nkPEsVT5dVXlbk3xFQ9s455J5uJN9p/043uDTZvxoAAzQyAH0AAAAFZAAgAAAAAM6Fmt/HRU0peUv/16DQqbx80YJ4rsyNJQOomEVsQ/3ABXMAIAAAAAB8dI+Gh1ic52aiLaYk+R9LUkqqPTmRP8nBpFWqGGqQPwVsACAAAAAAS5C1QEvGC+DNZX4vSZip02oVwt5xUp6N/qKXiSWpn8wAAzQzAH0AAAAFZAAgAAAAAEleECsYwTk9dhA5DoqbmBczfXM3+THEsRgtnm9rzWoUBXMAIAAAAADqh1hdyqmq0rP1JNCP6LefG6ccZTBxAfMW1+yze5X3ggVsACAAAAAAjQTlXLBgKdiqPbdz222gCrGEMLZAkBUIppvtnwHekSgAAzQ0AH0AAAAFZAAgAAAAAB1AYlGTTg36FM5ITHG/+5+TwUQH9bn4RMXcKZ7+/K1lBXMAIAAAAAATmhhIxpiT+z75fGfXJy5XMtVS0kCsDyxgp5+jZs922AVsACAAAAAAh5p+paQzn2zFBvMJw090AoX9pLCpBGFBb3rrTjecYtoAAzQ1AH0AAAAFZAAgAAAAAEKk7WGoX0IXnKUDtmjVP3J8cOIatQEPrEoyH2APyETuBXMAIAAAAAAOQ2aEYoMGhGhkY0E1hPctbPmpdQ/A2ZKJ82Gv6Ec4agVsACAAAAAA0ELd3kjmsgfzWoMWR+RL3vCIlu7NEE6+GLkWN3FoM5AAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAAQbW4AAAAAABBteADIAAAAAA==",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32-openinterval/rangeopts.json000066400000000000000000000002251521103432300313720ustar00rootroot00000000000000{
    "min": {
        "$numberInt": "0"
    },
    "max": {
        "$numberInt": "200"
    },
    "sparsity": {
        "$numberLong": "1"
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32-openinterval/value-to-encrypt.json000066400000000000000000000003271521103432300326110ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberInt": "23"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32/000077500000000000000000000000001521103432300237725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32/encrypted-payload-v2.json000066400000000000000000000027721521103432300306460ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DYECAAADcGF5bG9hZAAZAgAABGcABQIAAAMwAH0AAAAFZAAgAAAAAInd0noBhIiJMv8QTjcfgRqnnVhxRJRRACLfvgT+CTR/BXMAIAAAAADm0EjqF/T4EmR6Dw6NaPLrL0OuzS4AFvm90czFluAAygVsACAAAAAA5MXcYWjYlzhPFUDebBEa17B5z2bupmaW9uCdtLjc7RkAAzEAfQAAAAVkACAAAAAALyvwdd//Hc4qzpSaMFwsLHWOHVnvmw554fbJ47tYpY0FcwAgAAAAAG8rFusBCnyJnbwXJ+8aQ1WM+XBbFCF1rUmcXAZxQWkkBWwAIAAAAACN9YgQN69DpjbYlqV5j+d3Wxw0i4Xyv3a4l8MBSS7qAQADMgB9AAAABWQAIAAAAADLNQvw103YajKpjkNf6J2NdUtiwHFqSefCh5ZWTFHn5gVzACAAAAAAEhDT0LHoNKXVVHkV/TK03iRGiWnHip/oRoYSFvsAeM0FbAAgAAAAADyzWOWBxsAJkQm0lUsW+iHa+ecwl3xr1GAoKxnQQ8XZAAMzAH0AAAAFZAAgAAAAAJwxZVNtkLijHlV2NZ5gouB/igGOk+VtNMCS5yb0k8rTBXMAIAAAAADe0jDDhlPK2TEWxQrIltzfPBDbDsOGQ6po29f2DymLeAVsACAAAAAAaz1dHdDUmnFX4c9bw/Ya8K4IEXlsZnwqXXMerNlx3n8AABJjbQAEAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAAAAEG14AMgAAAAA",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32/rangeopts.json000066400000000000000000000002251521103432300266660ustar00rootroot00000000000000{
    "min": {
        "$numberInt": "0"
    },
    "max": {
        "$numberInt": "200"
    },
    "sparsity": {
        "$numberLong": "1"
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range-explicit/int32/value-to-encrypt.json000066400000000000000000000005761521103432300301130ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberInt": "23"
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$numberInt": "35"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/000077500000000000000000000000001521103432300212145ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/README.md000066400000000000000000000003551521103432300224760ustar00rootroot00000000000000The expected payloads in fle2-insert-range were obtained by printing data from the Range_Allowed_Types test:
https://github.com/mongodb/mongo/blob/ad4a7ae485fd0189542e59b3350e798b42b42d1b/src/mongo/crypto/fle_crypto_test.cpp#L1003-L1021
libmongocrypt-1.19.0/test/data/fle2-find-range/date-v2/000077500000000000000000000000001521103432300224565ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/date-v2/cmd.json000066400000000000000000000002331521103432300241120ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$date": {
                "$numberLong": "12345"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/date-v2/encrypted-field-map.json000066400000000000000000000011741521103432300272050ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "date",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/date-v2/encrypted-payload.json000066400000000000000000000261361521103432300270050ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DaUdAAADcGF5bG9hZABJHQAABGcANR0AAAMwAH0AAAAFZAAgAAAAABB+TYwDkWz8ouVl4Q4oIBElQKgGEDqMjPPSv+TZS8YABXMAIAAAAAAMLfd8ZW9xVgL8jqH0+wwdhD/Ktnb5fhh2ZM0iAVmS3QVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwAAzEAfQAAAAVkACAAAAAAffWVv+ysFys5eTN10LACdyWxHn/dCKMRWAQKzYmM5YEFcwAgAAAAAGDv6msNPvdhwK8sTlBu4iuoZyMtzmlAPYyKrR3b3KvGBWwAIAAAAAB61GI/ANIVYHOlZJaQ4pYzp8pK1EnxLI5uelzv27HynQADMgB9AAAABWQAIAAAAADwKL3PTbIxWwM3Ie8tK0sAdHbks7bZQ7k/+BtQb8hK1AVzACAAAAAA518WFoVWrUk+vd0h4ubURgpaBx5nuFnkUlPm7+Uv8j4FbAAgAAAAAOpTJTWFIUMsKaqzPZ2E+F5/MJKFRX9nbH7g/3FwbDyvAAMzAH0AAAAFZAAgAAAAAPeDZq0JZhU5dbRhsJu8i26uo0gCzLbXRuQv0qapUyUABXMAIAAAAABUYRvsvRAKNX88FMC9TKGV12jjrtOgqOUx3Aj2GyO4TgVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0AAzQAfQAAAAVkACAAAAAA1A4pQT0U6YKgy9rYhiDjktlclfRwylRHU68jAWaMsNgFcwAgAAAAAHp3IgqRDzqX/yPzN5ZwNh5UZFgp3RRLPpOjcts4EAqwBWwAIAAAAAAQsrQTEjSDj9bE2z6Z/5Rr6aJ7cy4w7d1o8a6QDbdVRgADNQB9AAAABWQAIAAAAABFCn58YDTy/VuFydht+w/S5teX3KDXxCZgx+Hax3wR0QVzACAAAAAAGF7v3ATOcTb3y23TbHJEXtuTtrY0f2TBWDVM9jn+VPUFbAAgAAAAAAOIZXl/9FE+TClKtkwYc0FJqubDKpX5JhcjvdBb8+afAAM2AH0AAAAFZAAgAAAAAGdbcKmZIVOCU80pgckeEkcL+93kExZCnO0hdAEvSwKeBXMAIAAAAAALbWpEG7Y/HtVTPWVb3kN+f/S3DbKr0BUG2RrfgeKMuAVsACAAAAAA/xLWzk4DlwlPogukD3gtqe4uxKX0GxEJYcG258VMxWsAAzcAfQAAAAVkACAAAAAAlEha7oMjYtH7Y+LT05oFkSZLYUifIGcaz3B/0l/bCIgFcwAgAAAAAGgOorOPXWrI59eXh1x+SYYepnFfICycaWAD3CwlFZZRBWwAIAAAAAAiwOi6zKj8Px7gELBSq8WGsq06elwYJKqk33igW1ZoLQADOAB9AAAABWQAIAAAAABNIe733XIhQXqiJNTdeGV8P48xU9Gy313Dcoj0bIOFuAVzACAAAAAAdCm75t1SwaYbXfPzqYDbibSWiSS4RwQimSSXivhnLYQFbAAgAAAAACn4x6fMcMNSQ488o0532pjr4xQ3NWdx6f7/2bkJxTjhAAM5AH0AAAAFZAAgAAAAACL9+rQRyywIXa5Pr7g2SnB0s0EjIct7PQtzjEkA69acBXMAIAAAAADz54imCCbu/qQkYP9wW2f5pHoBS+EyCe+xuDwC0UTiYgVsACAAAAAAKv602j4c3Bpn2t10qGl68eAD/fQsIH5lKMj8ANwrf7oAAzEwAH0AAAAFZAAgAAAAAKTK0NLhQ/+Y/HMxjRwBlXpXJAhAmCoWf1fReTegPnVpBXMAIAAAAAD7AlW+P4FfQS4r8d7EEvPVEP1diSbrVDBqg8ZvNl1XRAVsACAAAAAATTSEkff+/JMBjNwUciY2RQ6M66uMQMAtwU+UidDv1y4AAzExAH0AAAAFZAAgAAAAAGMbgPxi2Wu1AlqoDKTgyBnCZlnCjHm2naxRcizkIbYJBXMAIAAAAADMvSM3VZzVyRFCfUvcLXAXQFRIxlhm0t0dUsnaRZG4hgVsACAAAAAAI7uGriMAQc4A/a70Yi1Y7IAC7o/mfNYf7/FvwELYf80AAzEyAH0AAAAFZAAgAAAAAPnZ1bdmrcX0fsSxliuSqvDbRqwIiVg0tYp0PViRX0nOBXMAIAAAAAAqBdZGg9O74mnwyQF+lILtyzHdLOErDjPSf9sM8EqCugVsACAAAAAAwhuDsz+fCtqY8mW8QvEVQERjDChwrYTw4y7dinlCCOMAAzEzAH0AAAAFZAAgAAAAAJ40Dmb5BUT1AlWjfXB43nIbJgDn9rBg9FAeYR80WK0vBXMAIAAAAAAMPqLMDdNmnKzA3Hq49/NkJfs+/cjnyjSAbmiOFUE5FgVsACAAAAAAxbi7ql49Y4pduqWlLJqpwimRzrEnC7w5fWaMBiinHL8AAzE0AH0AAAAFZAAgAAAAAGelnhqWM2gUVy4P5QE/2Zfd7s9BugPqB/tcnSsFg5X0BXMAIAAAAAAWUhif3G+NMvZ3YPLB5OMuIhfPEu6U8KR9gTvJFz5uIwVsACAAAAAADEs8/aVSj2sJjxjv1K7o/aH8vZzt1bga73YiIKUx5DYAAzE1AH0AAAAFZAAgAAAAAD1xX2wCyf1aK1MoXnBAPfWLeBxsJI2i06tWbuiYKgElBXMAIAAAAACW1NW4RibvY0JRUzPvCmKnVbEy8AIS70fmsY08WgJOEgVsACAAAAAAQq9eIVoLcd4WxXUC3vub+EnxmcI2uP/yUWr3cz0jv9EAAzE2AH0AAAAFZAAgAAAAAHwU1LYeJmTch640sTu3VRRRdQg4YZ7S9IRfVXWHEWU8BXMAIAAAAACozWKD2YlqbQiBVVwJKptfAVM+R2FPJPtXkxVFAhHNXQVsACAAAAAAn7LS0QzTv9sOJzxH0ZqxsLYBYoArEo/PIXkU/zTnpM0AAzE3AH0AAAAFZAAgAAAAAHKaToAsILpmJyCE02I1iwmF/FibqaOb4b5nteuwOayfBXMAIAAAAABPxYjSK5DKgsdUZrZ+hM6ikejPCUK6Rqa0leoN7KOM0QVsACAAAAAAH9rPq5vvOIe9nTAcM1W1dVhQZ+gSkBohgoWLPcZnQXcAAzE4AH0AAAAFZAAgAAAAANTGiHqJVq28n7mMZsJD6gHxVQp1A6z8wgZVW+xV/lhmBXMAIAAAAABCR4BfdNVy7WE+IyQ312vYuIW0aGcXxr2II/MbNz8ZdAVsACAAAAAAng0GYpYJTypRLQUd5tIXWaAjZX5na04T/BypmwwrXPoAAzE5AH0AAAAFZAAgAAAAABooumzjEqp9Hvvd+sn1L82NI2iUGRl0nXQNJTHM7oyVBXMAIAAAAADgjz5L2ursK4C+pXXsJ6XHABhyallj9s/vSUgxXvjiiwVsACAAAAAAPjlAM0tbO6EUmLAeIZt57YMkMsuQfuC3T3d9vtnxgjwAAzIwAH0AAAAFZAAgAAAAAMA4jmE8U2uGkYUeKoYSlb22tfrRq2VlhV1Jq1kn4hV9BXMAIAAAAADG4fLeJUcINPSb1pMfAASJkuYsgS/59Eq/51mET/Y7RQVsACAAAAAAmwwcWOnzvpxm4pROXOL+BlxjEG/7v7hIautb2ubFT44AAzIxAH0AAAAFZAAgAAAAAK8/E3VHzHM6Kjp39GjFy+ci1IiUG5oxh0W6elV+oiX2BXMAIAAAAAA4/F4Q94xxb2TvZcMcji/DVTFrZlH8BL/HzD86RRmqNAVsACAAAAAAif3HPf6B1dTX/W+Vlp6ohadEQk/GAmHYzXfJia2zHeIAAzIyAH0AAAAFZAAgAAAAAGUX9ttLN1cCrOjlzsl/E6jEzQottNDw8Zo94nbO1133BXMAIAAAAAA7uVthFvXH+pbBrgQmnkPcpiHFEVCAi0WA7sAt9tlt3gVsACAAAAAAznaMStSbtGXU1Pb5z9KDTvEd79s6gmWYCKOKdzeijpEAAzIzAH0AAAAFZAAgAAAAAKnT/qg8N85Q9EQvpH7FBqUooxHFgrIjqLlIDheva2QSBXMAIAAAAABGAKkFMKoSIrvClWF7filoYM6fI9xSqOJVNS3dv4lxYwVsACAAAAAAgITE31hQA4ZOxpUFYSYv0mzWbd/6RKgbUXiUY96fBQEAAzI0AH0AAAAFZAAgAAAAAHRDRDT2hJrJ8X9zB9ELT28q8ZsfkYr92chaZYakiLlqBXMAIAAAAAAT0Le67ObldDta/Qb17dYfdslPsJTfGj3bWAgC0JIingVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoAAzI1AH0AAAAFZAAgAAAAAOOJcUjYOE0KqcYS1yZ363zglQXfr3XSD+R5fWLSivDoBXMAIAAAAABjeLe+tg37lNa+DdVxtlCtY77tV9PqfJ5X4XEKrfwu0AVsACAAAAAAlbpHiQAPLLTvSF+u58RBCLnYQKB5wciIQmANV9bkzsoAAzI2AH0AAAAFZAAgAAAAAMwWOOaWDDYUusdA1nyoaEB3C4/9GRpFNGags95Ddp4LBXMAIAAAAACLrsQXGWK15fW4mPEUXJ/90by13aG+727qWJep8QJ/WgVsACAAAAAAuThwsAsKUB56QAXC0MjJsZ9736atbiHPlK2tE0urf9QAAzI3AH0AAAAFZAAgAAAAABPRXBK0z8UANcvMDWntBjN9yF7iGMPLbhbaKrvHwcplBXMAIAAAAACZlqWsYPIb+ydmH03BxD3TqSGsSNoI7EVCy0VgW0TpYgVsACAAAAAAD2uaBv8oc7l4EeC5PWx5sfeyGZoas0JdFJ33M3jjgjMAAzI4AH0AAAAFZAAgAAAAAOn9/6pbzjIxFEApugaVOvVKXq23sDCJELv5UtLPDZI3BXMAIAAAAACHIwSDTlof0vFoigF4drbeM/8rdlj/4U386zQsNLtPGwVsACAAAAAAsYt/rXnpL55J9rlWSFRA4seaU6ggix7RgxbrJPu6gO4AAzI5AH0AAAAFZAAgAAAAAIMCESykv5b5d6mYjU5DlnO709lOFCaNoJBLtzBIqmg4BXMAIAAAAADs1Bfuaun4Es3nQ4kr29BzheLRDcFv+9a0gOGkSEcrDgVsACAAAAAA5kW6i/jOBSdoGAsZEZxVNRvt6miv86bP8JfUT+1KJg8AAzMwAH0AAAAFZAAgAAAAAFSPmr27XgKhUkbEvvC6Br5K1w7280NZrrhdzfYF+YGjBXMAIAAAAADv2h+Xq6kM7MHYTLMACRwbe2MzGHu4sdB67FGzDR6H4QVsACAAAAAAKII0MMC7o6GKVfGo2qBW/p35NupBp7MI6Gp0zXYwJOcAAzMxAH0AAAAFZAAgAAAAAPSV9qprvlNZK6OSQZNxKhJmBMs6QCKFESB/oeIvAS0iBXMAIAAAAAA835Jh22/pvZgKoYH6KjE+RRpYkaM1G35TWq6uplk/rgVsACAAAAAA162IdSb079yVlS7GkuSdHU3dOw03a+NS55ZPVBxbD08AAzMyAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzMzAH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzM0AH0AAAAFZAAgAAAAAMsF+h3Cqgrzmao79EFlt7XUVDVYxDlOuJp9fd2njUULBXMAIAAAAAD2XTbeYZdu87zL+ANjGEuK9itwm1oHIucqHxLnWhplmgVsACAAAAAAyJgZZ3MyP4IYKT2NtJq8DBkzz9PaxrK9vaR+XTsRyHkAAzM1AH0AAAAFZAAgAAAAAO9plUESIDneHgrY/f9U5lTjbWRSZntI8KlP6TXZF/9UBXMAIAAAAAB9XQ9jWdACGGIZZJiYisZoSPo9Pj3Rvh8oKD4VxtctfAVsACAAAAAA5F1w5HpoC4eHQWRgRJBj//WVTD43hJcGb3sh0bojJOMAAzM2AH0AAAAFZAAgAAAAANoXAlsVp44X14O/0ueeHYCCxsC5JmayKccwWgP28CeaBXMAIAAAAABbUhEE/E5or4+4I7alXpoNyu1xjNVrzCi5Yrwzg8IrbwVsACAAAAAAQDw5ok73ljnvljHGWgBU/oEkLq89nOIMufpAWJXZuTAAAzM3AH0AAAAFZAAgAAAAAKcyk7RbzydoBOTPIFBt6EtTMjVyGsUhx4BagUsRna6aBXMAIAAAAACXafcHWSM/heoFSFGYPn7Gla7ZcMcMfsucP/tDuGhOqAVsACAAAAAAUU6eINEpD/VlrAy2jh+5+baQ9cvclLglDA7w045G0c8AAzM4AH0AAAAFZAAgAAAAAAluaRdNRjUWvKqF8hBzAbi/PQo2X4xFpZtmltnA5WZ6BXMAIAAAAADPTpEz3N5BvgrfWg0cSexVcIT9QBcxXDUGeljLfn7roAVsACAAAAAAG8wIC5cXfbeMZn6jGD6Yy+F4ZbTTCjSsSS/iZM7IzJMAAzM5AH0AAAAFZAAgAAAAAPGEl1VBrxJgj7cgOGjNoOD0xJ0Tq+pvyalhT8/yN3zZBXMAIAAAAAAINdKtKw14MRjU/dycHNXSGRFB7d7PqG9v/P2nfGPkjQVsACAAAAAAq6K5bQZHywUk3XR/0l4qerhz4JTOTQ+bIsVGbSA1Ec0AAzQwAH0AAAAFZAAgAAAAAOaoAaheMsPE4WwsAzI9c6S3cQkXdEqpEFmUpgQF5z4rBXMAIAAAAABQLpIkaUKhTp05ZI8AA/v1oBv8ohmcmm7K+/sjqHMYgwVsACAAAAAADOtEUjUQlvKf6WdIrZleVwma3gjKcLshzajtLP9pXUMAAzQxAH0AAAAFZAAgAAAAAN/vipid0wavMuYm6q0Qej8I+0MZPIO/sysW2fAMMRgtBXMAIAAAAABhgUDVegsnV78ZNmD8Ad+G+L7bhvFs+T+LY3zDTjEchAVsACAAAAAAfazxCcVs9n83k3NINdlC6941pcksLfINz1Xtj5kS9DkAAzQyAH0AAAAFZAAgAAAAAJzIrJiV6IdouJe+T3EVegGE973goofh4QLPb3I4F+cqBXMAIAAAAAB56y67x2z8Pd/3ChjAw69wuFnahdqfyxfnvW7QvOsEKAVsACAAAAAAabBeIk2CWz/j1Yp4M4Q5Js5KMWyP6MID/hd3twPl4IQAAzQzAH0AAAAFZAAgAAAAABZvmCekYSVglzFe5FL+CoV0i5KjmDwvqMHVGApZe5vTBXMAIAAAAAAC91Pt03x+pRD80ZVX/7smx2TyCpnqAy4lVvF3TyspaAVsACAAAAAAALfFZEgQuV6swMxx0OpDfNj3GwhyxBfJr1tM+6CgjhIAAzQ0AH0AAAAFZAAgAAAAANzRchBZFlSsxneCNq4hBVa6QRPrYW+tB8AV+SQ/evNLBXMAIAAAAADMCXCbGCnmG0B8hyy5/vqT5TaA+0eQv9ihkS3uyELDJgVsACAAAAAA4hhse5B6zxYRGh30Dp6fHMWzXjgSx3KnE8bGxNFfM0kAAzQ1AH0AAAAFZAAgAAAAAJlfE4PVSVhiV0MUCkADagHeWmJXo3a3fokzKYIhIVeJBXMAIAAAAACr2IKAxCfGSCun4izvMYY3Y9Fs4wh6dtTmi2bW0DvZ2wVsACAAAAAAjL3zF8sDH7w9Vwqz5coXS26qyC7VwfiHjxU6GZ57Q3oAAzQ2AH0AAAAFZAAgAAAAAARBx5KrGgivFwv1HncHI5PakuJTFlFbQpgx8GLDoTjQBXMAIAAAAABLs2xM85oCKAFHh8f9a6ZI53LcCd1b4+RwqeCvU3Y8XAVsACAAAAAAftQ+WuNEgdAB8dypZB0wrE27S1lQ6BXavmwWZv6z81YAAzQ3AH0AAAAFZAAgAAAAADa3sc1oraS1k+f5kz0bP/VAI8whsZmKx61wL9H92XicBXMAIAAAAAAnwxVX0gAxYFOoP14PKCtOPDksSfR++d/CjffYBVurdwVsACAAAAAA3DIA+ylb/SNP3HFQV1hPJa5mf4gqucN/z8c5bbPrGTsAAzQ4AH0AAAAFZAAgAAAAAIVM7+M9JwZPQACwNIHkD/ZqlNA2FZQFdXJdo8bfUX4PBXMAIAAAAABpG3yn1N8JwanquFi3vUaLWKjzi1HUSMfIPM/xlCBqnAVsACAAAAAADbOJkKlJj9KHbCszGgiGZFCQsJj8QpV8MskeZThK4rUAAzQ5AH0AAAAFZAAgAAAAAKYYm2QnEs1cBoODhk/EUTIypw+w/Y6f+K1sgpcZDLQmBXMAIAAAAAB+95xrWxV4hFxipmtk8sLCkc1pLMB7vS4j8CNw8/+HOwVsACAAAAAArmDB4qxPUdH3oeyPQhCgcZkPJ2hR7Z6Ek3DieJl0JPoAAzUwAH0AAAAFZAAgAAAAAOBpiNsiKVq1JOV5dWF0I+nUJ2sH9vy0ewv62mnGtLFaBXMAIAAAAABe6yzBO7bUcbs+RzTzLr9BGeY12mLXpCStrMB0urFteAVsACAAAAAAyfxyA8IXLbXWP/56Mc6CH9Ph1mnbLVlsRtkh4ixfde8AAzUxAH0AAAAFZAAgAAAAAP2IBZxLNspoTPMraca0CfKhEocv/NcyyQQq9ZTGoNv/BXMAIAAAAACoq5/5PJ1s1LRwmrtanxViywKp9jERF1mGcGrhjUkX2AVsACAAAAAA03Wle00K7gOjKH5mUoB/s82KWvxoX9JW6hyjHQ1DdQ0AAzUyAH0AAAAFZAAgAAAAALBNlrMGNjhHwk8Lx97PYFZXx7QfDQhxKBYR3kLHr1IyBXMAIAAAAAA2jChQugSYMZBSW7hV5sEL9Hwp6jWd5Sucdwb1ByzS3gVsACAAAAAA2gY7FULKEIh15JMuGnzxqhR9VMIPjrKSdo6BoKydB0gAAzUzAH0AAAAFZAAgAAAAAEnZRVntb1MzQky6eD0K3tCySiGf4FLoyRlW56KnCog7BXMAIAAAAAAd1n7PJbc7BiAU3yvDmy4Ht51tJqE6papne4i/xxlkfQVsACAAAAAAu+it7o9jiENMeE97YjCinWpdRUTYCReVmz8OCvzOD3AAAzU0AH0AAAAFZAAgAAAAAKhexbhP3PeY6EBPmujthzaosaSSZFIMq5HnQwtFWWveBXMAIAAAAADMqCakj5/y4l6RZH7VjFd6cgHeE1NlLyRdCOFpHxGXQQVsACAAAAAA3dVm62UQ9+XpFKBAFI6Z08V+M4ZvYDqxqN/vRhDkiccAAzU1AH0AAAAFZAAgAAAAABnSfdBCATnGexxKQV7swwfxf2nk/wxIfjvffm3k4coFBXMAIAAAAADaPqt/BvDx4Nt6U22nZDYIBGR5jvRR/ba0UkqFYGOscQVsACAAAAAAwt8xn9mDRpjHeVqVGa4gpfZggqGRPEgOKxSiAKcF1v8AAzU2AH0AAAAFZAAgAAAAAPmG04KhEpPRHPRRnfAwWJ5k2wbAjLkqt6yHwMKeV6A2BXMAIAAAAABpgK5RiOaz3M0GgZrDahUXZwGxolwCh9hEp+/G2VI9GwVsACAAAAAAylgs3BiBjUnyOvBl3IK4yQxgBgZm8MBLtleFCKyZ19oAAzU3AH0AAAAFZAAgAAAAAFIxOj92Wi83wPyQ02iWdWdurZwzFndnx5pq3pY1w/4nBXMAIAAAAAASXgOXGFzKOyaetG1s/ZUGyNdW0X+5wRE8ln0fFudoxQVsACAAAAAAu34md3q5knY3mz6krEAsxVzXgHy8EnmWKdEwyIkcBfkAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAAJbW4AAAAAAAAAAAAJbXgAFYHpffQQIhEA",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "date",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/date-v2/mongocryptd-reply.json000066400000000000000000000034661521103432300270600ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "A/wAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAnQAAAANlZGdlc0luZm8AawAAAAlsb3dlckJvdW5kAAAAAAAAAAAACGxiSW5jbHVkZWQAAQl1cHBlckJvdW5kABWB6X30ECIRCHViSW5jbHVkZWQAAQlpbmRleE1pbgAAAAAAAAAAAAlpbmRleE1heAAVgel99BAiEQAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAABJjbQAAAAAAAAAAABJzAAEAAAAAAAAAAA==",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "date",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-precision-v2/000077500000000000000000000000001521103432300253635ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-precision-v2/cmd.json000066400000000000000000000001511521103432300270160ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {"$numberDecimal":"4.56000000000000"}
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-precision-v2/encrypted-field-map.json000066400000000000000000000014331521103432300321100ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "decimal",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               },
               "min": {"$numberDecimal":"0"},
               "max":{"$numberDecimal":"1234567890123456789"},
               "precision":{"$numberInt":"2"}
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-precision-v2/encrypted-payload.json000066400000000000000000000375731521103432300317210ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DdcqAAADcGF5bG9hZABjKgAABGcATyoAAAMwAH0AAAAFZAAgAAAAABB+TYwDkWz8ouVl4Q4oIBElQKgGEDqMjPPSv+TZS8YABXMAIAAAAAAMLfd8ZW9xVgL8jqH0+wwdhD/Ktnb5fhh2ZM0iAVmS3QVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwAAzEAfQAAAAVkACAAAAAAffWVv+ysFys5eTN10LACdyWxHn/dCKMRWAQKzYmM5YEFcwAgAAAAAGDv6msNPvdhwK8sTlBu4iuoZyMtzmlAPYyKrR3b3KvGBWwAIAAAAAB61GI/ANIVYHOlZJaQ4pYzp8pK1EnxLI5uelzv27HynQADMgB9AAAABWQAIAAAAADwKL3PTbIxWwM3Ie8tK0sAdHbks7bZQ7k/+BtQb8hK1AVzACAAAAAA518WFoVWrUk+vd0h4ubURgpaBx5nuFnkUlPm7+Uv8j4FbAAgAAAAAOpTJTWFIUMsKaqzPZ2E+F5/MJKFRX9nbH7g/3FwbDyvAAMzAH0AAAAFZAAgAAAAAPeDZq0JZhU5dbRhsJu8i26uo0gCzLbXRuQv0qapUyUABXMAIAAAAABUYRvsvRAKNX88FMC9TKGV12jjrtOgqOUx3Aj2GyO4TgVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0AAzQAfQAAAAVkACAAAAAA1A4pQT0U6YKgy9rYhiDjktlclfRwylRHU68jAWaMsNgFcwAgAAAAAHp3IgqRDzqX/yPzN5ZwNh5UZFgp3RRLPpOjcts4EAqwBWwAIAAAAAAQsrQTEjSDj9bE2z6Z/5Rr6aJ7cy4w7d1o8a6QDbdVRgADNQB9AAAABWQAIAAAAABFCn58YDTy/VuFydht+w/S5teX3KDXxCZgx+Hax3wR0QVzACAAAAAAGF7v3ATOcTb3y23TbHJEXtuTtrY0f2TBWDVM9jn+VPUFbAAgAAAAAAOIZXl/9FE+TClKtkwYc0FJqubDKpX5JhcjvdBb8+afAAM2AH0AAAAFZAAgAAAAAGdbcKmZIVOCU80pgckeEkcL+93kExZCnO0hdAEvSwKeBXMAIAAAAAALbWpEG7Y/HtVTPWVb3kN+f/S3DbKr0BUG2RrfgeKMuAVsACAAAAAA/xLWzk4DlwlPogukD3gtqe4uxKX0GxEJYcG258VMxWsAAzcAfQAAAAVkACAAAAAAlEha7oMjYtH7Y+LT05oFkSZLYUifIGcaz3B/0l/bCIgFcwAgAAAAAGgOorOPXWrI59eXh1x+SYYepnFfICycaWAD3CwlFZZRBWwAIAAAAAAiwOi6zKj8Px7gELBSq8WGsq06elwYJKqk33igW1ZoLQADOAB9AAAABWQAIAAAAABNIe733XIhQXqiJNTdeGV8P48xU9Gy313Dcoj0bIOFuAVzACAAAAAAdCm75t1SwaYbXfPzqYDbibSWiSS4RwQimSSXivhnLYQFbAAgAAAAACn4x6fMcMNSQ488o0532pjr4xQ3NWdx6f7/2bkJxTjhAAM5AH0AAAAFZAAgAAAAACL9+rQRyywIXa5Pr7g2SnB0s0EjIct7PQtzjEkA69acBXMAIAAAAADz54imCCbu/qQkYP9wW2f5pHoBS+EyCe+xuDwC0UTiYgVsACAAAAAAKv602j4c3Bpn2t10qGl68eAD/fQsIH5lKMj8ANwrf7oAAzEwAH0AAAAFZAAgAAAAAKTK0NLhQ/+Y/HMxjRwBlXpXJAhAmCoWf1fReTegPnVpBXMAIAAAAAD7AlW+P4FfQS4r8d7EEvPVEP1diSbrVDBqg8ZvNl1XRAVsACAAAAAATTSEkff+/JMBjNwUciY2RQ6M66uMQMAtwU+UidDv1y4AAzExAH0AAAAFZAAgAAAAAGMbgPxi2Wu1AlqoDKTgyBnCZlnCjHm2naxRcizkIbYJBXMAIAAAAADMvSM3VZzVyRFCfUvcLXAXQFRIxlhm0t0dUsnaRZG4hgVsACAAAAAAI7uGriMAQc4A/a70Yi1Y7IAC7o/mfNYf7/FvwELYf80AAzEyAH0AAAAFZAAgAAAAAPnZ1bdmrcX0fsSxliuSqvDbRqwIiVg0tYp0PViRX0nOBXMAIAAAAAAqBdZGg9O74mnwyQF+lILtyzHdLOErDjPSf9sM8EqCugVsACAAAAAAwhuDsz+fCtqY8mW8QvEVQERjDChwrYTw4y7dinlCCOMAAzEzAH0AAAAFZAAgAAAAAJ40Dmb5BUT1AlWjfXB43nIbJgDn9rBg9FAeYR80WK0vBXMAIAAAAAAMPqLMDdNmnKzA3Hq49/NkJfs+/cjnyjSAbmiOFUE5FgVsACAAAAAAxbi7ql49Y4pduqWlLJqpwimRzrEnC7w5fWaMBiinHL8AAzE0AH0AAAAFZAAgAAAAAGelnhqWM2gUVy4P5QE/2Zfd7s9BugPqB/tcnSsFg5X0BXMAIAAAAAAWUhif3G+NMvZ3YPLB5OMuIhfPEu6U8KR9gTvJFz5uIwVsACAAAAAADEs8/aVSj2sJjxjv1K7o/aH8vZzt1bga73YiIKUx5DYAAzE1AH0AAAAFZAAgAAAAAD1xX2wCyf1aK1MoXnBAPfWLeBxsJI2i06tWbuiYKgElBXMAIAAAAACW1NW4RibvY0JRUzPvCmKnVbEy8AIS70fmsY08WgJOEgVsACAAAAAAQq9eIVoLcd4WxXUC3vub+EnxmcI2uP/yUWr3cz0jv9EAAzE2AH0AAAAFZAAgAAAAAHwU1LYeJmTch640sTu3VRRRdQg4YZ7S9IRfVXWHEWU8BXMAIAAAAACozWKD2YlqbQiBVVwJKptfAVM+R2FPJPtXkxVFAhHNXQVsACAAAAAAn7LS0QzTv9sOJzxH0ZqxsLYBYoArEo/PIXkU/zTnpM0AAzE3AH0AAAAFZAAgAAAAAHKaToAsILpmJyCE02I1iwmF/FibqaOb4b5nteuwOayfBXMAIAAAAABPxYjSK5DKgsdUZrZ+hM6ikejPCUK6Rqa0leoN7KOM0QVsACAAAAAAH9rPq5vvOIe9nTAcM1W1dVhQZ+gSkBohgoWLPcZnQXcAAzE4AH0AAAAFZAAgAAAAANTGiHqJVq28n7mMZsJD6gHxVQp1A6z8wgZVW+xV/lhmBXMAIAAAAABCR4BfdNVy7WE+IyQ312vYuIW0aGcXxr2II/MbNz8ZdAVsACAAAAAAng0GYpYJTypRLQUd5tIXWaAjZX5na04T/BypmwwrXPoAAzE5AH0AAAAFZAAgAAAAABooumzjEqp9Hvvd+sn1L82NI2iUGRl0nXQNJTHM7oyVBXMAIAAAAADgjz5L2ursK4C+pXXsJ6XHABhyallj9s/vSUgxXvjiiwVsACAAAAAAPjlAM0tbO6EUmLAeIZt57YMkMsuQfuC3T3d9vtnxgjwAAzIwAH0AAAAFZAAgAAAAAMA4jmE8U2uGkYUeKoYSlb22tfrRq2VlhV1Jq1kn4hV9BXMAIAAAAADG4fLeJUcINPSb1pMfAASJkuYsgS/59Eq/51mET/Y7RQVsACAAAAAAmwwcWOnzvpxm4pROXOL+BlxjEG/7v7hIautb2ubFT44AAzIxAH0AAAAFZAAgAAAAAK8/E3VHzHM6Kjp39GjFy+ci1IiUG5oxh0W6elV+oiX2BXMAIAAAAAA4/F4Q94xxb2TvZcMcji/DVTFrZlH8BL/HzD86RRmqNAVsACAAAAAAif3HPf6B1dTX/W+Vlp6ohadEQk/GAmHYzXfJia2zHeIAAzIyAH0AAAAFZAAgAAAAAGUX9ttLN1cCrOjlzsl/E6jEzQottNDw8Zo94nbO1133BXMAIAAAAAA7uVthFvXH+pbBrgQmnkPcpiHFEVCAi0WA7sAt9tlt3gVsACAAAAAAznaMStSbtGXU1Pb5z9KDTvEd79s6gmWYCKOKdzeijpEAAzIzAH0AAAAFZAAgAAAAAKnT/qg8N85Q9EQvpH7FBqUooxHFgrIjqLlIDheva2QSBXMAIAAAAABGAKkFMKoSIrvClWF7filoYM6fI9xSqOJVNS3dv4lxYwVsACAAAAAAgITE31hQA4ZOxpUFYSYv0mzWbd/6RKgbUXiUY96fBQEAAzI0AH0AAAAFZAAgAAAAAHRDRDT2hJrJ8X9zB9ELT28q8ZsfkYr92chaZYakiLlqBXMAIAAAAAAT0Le67ObldDta/Qb17dYfdslPsJTfGj3bWAgC0JIingVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoAAzI1AH0AAAAFZAAgAAAAAOOJcUjYOE0KqcYS1yZ363zglQXfr3XSD+R5fWLSivDoBXMAIAAAAABjeLe+tg37lNa+DdVxtlCtY77tV9PqfJ5X4XEKrfwu0AVsACAAAAAAlbpHiQAPLLTvSF+u58RBCLnYQKB5wciIQmANV9bkzsoAAzI2AH0AAAAFZAAgAAAAAMwWOOaWDDYUusdA1nyoaEB3C4/9GRpFNGags95Ddp4LBXMAIAAAAACLrsQXGWK15fW4mPEUXJ/90by13aG+727qWJep8QJ/WgVsACAAAAAAuThwsAsKUB56QAXC0MjJsZ9736atbiHPlK2tE0urf9QAAzI3AH0AAAAFZAAgAAAAABPRXBK0z8UANcvMDWntBjN9yF7iGMPLbhbaKrvHwcplBXMAIAAAAACZlqWsYPIb+ydmH03BxD3TqSGsSNoI7EVCy0VgW0TpYgVsACAAAAAAD2uaBv8oc7l4EeC5PWx5sfeyGZoas0JdFJ33M3jjgjMAAzI4AH0AAAAFZAAgAAAAAOn9/6pbzjIxFEApugaVOvVKXq23sDCJELv5UtLPDZI3BXMAIAAAAACHIwSDTlof0vFoigF4drbeM/8rdlj/4U386zQsNLtPGwVsACAAAAAAsYt/rXnpL55J9rlWSFRA4seaU6ggix7RgxbrJPu6gO4AAzI5AH0AAAAFZAAgAAAAAIMCESykv5b5d6mYjU5DlnO709lOFCaNoJBLtzBIqmg4BXMAIAAAAADs1Bfuaun4Es3nQ4kr29BzheLRDcFv+9a0gOGkSEcrDgVsACAAAAAA5kW6i/jOBSdoGAsZEZxVNRvt6miv86bP8JfUT+1KJg8AAzMwAH0AAAAFZAAgAAAAAFSPmr27XgKhUkbEvvC6Br5K1w7280NZrrhdzfYF+YGjBXMAIAAAAADv2h+Xq6kM7MHYTLMACRwbe2MzGHu4sdB67FGzDR6H4QVsACAAAAAAKII0MMC7o6GKVfGo2qBW/p35NupBp7MI6Gp0zXYwJOcAAzMxAH0AAAAFZAAgAAAAAPSV9qprvlNZK6OSQZNxKhJmBMs6QCKFESB/oeIvAS0iBXMAIAAAAAA835Jh22/pvZgKoYH6KjE+RRpYkaM1G35TWq6uplk/rgVsACAAAAAA162IdSb079yVlS7GkuSdHU3dOw03a+NS55ZPVBxbD08AAzMyAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzMzAH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzM0AH0AAAAFZAAgAAAAAKJY+8+7psFzJb5T+Mg9UWb6gA9Y8NN9j/ML2jZkNDNPBXMAIAAAAAA2R/nCtSYfCim89BzdUPS+DTQGwYDk+2ihFPEBS8h+ygVsACAAAAAAaEQra7xyvA3JS0BasIpRVrz7ZXsp6RpH7OpfJBFzFG8AAzM1AH0AAAAFZAAgAAAAAI4qr+sJiRaqwZRhnenAzD7tTKq+jP1aaLyAln3w1HQuBXMAIAAAAADNYpqV73NpwN+Ta0ms1SRiu+6WNOOdGT+syghL+JAFhQVsACAAAAAAN07Fo9SK+fXp5Odk1J806pyVWc2WHXCtb1gJQknTgqsAAzM2AH0AAAAFZAAgAAAAAISgN1Hid7IWvDESN/3tywFZiBsZPYapOUx9/QjDDxLfBXMAIAAAAAA7lxpEz3+CGdv6/WKIAlIwRYURREKgn7+StwNoVekkDwVsACAAAAAAx+Oa2v1e1R7VomfsvcKO8VkY4eTl7LzjNQQL6Cj6GBQAAzM3AH0AAAAFZAAgAAAAAOTLdk1RIUzCsvK7xCXy+LxGhJf87fEL406U9QKta3JRBXMAIAAAAAD8+6UnUn8sN6AgQuuf7uFxW+2ZJNpZLgp3eKVtjbo9ewVsACAAAAAAQN3mZHmaDM0ZbUnk2O/+wCUjiCs4bnshfHjd/4ygLXcAAzM4AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzM5AH0AAAAFZAAgAAAAAPLX4XT1eMfokMvj73G6loHEotbdivVFM6cpMbU0zIOmBXMAIAAAAABuTqwm6E60kVBN5iClzLnMBozIQRYjMozzRNKVhixkEAVsACAAAAAAjvY9G0Of8EQcZ4GVfSEVz7jrNn7i4qps2r82jJmngKoAAzQwAH0AAAAFZAAgAAAAAGzGJAUZBcVKRb4bCSNaRxtcDH2TqIgHqMElD9RL7SzDBXMAIAAAAABbJfrLwBrqZ2Ylm9QfL7nkW+GJ8vTlaeMUDT5620ebaAVsACAAAAAASiaS1IlBls5Tan57XqqbR1cuvyOcoSibJJQGREzm4c0AAzQxAH0AAAAFZAAgAAAAAC028abAppwE/ApZHU5RbzZZ8OPD5eJ8/6+NgiSFf4d+BXMAIAAAAAD3THvDUYWULR+AVLuRRPPAMVMeZ2ldWpBYSODboszWbQVsACAAAAAAATOaeYj+kx3MTDeNUcKGbUxLZDeMjC8JrWnlHmWTamQAAzQyAH0AAAAFZAAgAAAAAHWr8wQYIKLiKeb3wd8kZQuXD/GUHDqXj12K/EQWV11CBXMAIAAAAADo3aFHDuyfls9tcWCxlFqJn4zDXd3WT9CIFYFjJnTYswVsACAAAAAAeMbIatR7DgefzuvF4WyNVDjJxP8KPA6U/rmMQIBvpM0AAzQzAH0AAAAFZAAgAAAAAMdRi6AAjF1Z9ucMqYl2Ud1PLUGOlOPJFgSrPTjs27u8BXMAIAAAAAAqOdI7+P8srvqCTFadwMM3iggaVOGcf1BB0EjBYeV6RAVsACAAAAAAU+V2GrqgxJYs9mxuak/8JMFICXwQ2vksrBdOvSwWFpoAAzQ0AH0AAAAFZAAgAAAAADKKe++fqh4sn0a8Bb+w3QMFnOqSE5hDI3zGQTcmJGcOBXMAIAAAAAC8ebHa++JmxVISv6LzjuMgEZqzKSZlJyujnSV9syRD9AVsACAAAAAAQcVNSjyetScLu78IrAYaAigerY4kWtnbctmIyb19Wa4AAzQ1AH0AAAAFZAAgAAAAAMKoHwhZcocaQy7asIuRG8+P1qPENgFAwzc3X1gZWYnJBXMAIAAAAAB+R01s+WdJjLa5p7STuEylradWr+2JDxsWx9bKDgXNDQVsACAAAAAADeXTBHsm+FH2pQVoqOBPPIJiTJLqrzGisNnQ3S3xYJAAAzQ2AH0AAAAFZAAgAAAAAF41XuyBvREKcxjDl+wbnillseykpAjCKHmwIu+RNvM7BXMAIAAAAAC2Wzq+2mfO7howoOZxquqvOuH1D2WdlzA1nK+LUp0FMgVsACAAAAAARha+D6DVeDxSjNyXXO5DMY+W70EGyfc7gxR4TjzcYusAAzQ3AH0AAAAFZAAgAAAAAAfONgdhLPEjvsMxTY9K4//7WjREuRmZ6Bpcf3yvdMf3BXMAIAAAAABCy/zjmzucxQkbJ96l5vS5x6SeyHE0Z+Aqp9oZgBcC6QVsACAAAAAAasG/uN4DnWHZLkLhH4cMzXk5F/HL2D+72WH+1jjgH8UAAzQ4AH0AAAAFZAAgAAAAAA5ZsebFm5NrSGs2E17+fUt4qkzsVmy4IJA5nGehtSBVBXMAIAAAAAAOzteKfp+YGPqn1fi8u/lKXP7E2Zgouwgt6KAADHX9AQVsACAAAAAA2+FaAbl8JZogfNCI0FFbmZZPy/KLF1u16FGrPspSbEIAAzQ5AH0AAAAFZAAgAAAAAHf6LIjrvy6I31w/8b910U9qU8cBIYiWn9mW55NYZF8VBXMAIAAAAACONPisRtnFG9vV2mTQ3hRR/hGuVRA9dGd9Lt9JqDoM8wVsACAAAAAA+h7V/jIYJcd0ALIvFBlwxkFqWxBVlkqT9wFkmumr4QcAAzUwAH0AAAAFZAAgAAAAAGSuCqGwDiCbKHUv5r+dNZHRupcQLFq+vnesTVMAAP9eBXMAIAAAAAA86UcdE9/45JJhoIKu7KXHb4CpP8laA0ga2+az4ydybAVsACAAAAAAqpiTVy3VNE3SSSzdkVQqCL5FKQR+jEzMUZCYGL92yloAAzUxAH0AAAAFZAAgAAAAAEeRZdjMb3fthvbV8aszi4gMScRnknrj6oMIP5QOG5wKBXMAIAAAAACl7skGzK2xwd7ZLGfv+3ODOfP2UQGEe+vGDZphWbFjGAVsACAAAAAAlQo0yTqQl/6qOSqTYU9an/9a5MgHqZRIkdmmx/pycGYAAzUyAH0AAAAFZAAgAAAAAGs3H8zIeyQeEbORy4WJZi3orD6mbEdHAoGlx2LbjYB7BXMAIAAAAABRBtcXvCZ0k/N0bTks4FqPjnI6y5WEjwmee3IPOV2dhgVsACAAAAAAzgOebXiyusjNVeww8k/dZzV+DJl1IAQaeS42s6txDwcAAzUzAH0AAAAFZAAgAAAAAHLQCN2v6k1M3Mh09ycsGxn2lg2rkoRXLm1zMe8CN6HuBXMAIAAAAAC6cm35hij6OZ3lC3WkuPBCYNpkl/G3gaPwe+B8uNFscAVsACAAAAAAzuZ6GKMfHIcJgVeBX6z4F90bdHeMJHM/g2uEeh/04uIAAzU0AH0AAAAFZAAgAAAAAJYETkDrNHQ6RA9efMan2750oIVURhaG7OF5A7FGJD7YBXMAIAAAAAAuS/DKY9DK1VCIQpro93O39U5YCk3gQvUy30nAm2sD/AVsACAAAAAAnyDClVlJh/MhHT+7ZN56OOy4fR44cgRcA3LoL6lcnOMAAzU1AH0AAAAFZAAgAAAAAKyMZSr4RDrwyOfEpxz12DkESUrG28NOIH9hD7K7V70WBXMAIAAAAAC+N+Wn1B9PBJwDM+RhQoDtxyBrzrpUsn639e7zymuHsgVsACAAAAAAnzjJywP47oA64M6CO6Ypagkg2WNwkzBec+idRpwgx8gAAzU2AH0AAAAFZAAgAAAAAOzfhanz31ZesiQO4eqwgIPzQBHm9q86H7Zz5St4UmOgBXMAIAAAAABRKkoeKJdPRKAhNpWJu65ZexKtvXN0YiYR71Slz+xbKAVsACAAAAAAMM9lS884l13bg0meIRtfAMe+H0BjqaWDxOhdNS2cJ0YAAzU3AH0AAAAFZAAgAAAAAMDQ5Ni9LzyYETb7v+Zsi3sI5gVVicaS9cXKeT5dyxguBXMAIAAAAAAXeLsOHQxkAHOnCUOSMBtxuqzhAZbTXcGGIo7BJQsqcgVsACAAAAAAD4kCd4scjl8215HLKkPEtvG6tqeFqEt7299YTtXHBZ0AAzU4AH0AAAAFZAAgAAAAAKor/9SYRTiqcVPbQZ4TNX6vGtGQLOgX3IJQOF00js0fBXMAIAAAAADnv8LhIkQu2Ngx/QkG1ivFBydRm1CXTCYDa5ehcHalwAVsACAAAAAAsFXF4ojkxEiA1AZplSiIbJhEMYtF2iB/J+3ijN6oYfsAAzU5AH0AAAAFZAAgAAAAAHM+4F6q+h+oG18k7Ugc3wEY+D1AF7CXs2uH1Vly9iOVBXMAIAAAAAASi+d+HDuasm4JlHqEKXFkeTtGfVSOXx7cCL/ickhEmQVsACAAAAAAYABUNdWFsGAzGGWTOgHesVjQiuF+z2Pq+2yjKpbuiuwAAzYwAH0AAAAFZAAgAAAAAN1a/xvr68bQdDLEWMwBJ+xkR1RSQe/rfHzm+oRUWjj7BXMAIAAAAADC6XG7b3b5ekJq3tcbVsNzRzjTx1n7eQZJm8y6RW+XjQVsACAAAAAAbKqpiW1mQiFG9e4RJwzhZ7GJfGP7eL5SGDi/wJAAcgwAAzYxAH0AAAAFZAAgAAAAABRi1V8GjEY+KnhNZdQj/MRfusJWh77uPc73hllc3Y6mBXMAIAAAAADZJrEhrrWh63Bg2WlJwWA1BJpERss3zYiVKRusPsXgYQVsACAAAAAAad/RFmsdVc+iIEpC7x7LvfLY4q2sgwhleAWUcQU/em0AAzYyAH0AAAAFZAAgAAAAAMfEhtfZsARdx3HIOX8r7sxCwj54Rlbjv9b5A4SHZCNQBXMAIAAAAAA9Nv9HBmezK3VVD5wO36C0r6UKnfpDA7J6nz9M1ZY9fwVsACAAAAAAZFJjrcvTNvMSm9ynZCWbxz8KSdM7XNQyg0EJLE4g2ZUAAzYzAH0AAAAFZAAgAAAAAEKW2CdiVdJFRASYIdGeFlmFxxTkWPJvZYDB3X56dJ8XBXMAIAAAAADTCvZIcS8sI4HXKprZfN5glm1GnOLgvCoa89mVtV5YrQVsACAAAAAAj4YXFjfWcNBVGnBGhMFGckJqnXY2ABMAR/RB8Lsp7rYAAzY0AH0AAAAFZAAgAAAAAEutYsuURdzGdpno2roJQZo1eqP1UbL6EDUtea85tUI4BXMAIAAAAABohKVuw/lWQ1kRgzJ0vBs8JyZwaGW+QYJJmWB5iA3KAAVsACAAAAAAC5ggnczB/q8AVMWNXK4U0lSRtX/ERZWXcPWhQKmcUaMAAzY1AH0AAAAFZAAgAAAAAO1iwPElahORehTk1yUZsKihXboCq/6zxwgRP36Xo3V1BXMAIAAAAADGnEuD75uB5/BaAShmt8r5xrHLbfUeKZ4dzT4YryYOdgVsACAAAAAAh/D+SjVZhqFrsDEgjnt/1bWzRS3Ni70PjyIAQZdJUKEAAzY2AH0AAAAFZAAgAAAAAMlJpmC3v7UaWyPUp8PJ3RSqgzCd4YwCcGCxdVx0M3hlBXMAIAAAAABAEL3SDse67rubcviazq9u5CRUiVxTrxGJMIvSDtTAyQVsACAAAAAAHvqGlnw1oL+ukOCHjxJqTB0Y1p13W4HTswK6dnOB9asAAzY3AH0AAAAFZAAgAAAAAOqxwR1L6dUiQ/18NIeMQeTQGFCSXhdyvZR3utp4gPvMBXMAIAAAAABF4HiwATABEf7/3uVWTV7+e0ZZxUz3ZRjwLZ7R2+JKOAVsACAAAAAAmokF/EQWaJOFRzVceXCK1SkMWMAG6mzhPemagfm4kaMAAzY4AH0AAAAFZAAgAAAAAOPl56k1CDGBmzlOnQnuuWxRlQILsfZt/RkuL3VXlmrZBXMAIAAAAABycFzLbUg66HkXBhqp2BIRmzaB3U06F+r76GufPC8X0AVsACAAAAAAUpyUsstlU4s2KZWxC2VX+lXtq17mqtj7BseMlXqk4z0AAzY5AH0AAAAFZAAgAAAAAAizvP0kRPP5NsR3Lgkjcu/hCN6Hlro/1et2JFGadJwUBXMAIAAAAADWh06ylmbMV7YUPM8nNE4gPmBovILP0csZEzlxIwqUHAVsACAAAAAA80WaShO2g1Y3H1v2JdN3yYr3hd7wJqqMVsqKLxy83+wAAzcwAH0AAAAFZAAgAAAAAG03sWigYFdzomEQS3piPmXHdctygfLtoB6v2JpuZXLrBXMAIAAAAAAyNHkG8/r/gchTxN3+xMJyn7D9EHg8xurSmtu2vKfm4AVsACAAAAAAF2hXTl12343tY7/58TzW/j/y+K8uEo6cbymbXHXbOBAAAzcxAH0AAAAFZAAgAAAAAN9vb5Mt+1faUL0Jv4Rw8Sm0teC7DiJoklmzCtHAEiqFBXMAIAAAAAA9BbRXVFpfhtABJeSQh2jjelAOs3i7PxjFo2h8y8ZtxwVsACAAAAAAEXGZx6aKA+kkc4MiwbgZh+5MlRM4VtsGXpWxLgUVuKwAAzcyAH0AAAAFZAAgAAAAAEvj5EUo7D1fLfzCl2pBEfKsxOYdtx0goFlgnu+G0MJWBXMAIAAAAADUtX+QWNQLc0N07HDKUOG/AUPC/L/SodoNyy5g+maTNgVsACAAAAAA3dkTrvKAUH413AMoHnJ9Y9Qe2At+TEaSHQ0yMTgoSuAAAzczAH0AAAAFZAAgAAAAAHx7x9/KJCvra1HfH2KLvRIPdXQ0PxB8/xYXn5Nc0dUsBXMAIAAAAACBVV98MvnXciPXhTE+7UF2SM8rtf0EyGTk7r95yS/CtgVsACAAAAAAJkXe/I9UqK0M6Qi3vKSQ303F01Kw1lsyofnGHGdP2bQAAzc0AH0AAAAFZAAgAAAAADLWupyXf9TGA3rTlFi1hmN8wUM3lCj3dpNPwkj87l6QBXMAIAAAAADrAhRHKXMwUFtBRjFKPnKJ9EVB9POnpoXd5nVFcY2lnwVsACAAAAAAES27lFQ6blo6J7VJKKnCLyL0uE2vhaVDHvGS+Xl6JIYAAzc1AH0AAAAFZAAgAAAAANWcTgudt0oVrkC0+4xZTvjCaPTwmc6e5mtVWnZAOk3YBXMAIAAAAABW1JAizgqkmBRa28ZmZ5JYXoVK7dVUkWHrbMOtdLwSCQVsACAAAAAA6jZgSp2B2FgxeRL9tH+RxdQmh3mXmMlfiyunxG6vH5MAAzc2AH0AAAAFZAAgAAAAACODdrQ54i6kkIpidgGIENcRtVihFBoIvhNPLFn54uF6BXMAIAAAAADhZlJEjgIWNHASaqD/cyl9R2ND6TUQs9qgmNco2F1y4wVsACAAAAAA5aJ5cDWCyhkPgpHGWR2irvMAWlPmHDYRFUQhv+CuTlYAAzc3AH0AAAAFZAAgAAAAAC8jR9JN8BFgXlC3lgo6SE03NRA7juVNe/l9JOagOC5CBXMAIAAAAABRNf8azLdXZOsLPV84A9ltNki+QBroHIc5VFkCZOuqPwVsACAAAAAAu9gbz8bYRPJj84i3VTjYGpGnKAgQZ3ZTbILn5Th8xvwAAzc4AH0AAAAFZAAgAAAAAGAlFyl5pYEcvq9Au4QbHC7urVIP5L3LUbUJ8utjuH3PBXMAIAAAAADs8HNWINXMZDOPssmJsGdAb5ZQzmTPM5C3hhqU0v6fhgVsACAAAAAAlypAIKxptBkZI8+bytdhfs0bloGxJyOBU0fMfoRwzjsAAzc5AH0AAAAFZAAgAAAAAGqVqopTQHEV3WxnVty1C3eAX6o8jI6IiwlOsFhRvTfdBXMAIAAAAACY/fxUTVzRUt3xQHuZ4Gi79y0WBYNRsjb+S89eqF7qzwVsACAAAAAARk0soe4ixB/nCi5uDhxnHLj/hsQO0177q51fu5KXS3AAAzgwAH0AAAAFZAAgAAAAAKh6pgPZVrc42VlReQ/B4wkdzsrIBNvdZA2isoX5vBt/BXMAIAAAAADHuW52B8dfG6nslza59o5xZu9cxDlklObfQqzG0VyV2gVsACAAAAAAZIQaUb92wwyr5anpcFORMGoYF0PBA5uCuz0HQlXVsKsAAzgxAH0AAAAFZAAgAAAAANuWg6p/03eY9PrLQEwv3I5PdG7qsZSQkX+DvlQ0zJOrBXMAIAAAAACM1VbBFOFOYxyDEB0btJ6dd8Ol+4gP36iZ9UiFbDI8fQVsACAAAAAATuqgqccWIOS1HcoXwEc2Bhf8L8bM4533fhaZY7eI4eoAAzgyAH0AAAAFZAAgAAAAAL6X4GBvAClaNaMMmpkFfhAAdm3es0mtFFSjpfjxlc2qBXMAIAAAAACo9MV6Bwgt3TP59JbvOnMN/41Uh/Cik+Ay9PyxUlc+AwVsACAAAAAAlmRoAn8+fZrATPye4R2AXLbEbaZbYHBeYMO+BGXfUkwAAzgzAH0AAAAFZAAgAAAAAFJjRYUVb4pcwLxGI5GDyjIEkMNlrOmz3EuN8HV7ebAFBXMAIAAAAABfYMVLK3KSn6TfE2zmjejrEqDGAJDBX3bwOd4Ue4Md9wVsACAAAAAAwgKbzRW4q6dXXa2zQSWbSQE6B2sO1K9dwOxwGmghjzkAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYABgAAABNtbgAAAAAAAAAAAAAAAAAAAEAwE214ABWB6X30ECIRAAAAAAAAQDAA",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "decimal",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            },
                            "min": {
                                "$numberDecimal": "0"
                            },
                            "max": {
                                "$numberDecimal": "1234567890123456789"
                            },
                            "precision": {
                                "$numberInt": "2"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-precision-v2/mongocryptd-reply.json000066400000000000000000000044331521103432300317600ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "AysBAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAzAAAAANlZGdlc0luZm8AmgAAABNsb3dlckJvdW5kAAAAAAAAAAAAAAAAAAAAQDAIbGJJbmNsdWRlZAABE3VwcGVyQm91bmQAFYHpffQQIhEAAAAAAABAMAh1YkluY2x1ZGVkAAEQcHJlY2lzaW9uAAIAAAATaW5kZXhNaW4AAAAAAAAAAAAAAAAAAABAMBNpbmRleE1heAAVgel99BAiEQAAAAAAAEAwABBwYXlsb2FkSWQA0gQAABBmaXJzdE9wZXJhdG9yAAEAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "decimal",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                },
                                "min": {
                                    "$numberDecimal": "0"
                                },
                                "max": {
                                    "$numberDecimal": "1234567890123456789"
                                },
                                "precision": {
                                    "$numberInt": "2"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-v2/000077500000000000000000000000001521103432300233725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-v2/cmd.json000066400000000000000000000001511521103432300250250ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {"$numberDecimal":"1.23000000000000"}
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-v2/encrypted-field-map.json000066400000000000000000000011771521103432300301240ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "decimal",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-v2/encrypted-payload.json000066400000000000000000000312051521103432300277120ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DT4iAAADcGF5bG9hZADSIQAABGcAviEAAAMwAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzEAfQAAAAVkACAAAAAADGB5yU2XT0fse/MPWgvBvZikVxrl5pf3S5K1hceKWooFcwAgAAAAAIxTmlLHMjNaVDEfJbXvRez0SEPWFREBJCT6qTHsrljoBWwAIAAAAAAlswzAl81+0DteibwHD+CG5mZJrfHXa9NnEFRtXybzzwADMgB9AAAABWQAIAAAAACiWPvPu6bBcyW+U/jIPVFm+oAPWPDTfY/zC9o2ZDQzTwVzACAAAAAANkf5wrUmHwopvPQc3VD0vg00BsGA5PtooRTxAUvIfsoFbAAgAAAAAGhEK2u8crwNyUtAWrCKUVa8+2V7KekaR+zqXyQRcxRvAAMzAH0AAAAFZAAgAAAAAI4qr+sJiRaqwZRhnenAzD7tTKq+jP1aaLyAln3w1HQuBXMAIAAAAADNYpqV73NpwN+Ta0ms1SRiu+6WNOOdGT+syghL+JAFhQVsACAAAAAAN07Fo9SK+fXp5Odk1J806pyVWc2WHXCtb1gJQknTgqsAAzQAfQAAAAVkACAAAAAAhKA3UeJ3sha8MRI3/e3LAVmIGxk9hqk5TH39CMMPEt8FcwAgAAAAADuXGkTPf4IZ2/r9YogCUjBFhRFEQqCfv5K3A2hV6SQPBWwAIAAAAADH45ra/V7VHtWiZ+y9wo7xWRjh5OXsvOM1BAvoKPoYFAADNQB9AAAABWQAIAAAAADky3ZNUSFMwrLyu8Ql8vi8RoSX/O3xC+NOlPUCrWtyUQVzACAAAAAA/PulJ1J/LDegIELrn+7hcVvtmSTaWS4Kd3ilbY26PXsFbAAgAAAAAEDd5mR5mgzNGW1J5Njv/sAlI4grOG57IXx43f+MoC13AAM2AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzcAfQAAAAVkACAAAAAA8tfhdPV4x+iQy+PvcbqWgcSi1t2K9UUzpykxtTTMg6YFcwAgAAAAAG5OrCboTrSRUE3mIKXMucwGjMhBFiMyjPNE0pWGLGQQBWwAIAAAAACO9j0bQ5/wRBxngZV9IRXPuOs2fuLiqmzavzaMmaeAqgADOAB9AAAABWQAIAAAAABsxiQFGQXFSkW+GwkjWkcbXAx9k6iIB6jBJQ/US+0swwVzACAAAAAAWyX6y8Aa6mdmJZvUHy+55FvhifL05WnjFA0+ettHm2gFbAAgAAAAAEomktSJQZbOU2p+e16qm0dXLr8jnKEomySUBkRM5uHNAAM5AH0AAAAFZAAgAAAAAC028abAppwE/ApZHU5RbzZZ8OPD5eJ8/6+NgiSFf4d+BXMAIAAAAAD3THvDUYWULR+AVLuRRPPAMVMeZ2ldWpBYSODboszWbQVsACAAAAAAATOaeYj+kx3MTDeNUcKGbUxLZDeMjC8JrWnlHmWTamQAAzEwAH0AAAAFZAAgAAAAAHWr8wQYIKLiKeb3wd8kZQuXD/GUHDqXj12K/EQWV11CBXMAIAAAAADo3aFHDuyfls9tcWCxlFqJn4zDXd3WT9CIFYFjJnTYswVsACAAAAAAeMbIatR7DgefzuvF4WyNVDjJxP8KPA6U/rmMQIBvpM0AAzExAH0AAAAFZAAgAAAAAKMcr5r7Xz2yrFanXGZbPST5UCkbI06Hctma2b5lAyAjBXMAIAAAAAAOs8fMoUPuLq3XhToTQujX1i45lm65QwuH0P8dJqwH+AVsACAAAAAAMNXrxYDT2xsv8nsp82icJm3e6DvOLZOdhMxHjZyU7sAAAzEyAH0AAAAFZAAgAAAAALNcFo8vCbJP9kGAMW8mmbJzn2WuJRiuuMS4OiUYrNg7BXMAIAAAAADWtUQT3RWkoszPbLZRCsla8HHaXegZkt37OoTSIWFxaAVsACAAAAAAWVyPN5uCYDiUGyxbLaidgE4B4zX9QeUGou77FkD+Z0UAAzEzAH0AAAAFZAAgAAAAAMF0GqC+QX0DW4evVw9ijfYYKAUgyrT5wNIQPBdtjBiLBXMAIAAAAADeXen8LoT1ka7Yg1QSQw+nVq68fTWspXNnHAkzMl/JoAVsACAAAAAAdLtS6EuItMfDyj5MDJKPoL0Az8EB6SZQNPlwfWMOz5AAAzE0AH0AAAAFZAAgAAAAAKX9Ml+ADrfpBphZ0QZ57vIbKVmxACRtsKIID3f7SvjbBXMAIAAAAAAW/SMWKm8CcUw9s6b82+/kbwtcVcIXUAWjd3gIl0yJaAVsACAAAAAAfAgReuju05ZO1FYSHjrxbgl3oILWdm+pLaIuLlpax2MAAzE1AH0AAAAFZAAgAAAAANY9/o8c+vB0xEU6w1sg6lKAy7YXHzhSY1xwAs919itRBXMAIAAAAAAm7OMmVntOxZs4sHgERPTpaIA1uyl326jlfY8yjkCfogVsACAAAAAAIswuXYuIGmYJpjAOqR8RgKavr9WhA5sCGFaZk+ebLlcAAzE2AH0AAAAFZAAgAAAAAKo4vFx7/xDSxPwb/pEVAidE/iixU7UhgISw9Jt5TI3mBXMAIAAAAAD5IIntCZXrE7KSYMODmMWFDW5h6ITewA7XCT6jRxZfUAVsACAAAAAAKZgX57jXtmEXI4dUHpVQQKGscRkE8Pb3ObuGFByhh9IAAzE3AH0AAAAFZAAgAAAAAOQAOO1VgpSmHrssm2CplVopLVGM/hn5J0Y9DnTQ97d3BXMAIAAAAADqWmoOVt7Lpya6Kmn5ZKULmPPAL1n02rycJRvtUknWngVsACAAAAAAq3hQkpkCKf+qNtWImVAe/Gc+jl3aRZiUgwv0m1VMQK0AAzE4AH0AAAAFZAAgAAAAAB/nb5j9nkv3IH0dZ/PEfWhqU6Nosh+ZDxqQqAyHweGEBXMAIAAAAAD9eZw7Iak7RCtIlXMw1dRulFgqn9jpGC3LfkPxD2w9ZwVsACAAAAAAy0LaIclUAZX/kXoyNG20zJIrB1wL2IbsapgC+sI8a7EAAzE5AH0AAAAFZAAgAAAAAHhauKkGHWp3KaTtPM4mMB6b6cqe32I48bXdo3KGb0r1BXMAIAAAAABeb5rjCI4lSZL81b/TFdlnYw6WLvvry7Ywu/f0kXz8YQVsACAAAAAARedF+f1fk0pAe0JbVMSkiKgNqYyod6PPgaVhDomgJ5oAAzIwAH0AAAAFZAAgAAAAAK3T25ahyPjbvmqd+a5BpeOu+u54ZUjhjtta4X3KuxofBXMAIAAAAADk3D7+zEp+V3VtcB29KEUE3cSJNjmota0b28RfqxtXHgVsACAAAAAACB13HydN9Bkykm7mDhg1vFYRSlWsGioARmK9dzXaZHwAAzIxAH0AAAAFZAAgAAAAAJq4mnb0VR5WmEBd1j7/4REWrvv2/LbJR/LIW3Wp9sUdBXMAIAAAAADYt8O9iNmKgCdxjihDoRMgP2eHfa7RS2UTV1THi6KSHQVsACAAAAAAX4IUOX8FrW5jyP31/DhtmjgyBMKLJ+pE8tVhdRGJ+pIAAzIyAH0AAAAFZAAgAAAAAIsg0b5jKPIPA5NbcnoSYN4zOkJwZuJFjpE5Ro8pMOt4BXMAIAAAAADhQqrpKCIbFELDWmSGJBn4jncmTYknqqNPKuC55jRQ4gVsACAAAAAAzSQUpsZjbUTf4WQfGHB2kIgozmnxQFOIXAYpt3AJyGIAAzIzAH0AAAAFZAAgAAAAAI6NCDeqLh3GdV/Omf64JBog9yb2cjsCUj4aTXmXY3KjBXMAIAAAAAAANSWYFbk0J9dXmg2IjzHJmtvsQb8mBYz5gJ44cXwhTQVsACAAAAAAM2bwol6ped5aUY/WlOYPbKyL9GpTN+C9QLWWZuo8GsoAAzI0AH0AAAAFZAAgAAAAANEz/Pu/siWmARVyG0HehtG0RuvJpjv+AW0xkRuKcQwUBXMAIAAAAABIUtg6po3hrHCNxUj1wkZuI/3TE9YKDZq3RghC5/KLUQVsACAAAAAAo2mbVAgAgolmzEdkPnIOAouTAI7rSgIOIFN5nHJXpkgAAzI1AH0AAAAFZAAgAAAAABEVHmVW+d5q66CV2LQlrdZDYz7tIljUtI+VaEurHZU4BXMAIAAAAADKGzvvTSkoXN1KfCKhiMZN6kD8wesP56J6B+LqjWgKzQVsACAAAAAAFlEC4bX0ZIfjADtfm0OWP58IDE0kie9UC3EP6eSuS50AAzI2AH0AAAAFZAAgAAAAAPvOBddi9PoSOtkVEuCXT+s+LxLTzmbtpN1Hs6OvURw9BXMAIAAAAAD7su3/wXLbKrELPZRR5YE8DmWdOKX2MeiJ4VqAWze5pQVsACAAAAAAYV2/MKfEzFVsfbtzJaabgz+1hKDH7La86moe2Rtb+ekAAzI3AH0AAAAFZAAgAAAAAJpa+FJvHBriQt9gv5uFTxzXiw1BPK+NFXirHlkWTLEuBXMAIAAAAAD2N2w5XS8RfPl+D/4HqoObOaY9jeTYr+dL2YXiISGo9AVsACAAAAAAHipAnJSPRlbgFb6zf/TlNDQS2HeWNBcxTolZU+adingAAzI4AH0AAAAFZAAgAAAAAF/TAl5DsdrsKNc+6uDbhsWqimDFk8/1noiuJ1gXW+KZBXMAIAAAAAD3YCRXc4JLfLX9l2mvRxzk+xOeQHqKfvo8UGQwVOKDwgVsACAAAAAAX82Xi5mRkk9jxMeCEagQd6c5j8+XN1r0iEFNjsks3nwAAzI5AH0AAAAFZAAgAAAAABNdBglgteulqnI+KZbNbFmlC8O/DHaXoivVR5kI8c/zBXMAIAAAAAAdig/KS8WwqwDfSfLB8CCO1WtyB17L0EKhttTeVnSOHwVsACAAAAAAjKt0vEPGM3c0X2qYjFG+TREfedC3mNT/OFQilC57aaAAAzMwAH0AAAAFZAAgAAAAANRXXrFtQQQgC0ScVyCvfrPzuziiu8rBe4fyRnZXDTnFBXMAIAAAAAClSklEdhI8GbmLBxo9Mgg65XjZY/6aaitfPDfgTw7EngVsACAAAAAAaYVtaN6BUGPyXDeZNIGeE5hmSaRvNIK4GHnyVLs33vAAAzMxAH0AAAAFZAAgAAAAAMwn9z/BWWIpziBj5FzyRBk5v1FU9fIvwrDmoWyswsBfBXMAIAAAAAAN0nhrt5FGl4jwnxw+T2s+Hk9MoftKdI+pepvPahB/2QVsACAAAAAAvPMkYNtmEtW6LmJQlMWSxySxIsyNqm1wtkLxXXF3b5oAAzMyAH0AAAAFZAAgAAAAAM45PK+8+3ID8/hLxNxlFpqdwsQSpJlfGuYWBg6on0KiBXMAIAAAAACIZ/fJ3BIOVgZou8DWxs496K08qea1SgDn1wqWkpnjZgVsACAAAAAAyDL7t4mmd18qlGlaPji+R93X5o17Ezz5HmtF5wj6kA8AAzMzAH0AAAAFZAAgAAAAAGZdtTybyh3nO178j6yU8v/cWRPLw8zbAeDf/9cD5j+oBXMAIAAAAABWtHTAB0jS59EyoLLHCc6nBGjRVfh9VMycX8iMASSq/AVsACAAAAAA3JYEnOVPnELHxu2trTpLnwr48WuLH6vPvdil1SBPdDAAAzM0AH0AAAAFZAAgAAAAAMB+GP1ElO2CW8WtvsiNmHvCfkjyeYUhja5+VcoDXk8dBXMAIAAAAACxkEm60Yya0O1yugexbMP/ZHMc3DS/QUvxrK5g23omogVsACAAAAAAD+pWOnxhsJCfRwMJWS7aefgdB9MUvp38+VBobt/GIXQAAzM1AH0AAAAFZAAgAAAAAMNluGDjzXirnwtURg8SYi62wJV2nDDYNhTNQHX94nxQBXMAIAAAAAC8FxOpEk7DP4G9aRiaCBMTcZRfZc/iV8v7sJz1hFc8vQVsACAAAAAAzHq+gZUXrxv1Z32tk87F5e0nLekIXSBB1yVSjIeQ72sAAzM2AH0AAAAFZAAgAAAAAAmNgU0w3FiyaWSMrxMTo1cd0TXh679cOJKa/CDR9FNHBXMAIAAAAADUqpkqOMWiBg8bg8jX9dYhE9oC5vfTJzEWLDf13ldnEAVsACAAAAAAdTWwTPd3SiSBlEFjhUX0xvQbFwznBNR9oRJmCiNEkSoAAzM3AH0AAAAFZAAgAAAAAORLbP1JQlpkpIT8Lqhe711uyUz10wKaAjsdac2zBFiXBXMAIAAAAACl76lhRZ/DzT6/K8iR8itLgVgwqiFvShfJzG8UhxwdAgVsACAAAAAA7obvfe9oidO5Nz/E7VmNRBvicWzU8nIzsUshsLgWdbwAAzM4AH0AAAAFZAAgAAAAAIecDUmoEoot6mt+Ea/hlAGrv22Xu/5E39gHXDEYMTbABXMAIAAAAACpmEMCPfMbOLRtDXJacXuvwdbsWoHAqrStlxYWiKoLJgVsACAAAAAAlJYyvvgXenZAO6VUcQ9eWxMd7YsCONoOKY6oNCOZn+wAAzM5AH0AAAAFZAAgAAAAAJpB1lCHQRwe0yukehZASh6gHUQkrYWqpuKmeLBtXRUCBXMAIAAAAAAdwW7FxroOvU6lABeg0TlRfvuRVaGbo7hwJYDba7JV+QVsACAAAAAAdr94hjlXCj0konIcbbOUD+BSg10oQfbb0PJAZ08rjB0AAzQwAH0AAAAFZAAgAAAAAO6kC7gwdrp1GZHvx3y0/tS2gdg3t0ChTId+fwVAc+aBBXMAIAAAAADUdDJCeFzKkphLmxYltiHWCMinTnTyvDWn5zhdVJcFwgVsACAAAAAAXRZackhuTjuWrSSlPtgBQPfuzhlVlqKd3fk5Qeh2JUsAAzQxAH0AAAAFZAAgAAAAACr3wyGbiyKvbOEtL2uQK5Ru/3VE3EoVdaja3d1x9n+KBXMAIAAAAABvYBaLgTKf+vUK+vQGWSkOoYXEG1eO0oe8z4PQLJfxXAVsACAAAAAAxGDaoHRRCur3PC+9NPFsDZV2j3RObv/jxhymiK5U/MsAAzQyAH0AAAAFZAAgAAAAAC/ec4RmjSaE0D7xjTXJdyut1Tf/zZc0ahAi1wHeAGfdBXMAIAAAAABFLC7p8RuQozldu7TrSpzX0lzJxGqggRjaXKG32YVkNQVsACAAAAAAiv62QaUD4wwqIs8oB6p3nEFhVgV1v6GQtxmpxnQZdwsAAzQzAH0AAAAFZAAgAAAAAIZZrr2ym3IAIN1TacwRglu9Iu3PmMEa3OKUXe6CyqUOBXMAIAAAAABoM4CgUBAyuaDW8BtbpY2jd2qi/qfSxueYZ6T3IhGuQQVsACAAAAAAJofwR78nyxwZmcuqMdTIihkfMYWYdXSg+Wh8+A52RZoAAzQ0AH0AAAAFZAAgAAAAAD6UCQSE3LAe1q1KfSO+8+RymwxqZhgItL3cYn/F0WN/BXMAIAAAAAAcirTnyxRpxeK6QuOYvi1F72Q3DYKYFlKZO/BAonbpEAVsACAAAAAAlD2jJX4bHNljyFgo4yvVIvDEHqWfAzps7bWl4VYtDF8AAzQ1AH0AAAAFZAAgAAAAABhFEgPHhzPS4TzKcBd8LCj23J6grXN5a0IuLxpSoxGZBXMAIAAAAABTHYYr+mlukrWw8xN6F0mQ9vqVQunchqg4vyqSbgUq7wVsACAAAAAAqQUTZnXweCnxJ9nbosGJYTZuMC0pk/fEThOPda+rThIAAzQ2AH0AAAAFZAAgAAAAANlu+I1Q4FGVldfUrNsbkC1Nd3sumvNT9lDOXAf5mjnbBXMAIAAAAAAmuj34l7vs73CjIEQ6PDhlSjPUra9B7JRUy12AXkp8BQVsACAAAAAAmM5hrV6tVhvi2+IsVmkWOrqU6maVOoz8pzuTdceyz6UAAzQ3AH0AAAAFZAAgAAAAAPDdaRz71jKkmTM8ySciWA0q5taw9iXT2Dgxr2Ulx9WKBXMAIAAAAACnEN75n593YpjJHVPmzZN4rm7FbcZM4KKMBERp0dnR6QVsACAAAAAAzVyfrmDnxhnjoA+LsVMWbhzw16aaTFGmL/saqCOjNp4AAzQ4AH0AAAAFZAAgAAAAAMhWGPKiuaOVkeMzMwgG5KUD/CaPkKOH2rqYtNvIuwNPBXMAIAAAAAAOdyVktu01Ha+Q0wWtkDp+ctMam+wwf5atE1wa8ho4cAVsACAAAAAAQOzrGHGE3b0Pme2JZ9XiVxyeoHXFSS7XNrVi0gJCIOsAAzQ5AH0AAAAFZAAgAAAAANcbmUfl/HgxGN5yx0B4q6LvXqOa41PcnPB+/o4BP5NsBXMAIAAAAACMKFR2THzWW2owyRBV423gYXsoIQ9h6OxzzIVxPgjHqwVsACAAAAAAguRoNmSt2PilQ0OaNMUZC1idhEPQTx6lQf3Lp76nqqUAAzUwAH0AAAAFZAAgAAAAAPJjIp6/YEdkGeFPeTpz224dVT86h1dYdddTkzSxnsqkBXMAIAAAAACSxSSpgQEOMXO1wUhqCwAxlUbLHH5MfE9Cq8xQ8+oqkAVsACAAAAAAKf4qYbLFkL3UZtY330duVDMwaWkTmqgFLVIuri9yrPsAAzUxAH0AAAAFZAAgAAAAAElLSQYt5yYw/KBWI3LMDP/T7dyyCKur041McmgBtuvSBXMAIAAAAADNQHsyYQmEDyQDOcjJDBCDEz9z/AzSsK0YYx9GqQGmrwVsACAAAAAAA/5G8R4lSfeP7DTtf39myccZKghlUp5gndKCiE3qQvsAAzUyAH0AAAAFZAAgAAAAAJD5b8XoMdIJxaNqvi9CJYhti4GKna2QfS4Mb2UbGROMBXMAIAAAAACmqv/fMUSlbqyqsTf3p4ZOSLvGIqo6VbSQn5coGTaeJAVsACAAAAAAXoAQ+x3vqeO66I+/dO2Yys04acTZjTZ9ggrr7eeAQ18AAzUzAH0AAAAFZAAgAAAAABZuWaiFy1bkMf6JcZPnMsw8EI0ZDATGZMUAB3qPik1rBXMAIAAAAAChKNranelKx6viKoU1Gmhge5KSFKhAOpIntw2WW7CgVAVsACAAAAAAQdbvBm8lN3wLxzuPNdZ/kADf0Y4EnBULte6KLMWaI3UAAzU0AH0AAAAFZAAgAAAAAGXT2XnXEy5XOmZ4EleWXfMH6FoitVYgsokK+vz+hOinBXMAIAAAAAAzFj3o33xdL2JoNpVZQB9B0YsRW41tNQ7l1M5pPjWJdAVsACAAAAAA5W0/TwxTFbVYce663/uNvd7aJGEyx5hEgKozi5OfZOwAAzU1AH0AAAAFZAAgAAAAALmWrBe7bniqWmj0c+14dD7uOapfVcQSVrhGdeCa9dkpBXMAIAAAAADOdLXaHATDzd/bQmDm/ycHACieCL2pfduibJ4apaqnEAVsACAAAAAAUYb6yi56J1tHRNQ3sMIDYTniLa619ISyNzVGJbR+vuEAAzU2AH0AAAAFZAAgAAAAANoRHZeXRtFthTS+x0eBFoGqOEwwiWVOmuvx1unVSwDfBXMAIAAAAACKQme6kNu/RK3Sa1dqqGJpJ9a3wNH0TPJn5VI6jRSBwgVsACAAAAAAnJe4m9veJd4i4O63c85e7/gOz34a/bLwLoQMgUrmTnQAAzU3AH0AAAAFZAAgAAAAAP3j0MkZcpp0Z1m8xmJpCkJ8xpgJd+ZGRa2Tc8uBbDosBXMAIAAAAABsywRbmTFllnJxboM+QNkHDC21UYngIINS4LkPBd8JTQVsACAAAAAADnZUaMeEbWfR4S0iPhYK2YUDGCs5Yl4SbZIWCZiwfK4AAzU4AH0AAAAFZAAgAAAAAHiaNPI6DwoGAD+Fud3oxa41/yNZ5u0fpoH84p71EsjQBXMAIAAAAABpxUICEp53Gy1iJBjhQf3o4lpNmvCRCU90xy/zOI4RXQVsACAAAAAARD1qLjRHgF0BMupWci06EIOGiliFpWA/SDbS+V2pHDgAAzU5AH0AAAAFZAAgAAAAADN7ns6rQpslTqTOV7LYWe2cRTYZisGi9NFgifRkL9IJBXMAIAAAAADQvlEPP8LrmX6QB3Swr+2UvASsJUpS36BUweyo8kqr8gVsACAAAAAAs8v9wVLslLRVQDf0MlqWynivNCKRh39qXnJhlB44CwYAAzYwAH0AAAAFZAAgAAAAANlIGVl8rnGyWl0DorN67OXZtDwk86OnFIGORZODwjl7BXMAIAAAAAAJVePYotD4fym30XEnAlP5s13Fp2ivHmO4aMdeFobjiwVsACAAAAAA8l1VD0UsmzDCZjpOyeuQ2YJD45PQfkXjbc1auhne274AAzYxAH0AAAAFZAAgAAAAACAby2Yg9QGpBNizuGSsRhSUu+oCKDatyKnCcw8bJRnKBXMAIAAAAAC1n19Wc6X2lBMvqLxmmRe68oBOuBzPqnfB2WRlhU2E9gVsACAAAAAA4w2gkmXMglT61Dwi74P4PC0zlYXSOhohqHjTBK1fPAgAAzYyAH0AAAAFZAAgAAAAAFB1P2GzqCGe+KH7i1dkxhTB8sqAm6q9jAYu3lkpQIb1BXMAIAAAAAAmWeeXoSCHixBwgFlDkNh/TMefk7jXNMFYXoLR3e72nwVsACAAAAAAPsNSObIHf9RSP7dnvHdEdmsL9rJ5LBgug17z8P61IlUAAzYzAH0AAAAFZAAgAAAAALBlfYaY5s5jiDsa5Xf6njo14QcEUWUccOXSpDwvZdaFBXMAIAAAAABQAn3ch0rsP24yc8VQnjt6VjdWkvR5JaiTD024uMJn8wVsACAAAAAAx+6z2BAdsHP4uRVetjatprbvEIkkkKxmqliVy1LHjn0AAzY0AH0AAAAFZAAgAAAAAKG2NiBkMrCDrSFbko9l3c21DKLUzNaqzXeNE3qx+wf0BXMAIAAAAABZ/2zXjQuRZgC4G/r2GtA0mMXZ3mTlF35sSX8Ad3dhVwVsACAAAAAAAOePERUAksi4qNTEhk+GveTrQtedJUWyMIOQIB+VMIsAAzY1AH0AAAAFZAAgAAAAAKixJF2os5iGdsoCl7fZWGyBK14WQ6OVoeiwKwsMYfzeBXMAIAAAAAA8T2gjKDrq11fLH8Eg8Ln+nhEg1iM2U5OSZB53jaf2mQVsACAAAAAAfzLQUCEfq4FM3v6RU81KJ0oza8LGzWTMX6UKQqrOEWcAAzY2AH0AAAAFZAAgAAAAAH3VHyaaOF6RF2ijSe+wYJvtYg0oUqTVVUh3sE1OHH3pBXMAIAAAAADaOkHjOWq+UJwVJ6t/QLh6bOSQ3O/Ge4xkXqAwqXPPxwVsACAAAAAAnMe21o3iegRK+vfZry+EwIQrpnhI/lh+0OEodhsvWHgAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAATbW4A/////2OOjTfAh62+Ce3/3xNteAD/////Y46NN8CHrb4J7f9fAA==",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "decimal",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/decimal128-v2/mongocryptd-reply.json000066400000000000000000000035401521103432300277650ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "AxwBAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAvQAAAANlZGdlc0luZm8AiwAAABNsb3dlckJvdW5kAAAAAAAAAAAAAAAAAAAAQDAIbGJJbmNsdWRlZAABE3VwcGVyQm91bmQAFYHpffQQIhEAAAAAAABAMAh1YkluY2x1ZGVkAAETaW5kZXhNaW4A/////2OOjTfAh62+Ce3/3xNpbmRleE1heAD/////Y46NN8CHrb4J7f9fABBwYXlsb2FkSWQA0gQAABBmaXJzdE9wZXJhdG9yAAEAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "decimal",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-find-range/double-precision-v2/000077500000000000000000000000001521103432300250045ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/double-precision-v2/cmd.json000066400000000000000000000001661521103432300264450ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$numberDouble": "123.456"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-precision-v2/encrypted-field-map.json000066400000000000000000000013251521103432300315310ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "double",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               },
               "min": 0.0,
               "max": 200.0,
               "precision": 2
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-precision-v2/encrypted-payload.json000066400000000000000000000211641521103432300313270ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DRwVAAADcGF5bG9hZAC4FAAABGcApBQAAAMwAH0AAAAFZAAgAAAAABB+TYwDkWz8ouVl4Q4oIBElQKgGEDqMjPPSv+TZS8YABXMAIAAAAAAMLfd8ZW9xVgL8jqH0+wwdhD/Ktnb5fhh2ZM0iAVmS3QVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwAAzEAfQAAAAVkACAAAAAAffWVv+ysFys5eTN10LACdyWxHn/dCKMRWAQKzYmM5YEFcwAgAAAAAGDv6msNPvdhwK8sTlBu4iuoZyMtzmlAPYyKrR3b3KvGBWwAIAAAAAB61GI/ANIVYHOlZJaQ4pYzp8pK1EnxLI5uelzv27HynQADMgB9AAAABWQAIAAAAADwKL3PTbIxWwM3Ie8tK0sAdHbks7bZQ7k/+BtQb8hK1AVzACAAAAAA518WFoVWrUk+vd0h4ubURgpaBx5nuFnkUlPm7+Uv8j4FbAAgAAAAAOpTJTWFIUMsKaqzPZ2E+F5/MJKFRX9nbH7g/3FwbDyvAAMzAH0AAAAFZAAgAAAAAPeDZq0JZhU5dbRhsJu8i26uo0gCzLbXRuQv0qapUyUABXMAIAAAAABUYRvsvRAKNX88FMC9TKGV12jjrtOgqOUx3Aj2GyO4TgVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0AAzQAfQAAAAVkACAAAAAA1A4pQT0U6YKgy9rYhiDjktlclfRwylRHU68jAWaMsNgFcwAgAAAAAHp3IgqRDzqX/yPzN5ZwNh5UZFgp3RRLPpOjcts4EAqwBWwAIAAAAAAQsrQTEjSDj9bE2z6Z/5Rr6aJ7cy4w7d1o8a6QDbdVRgADNQB9AAAABWQAIAAAAABFCn58YDTy/VuFydht+w/S5teX3KDXxCZgx+Hax3wR0QVzACAAAAAAGF7v3ATOcTb3y23TbHJEXtuTtrY0f2TBWDVM9jn+VPUFbAAgAAAAAAOIZXl/9FE+TClKtkwYc0FJqubDKpX5JhcjvdBb8+afAAM2AH0AAAAFZAAgAAAAAGdbcKmZIVOCU80pgckeEkcL+93kExZCnO0hdAEvSwKeBXMAIAAAAAALbWpEG7Y/HtVTPWVb3kN+f/S3DbKr0BUG2RrfgeKMuAVsACAAAAAA/xLWzk4DlwlPogukD3gtqe4uxKX0GxEJYcG258VMxWsAAzcAfQAAAAVkACAAAAAAlEha7oMjYtH7Y+LT05oFkSZLYUifIGcaz3B/0l/bCIgFcwAgAAAAAGgOorOPXWrI59eXh1x+SYYepnFfICycaWAD3CwlFZZRBWwAIAAAAAAiwOi6zKj8Px7gELBSq8WGsq06elwYJKqk33igW1ZoLQADOAB9AAAABWQAIAAAAABNIe733XIhQXqiJNTdeGV8P48xU9Gy313Dcoj0bIOFuAVzACAAAAAAdCm75t1SwaYbXfPzqYDbibSWiSS4RwQimSSXivhnLYQFbAAgAAAAACn4x6fMcMNSQ488o0532pjr4xQ3NWdx6f7/2bkJxTjhAAM5AH0AAAAFZAAgAAAAACL9+rQRyywIXa5Pr7g2SnB0s0EjIct7PQtzjEkA69acBXMAIAAAAADz54imCCbu/qQkYP9wW2f5pHoBS+EyCe+xuDwC0UTiYgVsACAAAAAAKv602j4c3Bpn2t10qGl68eAD/fQsIH5lKMj8ANwrf7oAAzEwAH0AAAAFZAAgAAAAAKTK0NLhQ/+Y/HMxjRwBlXpXJAhAmCoWf1fReTegPnVpBXMAIAAAAAD7AlW+P4FfQS4r8d7EEvPVEP1diSbrVDBqg8ZvNl1XRAVsACAAAAAATTSEkff+/JMBjNwUciY2RQ6M66uMQMAtwU+UidDv1y4AAzExAH0AAAAFZAAgAAAAAGMbgPxi2Wu1AlqoDKTgyBnCZlnCjHm2naxRcizkIbYJBXMAIAAAAADMvSM3VZzVyRFCfUvcLXAXQFRIxlhm0t0dUsnaRZG4hgVsACAAAAAAI7uGriMAQc4A/a70Yi1Y7IAC7o/mfNYf7/FvwELYf80AAzEyAH0AAAAFZAAgAAAAAPnZ1bdmrcX0fsSxliuSqvDbRqwIiVg0tYp0PViRX0nOBXMAIAAAAAAqBdZGg9O74mnwyQF+lILtyzHdLOErDjPSf9sM8EqCugVsACAAAAAAwhuDsz+fCtqY8mW8QvEVQERjDChwrYTw4y7dinlCCOMAAzEzAH0AAAAFZAAgAAAAAJ40Dmb5BUT1AlWjfXB43nIbJgDn9rBg9FAeYR80WK0vBXMAIAAAAAAMPqLMDdNmnKzA3Hq49/NkJfs+/cjnyjSAbmiOFUE5FgVsACAAAAAAxbi7ql49Y4pduqWlLJqpwimRzrEnC7w5fWaMBiinHL8AAzE0AH0AAAAFZAAgAAAAAGelnhqWM2gUVy4P5QE/2Zfd7s9BugPqB/tcnSsFg5X0BXMAIAAAAAAWUhif3G+NMvZ3YPLB5OMuIhfPEu6U8KR9gTvJFz5uIwVsACAAAAAADEs8/aVSj2sJjxjv1K7o/aH8vZzt1bga73YiIKUx5DYAAzE1AH0AAAAFZAAgAAAAAD1xX2wCyf1aK1MoXnBAPfWLeBxsJI2i06tWbuiYKgElBXMAIAAAAACW1NW4RibvY0JRUzPvCmKnVbEy8AIS70fmsY08WgJOEgVsACAAAAAAQq9eIVoLcd4WxXUC3vub+EnxmcI2uP/yUWr3cz0jv9EAAzE2AH0AAAAFZAAgAAAAAHwU1LYeJmTch640sTu3VRRRdQg4YZ7S9IRfVXWHEWU8BXMAIAAAAACozWKD2YlqbQiBVVwJKptfAVM+R2FPJPtXkxVFAhHNXQVsACAAAAAAn7LS0QzTv9sOJzxH0ZqxsLYBYoArEo/PIXkU/zTnpM0AAzE3AH0AAAAFZAAgAAAAAHKaToAsILpmJyCE02I1iwmF/FibqaOb4b5nteuwOayfBXMAIAAAAABPxYjSK5DKgsdUZrZ+hM6ikejPCUK6Rqa0leoN7KOM0QVsACAAAAAAH9rPq5vvOIe9nTAcM1W1dVhQZ+gSkBohgoWLPcZnQXcAAzE4AH0AAAAFZAAgAAAAANTGiHqJVq28n7mMZsJD6gHxVQp1A6z8wgZVW+xV/lhmBXMAIAAAAABCR4BfdNVy7WE+IyQ312vYuIW0aGcXxr2II/MbNz8ZdAVsACAAAAAAng0GYpYJTypRLQUd5tIXWaAjZX5na04T/BypmwwrXPoAAzE5AH0AAAAFZAAgAAAAABooumzjEqp9Hvvd+sn1L82NI2iUGRl0nXQNJTHM7oyVBXMAIAAAAADgjz5L2ursK4C+pXXsJ6XHABhyallj9s/vSUgxXvjiiwVsACAAAAAAPjlAM0tbO6EUmLAeIZt57YMkMsuQfuC3T3d9vtnxgjwAAzIwAH0AAAAFZAAgAAAAAMA4jmE8U2uGkYUeKoYSlb22tfrRq2VlhV1Jq1kn4hV9BXMAIAAAAADG4fLeJUcINPSb1pMfAASJkuYsgS/59Eq/51mET/Y7RQVsACAAAAAAmwwcWOnzvpxm4pROXOL+BlxjEG/7v7hIautb2ubFT44AAzIxAH0AAAAFZAAgAAAAAK8/E3VHzHM6Kjp39GjFy+ci1IiUG5oxh0W6elV+oiX2BXMAIAAAAAA4/F4Q94xxb2TvZcMcji/DVTFrZlH8BL/HzD86RRmqNAVsACAAAAAAif3HPf6B1dTX/W+Vlp6ohadEQk/GAmHYzXfJia2zHeIAAzIyAH0AAAAFZAAgAAAAAGUX9ttLN1cCrOjlzsl/E6jEzQottNDw8Zo94nbO1133BXMAIAAAAAA7uVthFvXH+pbBrgQmnkPcpiHFEVCAi0WA7sAt9tlt3gVsACAAAAAAznaMStSbtGXU1Pb5z9KDTvEd79s6gmWYCKOKdzeijpEAAzIzAH0AAAAFZAAgAAAAAKnT/qg8N85Q9EQvpH7FBqUooxHFgrIjqLlIDheva2QSBXMAIAAAAABGAKkFMKoSIrvClWF7filoYM6fI9xSqOJVNS3dv4lxYwVsACAAAAAAgITE31hQA4ZOxpUFYSYv0mzWbd/6RKgbUXiUY96fBQEAAzI0AH0AAAAFZAAgAAAAAHRDRDT2hJrJ8X9zB9ELT28q8ZsfkYr92chaZYakiLlqBXMAIAAAAAAT0Le67ObldDta/Qb17dYfdslPsJTfGj3bWAgC0JIingVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoAAzI1AH0AAAAFZAAgAAAAAOOJcUjYOE0KqcYS1yZ363zglQXfr3XSD+R5fWLSivDoBXMAIAAAAABjeLe+tg37lNa+DdVxtlCtY77tV9PqfJ5X4XEKrfwu0AVsACAAAAAAlbpHiQAPLLTvSF+u58RBCLnYQKB5wciIQmANV9bkzsoAAzI2AH0AAAAFZAAgAAAAAMwWOOaWDDYUusdA1nyoaEB3C4/9GRpFNGags95Ddp4LBXMAIAAAAACLrsQXGWK15fW4mPEUXJ/90by13aG+727qWJep8QJ/WgVsACAAAAAAuThwsAsKUB56QAXC0MjJsZ9736atbiHPlK2tE0urf9QAAzI3AH0AAAAFZAAgAAAAABPRXBK0z8UANcvMDWntBjN9yF7iGMPLbhbaKrvHwcplBXMAIAAAAACZlqWsYPIb+ydmH03BxD3TqSGsSNoI7EVCy0VgW0TpYgVsACAAAAAAD2uaBv8oc7l4EeC5PWx5sfeyGZoas0JdFJ33M3jjgjMAAzI4AH0AAAAFZAAgAAAAAOn9/6pbzjIxFEApugaVOvVKXq23sDCJELv5UtLPDZI3BXMAIAAAAACHIwSDTlof0vFoigF4drbeM/8rdlj/4U386zQsNLtPGwVsACAAAAAAsYt/rXnpL55J9rlWSFRA4seaU6ggix7RgxbrJPu6gO4AAzI5AH0AAAAFZAAgAAAAAIMCESykv5b5d6mYjU5DlnO709lOFCaNoJBLtzBIqmg4BXMAIAAAAADs1Bfuaun4Es3nQ4kr29BzheLRDcFv+9a0gOGkSEcrDgVsACAAAAAA5kW6i/jOBSdoGAsZEZxVNRvt6miv86bP8JfUT+1KJg8AAzMwAH0AAAAFZAAgAAAAAFSPmr27XgKhUkbEvvC6Br5K1w7280NZrrhdzfYF+YGjBXMAIAAAAADv2h+Xq6kM7MHYTLMACRwbe2MzGHu4sdB67FGzDR6H4QVsACAAAAAAKII0MMC7o6GKVfGo2qBW/p35NupBp7MI6Gp0zXYwJOcAAzMxAH0AAAAFZAAgAAAAAPSV9qprvlNZK6OSQZNxKhJmBMs6QCKFESB/oeIvAS0iBXMAIAAAAAA835Jh22/pvZgKoYH6KjE+RRpYkaM1G35TWq6uplk/rgVsACAAAAAA162IdSb079yVlS7GkuSdHU3dOw03a+NS55ZPVBxbD08AAzMyAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzMzAH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzM0AH0AAAAFZAAgAAAAAKJY+8+7psFzJb5T+Mg9UWb6gA9Y8NN9j/ML2jZkNDNPBXMAIAAAAAA2R/nCtSYfCim89BzdUPS+DTQGwYDk+2ihFPEBS8h+ygVsACAAAAAAaEQra7xyvA3JS0BasIpRVrz7ZXsp6RpH7OpfJBFzFG8AAzM1AH0AAAAFZAAgAAAAAI4qr+sJiRaqwZRhnenAzD7tTKq+jP1aaLyAln3w1HQuBXMAIAAAAADNYpqV73NpwN+Ta0ms1SRiu+6WNOOdGT+syghL+JAFhQVsACAAAAAAN07Fo9SK+fXp5Odk1J806pyVWc2WHXCtb1gJQknTgqsAAzM2AH0AAAAFZAAgAAAAAISgN1Hid7IWvDESN/3tywFZiBsZPYapOUx9/QjDDxLfBXMAIAAAAAA7lxpEz3+CGdv6/WKIAlIwRYURREKgn7+StwNoVekkDwVsACAAAAAAx+Oa2v1e1R7VomfsvcKO8VkY4eTl7LzjNQQL6Cj6GBQAAzM3AH0AAAAFZAAgAAAAAOTLdk1RIUzCsvK7xCXy+LxGhJf87fEL406U9QKta3JRBXMAIAAAAAD8+6UnUn8sN6AgQuuf7uFxW+2ZJNpZLgp3eKVtjbo9ewVsACAAAAAAQN3mZHmaDM0ZbUnk2O/+wCUjiCs4bnshfHjd/4ygLXcAAzM4AH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzM5AH0AAAAFZAAgAAAAADtbVEI2tdkrowEMdkacD2w0Y3T3Ofi7PH6HmA6sP0c/BXMAIAAAAADuBSROnZHA+NgUPH8d0LnWFiDsM2bY8bzjC1+elSsIygVsACAAAAAAR0G2m+uANoWknkr/NerFcG+fECVxNIs0cqbY1t/U/0MAAzQwAH0AAAAFZAAgAAAAAAh3WpeMVlikPFYj9hLj+fmIqVt6omCSF75W3TPExyWpBXMAIAAAAAAsQkRmwqeVj2gGE03orb6PtrIzDt6dDU3hgSQi8E2wKgVsACAAAAAA3GHaRE2RAcaBRd8VzmYzWeBD2Gmy91eTK1k8YdWObZcAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYABgAAAAFtbgAAAAAAAAAAAAFteAAAAAAAAABpQAA=",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "double",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            },
                            "min": {
                                "$numberDouble": "0.0"
                            },
                            "max": {
                                "$numberDouble": "200.0"
                            },
                            "precision": {
                                "$numberInt": "2"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-precision-v2/mongocryptd-reply.json000066400000000000000000000037261521103432300314050ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "AwsBAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YArAAAAANlZGdlc0luZm8AegAAAAFsb3dlckJvdW5kAAAAAAAAAAAACGxiSW5jbHVkZWQAAQF1cHBlckJvdW5kAAAAAAAAAGlACHViSW5jbHVkZWQAARBwcmVjaXNpb24AAgAAAAFpbmRleE1pbgAAAAAAAAAAAAFpbmRleE1heAAAAAAAAABpQAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAABJjbQAAAAAAAAAAABJzAAEAAAAAAAAAAA==",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "double",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                },
                                "min": 0.0,
                                "max": 200.0,
                                "precision": 2
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-v2/000077500000000000000000000000001521103432300230135ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/double-v2/cmd.json000066400000000000000000000001661521103432300244540ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$numberDouble": "123.456"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-v2/encrypted-field-map.json000066400000000000000000000011761521103432300275440ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "double",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-v2/encrypted-payload.json000066400000000000000000000206401521103432300273340ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DZUVAAADcGF5bG9hZAA5FQAABGcAJRUAAAMwAH0AAAAFZAAgAAAAAJam7JYsZe2cN20ZYm2W3v1pisNt5PLiniMzymBLWyMtBXMAIAAAAABxCsKVMZMTn3n+R2L7pVz5nW804r8HcK0mCBw3jUXKXAVsACAAAAAA7j3JGnNtR64P4dJLeUoScFRGfa8ekjh3dvhw46sRFk0AAzEAfQAAAAVkACAAAAAAxetfHSxpn7mA5GbAzYUu5kbqK47ZhL7UgY+gC0MqgoEFcwAgAAAAAMhCzEKvwJ8UpoFzHyQh5LLCcaHskeMpEn28l33Hv3LvBWwAIAAAAABy8FX5LKQNiGZEXJVjAVgH2gEoFIevnkl3PZxd6jCxRAADMgB9AAAABWQAIAAAAAAm83FA9yDUpwkbKTihe7m53u+DivS9BU2b4vQMtCVQ2AVzACAAAAAAz3m1UB/AbZPa4QSKFDnUgHaT78+6iGOFAtouiBorEgEFbAAgAAAAAIgbpyYtJj5513Z5XYqviH/HXG/5+mqR52iBbfqMmDtZAAMzAH0AAAAFZAAgAAAAACUc2CtD1MK/UTxtv+8iA9FoHEyTwdl43HKeSwDw2Lp5BXMAIAAAAACCIduIdw65bQMzRYRfjBJj62bc69T4QqH4QoWanwlvowVsACAAAAAAM0TV7S+aPVVzJOQ+cpSNKHTwyQ0mWa8tcHzfk3nR+9IAAzQAfQAAAAVkACAAAAAAdJodaz92eYj2xzucHnRUHYHOzSQdqkwcJB3JURhjfRMFcwAgAAAAAKRXbaWESNa//38O5t8cEBzAmCAojmYhtIrkvafKqWipBWwAIAAAAABLX2bsNxxDGCvI6wEncawKzfvJ9Co2LUb5Lp8JekSGfgADNQB9AAAABWQAIAAAAAAC/I4TQRtCl12YZmdGz17X4GqSQgfwCPgRBwdHmdwu+QVzACAAAAAAx8f3z2ut/RAZhleari4vCEE+tNIn4ikjoUwzitfQ588FbAAgAAAAAJci0w1ZB8W2spJQ+kMpod6HSCtSR2jrabOH+B0fj3A4AAM2AH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzcAfQAAAAVkACAAAAAAolj7z7umwXMlvlP4yD1RZvqAD1jw032P8wvaNmQ0M08FcwAgAAAAADZH+cK1Jh8KKbz0HN1Q9L4NNAbBgOT7aKEU8QFLyH7KBWwAIAAAAABoRCtrvHK8DclLQFqwilFWvPtleynpGkfs6l8kEXMUbwADOAB9AAAABWQAIAAAAACOKq/rCYkWqsGUYZ3pwMw+7Uyqvoz9Wmi8gJZ98NR0LgVzACAAAAAAzWKale9zacDfk2tJrNUkYrvuljTjnRk/rMoIS/iQBYUFbAAgAAAAADdOxaPUivn16eTnZNSfNOqclVnNlh1wrW9YCUJJ04KrAAM5AH0AAAAFZAAgAAAAAISgN1Hid7IWvDESN/3tywFZiBsZPYapOUx9/QjDDxLfBXMAIAAAAAA7lxpEz3+CGdv6/WKIAlIwRYURREKgn7+StwNoVekkDwVsACAAAAAAx+Oa2v1e1R7VomfsvcKO8VkY4eTl7LzjNQQL6Cj6GBQAAzEwAH0AAAAFZAAgAAAAAOTLdk1RIUzCsvK7xCXy+LxGhJf87fEL406U9QKta3JRBXMAIAAAAAD8+6UnUn8sN6AgQuuf7uFxW+2ZJNpZLgp3eKVtjbo9ewVsACAAAAAAQN3mZHmaDM0ZbUnk2O/+wCUjiCs4bnshfHjd/4ygLXcAAzExAH0AAAAFZAAgAAAAAFH9l9GGA1I52atJV5jNUf1lx8jBjoEoVoME97v5GFJiBXMAIAAAAAC1qH3Kd78Dr9NGbw7y9D/XYBwv5h1LLO8la5OU7g8UkQVsACAAAAAArZ6atJCYrVfHB8dSNPOFf6nnDADBMJcIEj8ljPvxHp8AAzEyAH0AAAAFZAAgAAAAAPLX4XT1eMfokMvj73G6loHEotbdivVFM6cpMbU0zIOmBXMAIAAAAABuTqwm6E60kVBN5iClzLnMBozIQRYjMozzRNKVhixkEAVsACAAAAAAjvY9G0Of8EQcZ4GVfSEVz7jrNn7i4qps2r82jJmngKoAAzEzAH0AAAAFZAAgAAAAAGzGJAUZBcVKRb4bCSNaRxtcDH2TqIgHqMElD9RL7SzDBXMAIAAAAABbJfrLwBrqZ2Ylm9QfL7nkW+GJ8vTlaeMUDT5620ebaAVsACAAAAAASiaS1IlBls5Tan57XqqbR1cuvyOcoSibJJQGREzm4c0AAzE0AH0AAAAFZAAgAAAAAC028abAppwE/ApZHU5RbzZZ8OPD5eJ8/6+NgiSFf4d+BXMAIAAAAAD3THvDUYWULR+AVLuRRPPAMVMeZ2ldWpBYSODboszWbQVsACAAAAAAATOaeYj+kx3MTDeNUcKGbUxLZDeMjC8JrWnlHmWTamQAAzE1AH0AAAAFZAAgAAAAAHWr8wQYIKLiKeb3wd8kZQuXD/GUHDqXj12K/EQWV11CBXMAIAAAAADo3aFHDuyfls9tcWCxlFqJn4zDXd3WT9CIFYFjJnTYswVsACAAAAAAeMbIatR7DgefzuvF4WyNVDjJxP8KPA6U/rmMQIBvpM0AAzE2AH0AAAAFZAAgAAAAAMdRi6AAjF1Z9ucMqYl2Ud1PLUGOlOPJFgSrPTjs27u8BXMAIAAAAAAqOdI7+P8srvqCTFadwMM3iggaVOGcf1BB0EjBYeV6RAVsACAAAAAAU+V2GrqgxJYs9mxuak/8JMFICXwQ2vksrBdOvSwWFpoAAzE3AH0AAAAFZAAgAAAAADKKe++fqh4sn0a8Bb+w3QMFnOqSE5hDI3zGQTcmJGcOBXMAIAAAAAC8ebHa++JmxVISv6LzjuMgEZqzKSZlJyujnSV9syRD9AVsACAAAAAAQcVNSjyetScLu78IrAYaAigerY4kWtnbctmIyb19Wa4AAzE4AH0AAAAFZAAgAAAAAMKoHwhZcocaQy7asIuRG8+P1qPENgFAwzc3X1gZWYnJBXMAIAAAAAB+R01s+WdJjLa5p7STuEylradWr+2JDxsWx9bKDgXNDQVsACAAAAAADeXTBHsm+FH2pQVoqOBPPIJiTJLqrzGisNnQ3S3xYJAAAzE5AH0AAAAFZAAgAAAAAF41XuyBvREKcxjDl+wbnillseykpAjCKHmwIu+RNvM7BXMAIAAAAAC2Wzq+2mfO7howoOZxquqvOuH1D2WdlzA1nK+LUp0FMgVsACAAAAAARha+D6DVeDxSjNyXXO5DMY+W70EGyfc7gxR4TjzcYusAAzIwAH0AAAAFZAAgAAAAAAfONgdhLPEjvsMxTY9K4//7WjREuRmZ6Bpcf3yvdMf3BXMAIAAAAABCy/zjmzucxQkbJ96l5vS5x6SeyHE0Z+Aqp9oZgBcC6QVsACAAAAAAasG/uN4DnWHZLkLhH4cMzXk5F/HL2D+72WH+1jjgH8UAAzIxAH0AAAAFZAAgAAAAAA5ZsebFm5NrSGs2E17+fUt4qkzsVmy4IJA5nGehtSBVBXMAIAAAAAAOzteKfp+YGPqn1fi8u/lKXP7E2Zgouwgt6KAADHX9AQVsACAAAAAA2+FaAbl8JZogfNCI0FFbmZZPy/KLF1u16FGrPspSbEIAAzIyAH0AAAAFZAAgAAAAAHf6LIjrvy6I31w/8b910U9qU8cBIYiWn9mW55NYZF8VBXMAIAAAAACONPisRtnFG9vV2mTQ3hRR/hGuVRA9dGd9Lt9JqDoM8wVsACAAAAAA+h7V/jIYJcd0ALIvFBlwxkFqWxBVlkqT9wFkmumr4QcAAzIzAH0AAAAFZAAgAAAAAGSuCqGwDiCbKHUv5r+dNZHRupcQLFq+vnesTVMAAP9eBXMAIAAAAAA86UcdE9/45JJhoIKu7KXHb4CpP8laA0ga2+az4ydybAVsACAAAAAAqpiTVy3VNE3SSSzdkVQqCL5FKQR+jEzMUZCYGL92yloAAzI0AH0AAAAFZAAgAAAAAEeRZdjMb3fthvbV8aszi4gMScRnknrj6oMIP5QOG5wKBXMAIAAAAACl7skGzK2xwd7ZLGfv+3ODOfP2UQGEe+vGDZphWbFjGAVsACAAAAAAlQo0yTqQl/6qOSqTYU9an/9a5MgHqZRIkdmmx/pycGYAAzI1AH0AAAAFZAAgAAAAAGs3H8zIeyQeEbORy4WJZi3orD6mbEdHAoGlx2LbjYB7BXMAIAAAAABRBtcXvCZ0k/N0bTks4FqPjnI6y5WEjwmee3IPOV2dhgVsACAAAAAAzgOebXiyusjNVeww8k/dZzV+DJl1IAQaeS42s6txDwcAAzI2AH0AAAAFZAAgAAAAAJF9jmuG6akwbUyFRneEJSwJVAnAyBP8MVBWmp5HdLbsBXMAIAAAAACAsVaCKlRHXUrxy3CIFsgj0oYjHgUL84xcKlimxv3uIgVsACAAAAAAWAD9GQ0gke6gcBa9j92kL7YCuCyDKKScwCsIEznx4HgAAzI3AH0AAAAFZAAgAAAAAC5EctBiSJ12CwPnII7OjMm9GvhrvYYF4R74SzaBln3JBXMAIAAAAADWwC8rfcwkr8Jy2lgndv8tnqAe6pFk779S3bvK+NCT7gVsACAAAAAA+BZeW1ebM+IrCBPW1/pZgKfmgCmmYFWFWXlX6yLZAmQAAzI4AH0AAAAFZAAgAAAAAIrSpJ1cjxXbMxyFfJ+ztUZa4BGAaVTG+SRbw7qEckvMBXMAIAAAAAA1DyadE9lSSVg/I1M8CK7xcD+NQaxF7L2XEEe6bIB39QVsACAAAAAAZ/yInspF9w++BmD0WLl6YfT8oB7fciVDrHu2M2eJ05MAAzI5AH0AAAAFZAAgAAAAACEIYgnZe7xTrbKvBHuu7ZHdcmZsfXopLpYrCqhxpQ9kBXMAIAAAAAAVl7hCDcjYI88JrAeaXoACQ9vQhwhvjaXPjz8HHlUXogVsACAAAAAAAfevclz71zhEQVqYs9GnDd0tn0ARsuQCm3rVbFpQVpUAAzMwAH0AAAAFZAAgAAAAAC94G4jKC+sRuH2ktMk+0SRIgVsz5tFyiDuajvuMPmGNBXMAIAAAAABXXwxmV94HTdMrvVUMQa/qHStGXPZGM+ic5YU2WZ9KRgVsACAAAAAA9ROGdVxtVKO4zf19Fo5HfI25J7YY24uFACegiF6bvHcAAzMxAH0AAAAFZAAgAAAAAIbVVUtt1BfyCKUTjc/VQqEhPe9SlmOSPGiijNCRDBW2BXMAIAAAAAB9E2S6JTwBNH7R7Vd4qifWVMA18A3DIOkMRGh8kLvzbAVsACAAAAAARSj19dZx9lpIHMA89PB9zs1IF1ear7StdA3160Hu838AAzMyAH0AAAAFZAAgAAAAANkkkMXM5+z/4ZZuAGTYNZ44KEgSJPVNJLc4XYrKr4uGBXMAIAAAAABUBR7tc6UpCOLAuPTW5qX368w6cQpanWy3HiOculRhlgVsACAAAAAADZ71+l66zHZSDmn4MImNMOG0lsSkdSMqx9KaB2m6v3IAAzMzAH0AAAAFZAAgAAAAALQbgBgLdz/MMx4k//Kn62hpxlgo2nvv796edsd4q/2BBXMAIAAAAADG0vQlJddwGQg6XGyfya512ig6OPRvXArahFSlnaykcwVsACAAAAAAa8fk8XBlKzXzMxqGwbIT0w/xg3w0IFeS39wBHamlVckAAzM0AH0AAAAFZAAgAAAAACTx2/UKpZD/ivss6cC2wawKnquaPH1YFmtMZiNd1UjuBXMAIAAAAACfaVAm3FeWBxz/aFE6MybVQfNHasdlSNxl5g+/U9UY/wVsACAAAAAAoXz2pLDpB7CrxoNUJediSkn347wcgYW495pq1wl1wHYAAzM1AH0AAAAFZAAgAAAAAPa4yKTtkUtySuWo1ZQsp2QXtPb5SYqzA5vYDnS1P6c0BXMAIAAAAADKnF58R1sXlHlsHIvCBR3YWW/qk54z9CTDhZydkD1cOQVsACAAAAAAHW3ERalTFWKMzjuXF3nFh0pSrQxM/ojnPbPhc4v5MaQAAzM2AH0AAAAFZAAgAAAAAN5WJnMBmfgpuQPyonmY5X6OdRvuHw4nhsnGRnFAQ95VBXMAIAAAAACwftzu7KVV1rmGKwXtJjs3cJ1gE3apr8+N0SAg1F2cHwVsACAAAAAATDW0reyaCjbJuVLJzbSLx1OBuBoQu+090kgW4RurVacAAzM3AH0AAAAFZAAgAAAAACHvDsaPhoSb6DeGnKQ1QOpGYAgK82qpnqwcmzSeWaJHBXMAIAAAAABRq3C5+dOfnkAHM5Mg5hPB3O4jhwQlBgQWLA7Ph5bhgwVsACAAAAAAqkC8zYASvkVrp0pqmDyFCkPaDmD/ePAJpMuNOCBhni8AAzM4AH0AAAAFZAAgAAAAAOBePJvccPMJmy515KB1AkXF5Pi8NOG4V8psWy0SPRP+BXMAIAAAAAB3dOJG9xIDtEKCRzeNnPS3bFZepMj8UKBobKpSoCPqpgVsACAAAAAAPG3IxQVOdZrr509ggm5FKizWWoZPuVtOgOIGZ3m+pdEAAzM5AH0AAAAFZAAgAAAAABUvRrDQKEXLMdhnzXRdhiL6AGNs2TojPky+YVLXs+JnBXMAIAAAAAD1kYicbEEcPzD4QtuSYQQWDPq8fuUWGddpWayKn3dT9QVsACAAAAAA9+Sf7PbyFcY45hP9oTfjQiOUS3vEIAT8C0vOHymwYSUAAzQwAH0AAAAFZAAgAAAAAOvSnpujeKNen4pqc2HR63C5s5oJ1Vf4CsbKoYQvkwl5BXMAIAAAAACw2+vAMdibzd2YVVNfk81yXkFZP0WLJ82JBxJmXnYE+QVsACAAAAAArQ/E1ACyhK4ZyLqH9mNkCU7WClqRQTGyW9tciSGG/EMAAzQxAH0AAAAFZAAgAAAAAAo0xfGG7tJ3GWhgPVhW5Zn239nTD3PadShCNRc9TwdNBXMAIAAAAADZh243oOhenu0s/P/5KZLBDh9ADqKHtSWcXpO9D2sIjgVsACAAAAAAlgTPaoQKz+saU8rwCT3UiNOdG6hdpjzFx9GBn08ZkBEAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAABbW4AAAAAAAAAEAABbXgA////////738A",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "double",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/double-v2/mongocryptd-reply.json000066400000000000000000000034701521103432300274100ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "A/wAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAnQAAAANlZGdlc0luZm8AawAAAAFsb3dlckJvdW5kAAAAAAAAABAACGxiSW5jbHVkZWQAAQF1cHBlckJvdW5kAP///////+9/CHViSW5jbHVkZWQAAQFpbmRleE1pbgAAAAAAAAAQAAFpbmRleE1heAD////////vfwAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAABJjbQAAAAAAAAAAABJzAAEAAAAAAAAAAA==",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "double",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int32-v2/000077500000000000000000000000001521103432300225005ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/int32-v2/cmd.json000066400000000000000000000001621521103432300241350ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$numberInt": "123456"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int32-v2/encrypted-field-map.json000066400000000000000000000011731521103432300272260ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int32-v2/encrypted-payload.json000066400000000000000000000211011521103432300270120ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DQ4WAAADcGF5bG9hZAC6FQAABGcAphUAAAMwAH0AAAAFZAAgAAAAABB+TYwDkWz8ouVl4Q4oIBElQKgGEDqMjPPSv+TZS8YABXMAIAAAAAAMLfd8ZW9xVgL8jqH0+wwdhD/Ktnb5fhh2ZM0iAVmS3QVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwAAzEAfQAAAAVkACAAAAAAffWVv+ysFys5eTN10LACdyWxHn/dCKMRWAQKzYmM5YEFcwAgAAAAAGDv6msNPvdhwK8sTlBu4iuoZyMtzmlAPYyKrR3b3KvGBWwAIAAAAAB61GI/ANIVYHOlZJaQ4pYzp8pK1EnxLI5uelzv27HynQADMgB9AAAABWQAIAAAAADwKL3PTbIxWwM3Ie8tK0sAdHbks7bZQ7k/+BtQb8hK1AVzACAAAAAA518WFoVWrUk+vd0h4ubURgpaBx5nuFnkUlPm7+Uv8j4FbAAgAAAAAOpTJTWFIUMsKaqzPZ2E+F5/MJKFRX9nbH7g/3FwbDyvAAMzAH0AAAAFZAAgAAAAAPeDZq0JZhU5dbRhsJu8i26uo0gCzLbXRuQv0qapUyUABXMAIAAAAABUYRvsvRAKNX88FMC9TKGV12jjrtOgqOUx3Aj2GyO4TgVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0AAzQAfQAAAAVkACAAAAAA1A4pQT0U6YKgy9rYhiDjktlclfRwylRHU68jAWaMsNgFcwAgAAAAAHp3IgqRDzqX/yPzN5ZwNh5UZFgp3RRLPpOjcts4EAqwBWwAIAAAAAAQsrQTEjSDj9bE2z6Z/5Rr6aJ7cy4w7d1o8a6QDbdVRgADNQB9AAAABWQAIAAAAABFCn58YDTy/VuFydht+w/S5teX3KDXxCZgx+Hax3wR0QVzACAAAAAAGF7v3ATOcTb3y23TbHJEXtuTtrY0f2TBWDVM9jn+VPUFbAAgAAAAAAOIZXl/9FE+TClKtkwYc0FJqubDKpX5JhcjvdBb8+afAAM2AH0AAAAFZAAgAAAAAGdbcKmZIVOCU80pgckeEkcL+93kExZCnO0hdAEvSwKeBXMAIAAAAAALbWpEG7Y/HtVTPWVb3kN+f/S3DbKr0BUG2RrfgeKMuAVsACAAAAAA/xLWzk4DlwlPogukD3gtqe4uxKX0GxEJYcG258VMxWsAAzcAfQAAAAVkACAAAAAAlEha7oMjYtH7Y+LT05oFkSZLYUifIGcaz3B/0l/bCIgFcwAgAAAAAGgOorOPXWrI59eXh1x+SYYepnFfICycaWAD3CwlFZZRBWwAIAAAAAAiwOi6zKj8Px7gELBSq8WGsq06elwYJKqk33igW1ZoLQADOAB9AAAABWQAIAAAAABNIe733XIhQXqiJNTdeGV8P48xU9Gy313Dcoj0bIOFuAVzACAAAAAAdCm75t1SwaYbXfPzqYDbibSWiSS4RwQimSSXivhnLYQFbAAgAAAAACn4x6fMcMNSQ488o0532pjr4xQ3NWdx6f7/2bkJxTjhAAM5AH0AAAAFZAAgAAAAACL9+rQRyywIXa5Pr7g2SnB0s0EjIct7PQtzjEkA69acBXMAIAAAAADz54imCCbu/qQkYP9wW2f5pHoBS+EyCe+xuDwC0UTiYgVsACAAAAAAKv602j4c3Bpn2t10qGl68eAD/fQsIH5lKMj8ANwrf7oAAzEwAH0AAAAFZAAgAAAAAKTK0NLhQ/+Y/HMxjRwBlXpXJAhAmCoWf1fReTegPnVpBXMAIAAAAAD7AlW+P4FfQS4r8d7EEvPVEP1diSbrVDBqg8ZvNl1XRAVsACAAAAAATTSEkff+/JMBjNwUciY2RQ6M66uMQMAtwU+UidDv1y4AAzExAH0AAAAFZAAgAAAAAGMbgPxi2Wu1AlqoDKTgyBnCZlnCjHm2naxRcizkIbYJBXMAIAAAAADMvSM3VZzVyRFCfUvcLXAXQFRIxlhm0t0dUsnaRZG4hgVsACAAAAAAI7uGriMAQc4A/a70Yi1Y7IAC7o/mfNYf7/FvwELYf80AAzEyAH0AAAAFZAAgAAAAAPnZ1bdmrcX0fsSxliuSqvDbRqwIiVg0tYp0PViRX0nOBXMAIAAAAAAqBdZGg9O74mnwyQF+lILtyzHdLOErDjPSf9sM8EqCugVsACAAAAAAwhuDsz+fCtqY8mW8QvEVQERjDChwrYTw4y7dinlCCOMAAzEzAH0AAAAFZAAgAAAAAJ40Dmb5BUT1AlWjfXB43nIbJgDn9rBg9FAeYR80WK0vBXMAIAAAAAAMPqLMDdNmnKzA3Hq49/NkJfs+/cjnyjSAbmiOFUE5FgVsACAAAAAAxbi7ql49Y4pduqWlLJqpwimRzrEnC7w5fWaMBiinHL8AAzE0AH0AAAAFZAAgAAAAAGelnhqWM2gUVy4P5QE/2Zfd7s9BugPqB/tcnSsFg5X0BXMAIAAAAAAWUhif3G+NMvZ3YPLB5OMuIhfPEu6U8KR9gTvJFz5uIwVsACAAAAAADEs8/aVSj2sJjxjv1K7o/aH8vZzt1bga73YiIKUx5DYAAzE1AH0AAAAFZAAgAAAAAD1xX2wCyf1aK1MoXnBAPfWLeBxsJI2i06tWbuiYKgElBXMAIAAAAACW1NW4RibvY0JRUzPvCmKnVbEy8AIS70fmsY08WgJOEgVsACAAAAAAQq9eIVoLcd4WxXUC3vub+EnxmcI2uP/yUWr3cz0jv9EAAzE2AH0AAAAFZAAgAAAAAHwU1LYeJmTch640sTu3VRRRdQg4YZ7S9IRfVXWHEWU8BXMAIAAAAACozWKD2YlqbQiBVVwJKptfAVM+R2FPJPtXkxVFAhHNXQVsACAAAAAAn7LS0QzTv9sOJzxH0ZqxsLYBYoArEo/PIXkU/zTnpM0AAzE3AH0AAAAFZAAgAAAAAHKaToAsILpmJyCE02I1iwmF/FibqaOb4b5nteuwOayfBXMAIAAAAABPxYjSK5DKgsdUZrZ+hM6ikejPCUK6Rqa0leoN7KOM0QVsACAAAAAAH9rPq5vvOIe9nTAcM1W1dVhQZ+gSkBohgoWLPcZnQXcAAzE4AH0AAAAFZAAgAAAAANTGiHqJVq28n7mMZsJD6gHxVQp1A6z8wgZVW+xV/lhmBXMAIAAAAABCR4BfdNVy7WE+IyQ312vYuIW0aGcXxr2II/MbNz8ZdAVsACAAAAAAng0GYpYJTypRLQUd5tIXWaAjZX5na04T/BypmwwrXPoAAzE5AH0AAAAFZAAgAAAAABooumzjEqp9Hvvd+sn1L82NI2iUGRl0nXQNJTHM7oyVBXMAIAAAAADgjz5L2ursK4C+pXXsJ6XHABhyallj9s/vSUgxXvjiiwVsACAAAAAAPjlAM0tbO6EUmLAeIZt57YMkMsuQfuC3T3d9vtnxgjwAAzIwAH0AAAAFZAAgAAAAAMA4jmE8U2uGkYUeKoYSlb22tfrRq2VlhV1Jq1kn4hV9BXMAIAAAAADG4fLeJUcINPSb1pMfAASJkuYsgS/59Eq/51mET/Y7RQVsACAAAAAAmwwcWOnzvpxm4pROXOL+BlxjEG/7v7hIautb2ubFT44AAzIxAH0AAAAFZAAgAAAAAK8/E3VHzHM6Kjp39GjFy+ci1IiUG5oxh0W6elV+oiX2BXMAIAAAAAA4/F4Q94xxb2TvZcMcji/DVTFrZlH8BL/HzD86RRmqNAVsACAAAAAAif3HPf6B1dTX/W+Vlp6ohadEQk/GAmHYzXfJia2zHeIAAzIyAH0AAAAFZAAgAAAAAGUX9ttLN1cCrOjlzsl/E6jEzQottNDw8Zo94nbO1133BXMAIAAAAAA7uVthFvXH+pbBrgQmnkPcpiHFEVCAi0WA7sAt9tlt3gVsACAAAAAAznaMStSbtGXU1Pb5z9KDTvEd79s6gmWYCKOKdzeijpEAAzIzAH0AAAAFZAAgAAAAAKnT/qg8N85Q9EQvpH7FBqUooxHFgrIjqLlIDheva2QSBXMAIAAAAABGAKkFMKoSIrvClWF7filoYM6fI9xSqOJVNS3dv4lxYwVsACAAAAAAgITE31hQA4ZOxpUFYSYv0mzWbd/6RKgbUXiUY96fBQEAAzI0AH0AAAAFZAAgAAAAAHRDRDT2hJrJ8X9zB9ELT28q8ZsfkYr92chaZYakiLlqBXMAIAAAAAAT0Le67ObldDta/Qb17dYfdslPsJTfGj3bWAgC0JIingVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoAAzI1AH0AAAAFZAAgAAAAAOOJcUjYOE0KqcYS1yZ363zglQXfr3XSD+R5fWLSivDoBXMAIAAAAABjeLe+tg37lNa+DdVxtlCtY77tV9PqfJ5X4XEKrfwu0AVsACAAAAAAlbpHiQAPLLTvSF+u58RBCLnYQKB5wciIQmANV9bkzsoAAzI2AH0AAAAFZAAgAAAAAMwWOOaWDDYUusdA1nyoaEB3C4/9GRpFNGags95Ddp4LBXMAIAAAAACLrsQXGWK15fW4mPEUXJ/90by13aG+727qWJep8QJ/WgVsACAAAAAAuThwsAsKUB56QAXC0MjJsZ9736atbiHPlK2tE0urf9QAAzI3AH0AAAAFZAAgAAAAABPRXBK0z8UANcvMDWntBjN9yF7iGMPLbhbaKrvHwcplBXMAIAAAAACZlqWsYPIb+ydmH03BxD3TqSGsSNoI7EVCy0VgW0TpYgVsACAAAAAAD2uaBv8oc7l4EeC5PWx5sfeyGZoas0JdFJ33M3jjgjMAAzI4AH0AAAAFZAAgAAAAAOn9/6pbzjIxFEApugaVOvVKXq23sDCJELv5UtLPDZI3BXMAIAAAAACHIwSDTlof0vFoigF4drbeM/8rdlj/4U386zQsNLtPGwVsACAAAAAAsYt/rXnpL55J9rlWSFRA4seaU6ggix7RgxbrJPu6gO4AAzI5AH0AAAAFZAAgAAAAAIMCESykv5b5d6mYjU5DlnO709lOFCaNoJBLtzBIqmg4BXMAIAAAAADs1Bfuaun4Es3nQ4kr29BzheLRDcFv+9a0gOGkSEcrDgVsACAAAAAA5kW6i/jOBSdoGAsZEZxVNRvt6miv86bP8JfUT+1KJg8AAzMwAH0AAAAFZAAgAAAAAFSPmr27XgKhUkbEvvC6Br5K1w7280NZrrhdzfYF+YGjBXMAIAAAAADv2h+Xq6kM7MHYTLMACRwbe2MzGHu4sdB67FGzDR6H4QVsACAAAAAAKII0MMC7o6GKVfGo2qBW/p35NupBp7MI6Gp0zXYwJOcAAzMxAH0AAAAFZAAgAAAAAPSV9qprvlNZK6OSQZNxKhJmBMs6QCKFESB/oeIvAS0iBXMAIAAAAAA835Jh22/pvZgKoYH6KjE+RRpYkaM1G35TWq6uplk/rgVsACAAAAAA162IdSb079yVlS7GkuSdHU3dOw03a+NS55ZPVBxbD08AAzMyAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzMzAH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzM0AH0AAAAFZAAgAAAAAKJY+8+7psFzJb5T+Mg9UWb6gA9Y8NN9j/ML2jZkNDNPBXMAIAAAAAA2R/nCtSYfCim89BzdUPS+DTQGwYDk+2ihFPEBS8h+ygVsACAAAAAAaEQra7xyvA3JS0BasIpRVrz7ZXsp6RpH7OpfJBFzFG8AAzM1AH0AAAAFZAAgAAAAAI4qr+sJiRaqwZRhnenAzD7tTKq+jP1aaLyAln3w1HQuBXMAIAAAAADNYpqV73NpwN+Ta0ms1SRiu+6WNOOdGT+syghL+JAFhQVsACAAAAAAN07Fo9SK+fXp5Odk1J806pyVWc2WHXCtb1gJQknTgqsAAzM2AH0AAAAFZAAgAAAAAISgN1Hid7IWvDESN/3tywFZiBsZPYapOUx9/QjDDxLfBXMAIAAAAAA7lxpEz3+CGdv6/WKIAlIwRYURREKgn7+StwNoVekkDwVsACAAAAAAx+Oa2v1e1R7VomfsvcKO8VkY4eTl7LzjNQQL6Cj6GBQAAzM3AH0AAAAFZAAgAAAAACFSn9LbHIGoJEDxQm+l/VhB1Qp2sDHEDDH/n/c8F8n8BXMAIAAAAACBO4bhRYkAW8TOvy65UHl7YNTK+4vge++76cn1G8/dugVsACAAAAAAzG6xXvdYOA8Myyt5WXWt0BWkQNQQtmstxsv5gCZsd54AAzM4AH0AAAAFZAAgAAAAABo9tv/mha/vJfQi56w2eTYcwoM/vprKLcIz5khfTjPoBXMAIAAAAAAvXKicv//4wGM3SoLcBUse+cGPll+OwzjqT0W5G+/DjwVsACAAAAAABhSDCzL78/uY6jz8VOCeitM7fALtBvmUHqgIqFonVYwAAzM5AH0AAAAFZAAgAAAAANtnkAP/k69i6SrAPWi4ibUkKJyqd4Xwiurqw+GBF7bTBXMAIAAAAAB0/TeuDQTjB9EBR27pJppA5mLgnGhf1tQAUQ1K4HfzBAVsACAAAAAAcDsgTilc7TneMVqO8r/+ASX300fPG2QzYds6WSAejHkAAzQwAH0AAAAFZAAgAAAAACSbt5cVtVjP7BGAWK09HRCwAyV9It3zsFV0xaSVTkCYBXMAIAAAAAB2oDy5xJX9PzwQxDkT/mZcL4L8MEH2iAQkbFRJX8uDnwVsACAAAAAA9gBAm/VjmIxxyxjC3WYtk5garVKwbEaKA4ehcrTrV0AAAzQxAH0AAAAFZAAgAAAAADy4xsEPgwXBxOl0h/pJ6yBU6qtcwoG5bVLxVu8EMIm9BXMAIAAAAADn4/5T92MZh6GQWJaetGTp8cd/CaTHqLoHDQVKFkH5dAVsACAAAAAAhM7MF/OTjXzQWgJHPIFNrhg37ABa0su5NuLJ6J8IJocAAzQyAH0AAAAFZAAgAAAAAMCDivXFvBdpcRif1dzSBn9lMOopHJ7nZHj4z3LDUmgzBXMAIAAAAAA+Ck5FgJyhL5IoIx/YgLVvuwdgUbQu2ZYmZOWiCyXzVgVsACAAAAAAvjiZ++YlRXaljBgRVEESlPFy9za1y7/XBgSaxLnTJu0AABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAAQbW4AAAAAABBteACH1hIAAA==",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int32-v2/mongocryptd-reply.json000066400000000000000000000034351521103432300270760ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "A+wAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAjQAAAANlZGdlc0luZm8AWwAAABBsb3dlckJvdW5kAAAAAAAIbGJJbmNsdWRlZAABEHVwcGVyQm91bmQAh9YSAAh1YkluY2x1ZGVkAAEQaW5kZXhNaW4AAAAAABBpbmRleE1heACH1hIAABBwYXlsb2FkSWQA0gQAABBmaXJzdE9wZXJhdG9yAAEAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int64-v2/000077500000000000000000000000001521103432300225055ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-find-range/int64-v2/cmd.json000066400000000000000000000001761521103432300241470ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$numberLong": "12345678901234567"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int64-v2/encrypted-field-map.json000066400000000000000000000011741521103432300272340ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "long",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int64-v2/encrypted-payload.json000066400000000000000000000261361521103432300270340ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$binary": {
                "base64": "DaUdAAADcGF5bG9hZABJHQAABGcANR0AAAMwAH0AAAAFZAAgAAAAABB+TYwDkWz8ouVl4Q4oIBElQKgGEDqMjPPSv+TZS8YABXMAIAAAAAAMLfd8ZW9xVgL8jqH0+wwdhD/Ktnb5fhh2ZM0iAVmS3QVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwAAzEAfQAAAAVkACAAAAAAffWVv+ysFys5eTN10LACdyWxHn/dCKMRWAQKzYmM5YEFcwAgAAAAAGDv6msNPvdhwK8sTlBu4iuoZyMtzmlAPYyKrR3b3KvGBWwAIAAAAAB61GI/ANIVYHOlZJaQ4pYzp8pK1EnxLI5uelzv27HynQADMgB9AAAABWQAIAAAAADwKL3PTbIxWwM3Ie8tK0sAdHbks7bZQ7k/+BtQb8hK1AVzACAAAAAA518WFoVWrUk+vd0h4ubURgpaBx5nuFnkUlPm7+Uv8j4FbAAgAAAAAOpTJTWFIUMsKaqzPZ2E+F5/MJKFRX9nbH7g/3FwbDyvAAMzAH0AAAAFZAAgAAAAAPeDZq0JZhU5dbRhsJu8i26uo0gCzLbXRuQv0qapUyUABXMAIAAAAABUYRvsvRAKNX88FMC9TKGV12jjrtOgqOUx3Aj2GyO4TgVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0AAzQAfQAAAAVkACAAAAAA1A4pQT0U6YKgy9rYhiDjktlclfRwylRHU68jAWaMsNgFcwAgAAAAAHp3IgqRDzqX/yPzN5ZwNh5UZFgp3RRLPpOjcts4EAqwBWwAIAAAAAAQsrQTEjSDj9bE2z6Z/5Rr6aJ7cy4w7d1o8a6QDbdVRgADNQB9AAAABWQAIAAAAABFCn58YDTy/VuFydht+w/S5teX3KDXxCZgx+Hax3wR0QVzACAAAAAAGF7v3ATOcTb3y23TbHJEXtuTtrY0f2TBWDVM9jn+VPUFbAAgAAAAAAOIZXl/9FE+TClKtkwYc0FJqubDKpX5JhcjvdBb8+afAAM2AH0AAAAFZAAgAAAAAGdbcKmZIVOCU80pgckeEkcL+93kExZCnO0hdAEvSwKeBXMAIAAAAAALbWpEG7Y/HtVTPWVb3kN+f/S3DbKr0BUG2RrfgeKMuAVsACAAAAAA/xLWzk4DlwlPogukD3gtqe4uxKX0GxEJYcG258VMxWsAAzcAfQAAAAVkACAAAAAAlEha7oMjYtH7Y+LT05oFkSZLYUifIGcaz3B/0l/bCIgFcwAgAAAAAGgOorOPXWrI59eXh1x+SYYepnFfICycaWAD3CwlFZZRBWwAIAAAAAAiwOi6zKj8Px7gELBSq8WGsq06elwYJKqk33igW1ZoLQADOAB9AAAABWQAIAAAAABNIe733XIhQXqiJNTdeGV8P48xU9Gy313Dcoj0bIOFuAVzACAAAAAAdCm75t1SwaYbXfPzqYDbibSWiSS4RwQimSSXivhnLYQFbAAgAAAAACn4x6fMcMNSQ488o0532pjr4xQ3NWdx6f7/2bkJxTjhAAM5AH0AAAAFZAAgAAAAACL9+rQRyywIXa5Pr7g2SnB0s0EjIct7PQtzjEkA69acBXMAIAAAAADz54imCCbu/qQkYP9wW2f5pHoBS+EyCe+xuDwC0UTiYgVsACAAAAAAKv602j4c3Bpn2t10qGl68eAD/fQsIH5lKMj8ANwrf7oAAzEwAH0AAAAFZAAgAAAAAKTK0NLhQ/+Y/HMxjRwBlXpXJAhAmCoWf1fReTegPnVpBXMAIAAAAAD7AlW+P4FfQS4r8d7EEvPVEP1diSbrVDBqg8ZvNl1XRAVsACAAAAAATTSEkff+/JMBjNwUciY2RQ6M66uMQMAtwU+UidDv1y4AAzExAH0AAAAFZAAgAAAAAGMbgPxi2Wu1AlqoDKTgyBnCZlnCjHm2naxRcizkIbYJBXMAIAAAAADMvSM3VZzVyRFCfUvcLXAXQFRIxlhm0t0dUsnaRZG4hgVsACAAAAAAI7uGriMAQc4A/a70Yi1Y7IAC7o/mfNYf7/FvwELYf80AAzEyAH0AAAAFZAAgAAAAAPnZ1bdmrcX0fsSxliuSqvDbRqwIiVg0tYp0PViRX0nOBXMAIAAAAAAqBdZGg9O74mnwyQF+lILtyzHdLOErDjPSf9sM8EqCugVsACAAAAAAwhuDsz+fCtqY8mW8QvEVQERjDChwrYTw4y7dinlCCOMAAzEzAH0AAAAFZAAgAAAAAJ40Dmb5BUT1AlWjfXB43nIbJgDn9rBg9FAeYR80WK0vBXMAIAAAAAAMPqLMDdNmnKzA3Hq49/NkJfs+/cjnyjSAbmiOFUE5FgVsACAAAAAAxbi7ql49Y4pduqWlLJqpwimRzrEnC7w5fWaMBiinHL8AAzE0AH0AAAAFZAAgAAAAAGelnhqWM2gUVy4P5QE/2Zfd7s9BugPqB/tcnSsFg5X0BXMAIAAAAAAWUhif3G+NMvZ3YPLB5OMuIhfPEu6U8KR9gTvJFz5uIwVsACAAAAAADEs8/aVSj2sJjxjv1K7o/aH8vZzt1bga73YiIKUx5DYAAzE1AH0AAAAFZAAgAAAAAD1xX2wCyf1aK1MoXnBAPfWLeBxsJI2i06tWbuiYKgElBXMAIAAAAACW1NW4RibvY0JRUzPvCmKnVbEy8AIS70fmsY08WgJOEgVsACAAAAAAQq9eIVoLcd4WxXUC3vub+EnxmcI2uP/yUWr3cz0jv9EAAzE2AH0AAAAFZAAgAAAAAHwU1LYeJmTch640sTu3VRRRdQg4YZ7S9IRfVXWHEWU8BXMAIAAAAACozWKD2YlqbQiBVVwJKptfAVM+R2FPJPtXkxVFAhHNXQVsACAAAAAAn7LS0QzTv9sOJzxH0ZqxsLYBYoArEo/PIXkU/zTnpM0AAzE3AH0AAAAFZAAgAAAAAHKaToAsILpmJyCE02I1iwmF/FibqaOb4b5nteuwOayfBXMAIAAAAABPxYjSK5DKgsdUZrZ+hM6ikejPCUK6Rqa0leoN7KOM0QVsACAAAAAAH9rPq5vvOIe9nTAcM1W1dVhQZ+gSkBohgoWLPcZnQXcAAzE4AH0AAAAFZAAgAAAAANTGiHqJVq28n7mMZsJD6gHxVQp1A6z8wgZVW+xV/lhmBXMAIAAAAABCR4BfdNVy7WE+IyQ312vYuIW0aGcXxr2II/MbNz8ZdAVsACAAAAAAng0GYpYJTypRLQUd5tIXWaAjZX5na04T/BypmwwrXPoAAzE5AH0AAAAFZAAgAAAAABooumzjEqp9Hvvd+sn1L82NI2iUGRl0nXQNJTHM7oyVBXMAIAAAAADgjz5L2ursK4C+pXXsJ6XHABhyallj9s/vSUgxXvjiiwVsACAAAAAAPjlAM0tbO6EUmLAeIZt57YMkMsuQfuC3T3d9vtnxgjwAAzIwAH0AAAAFZAAgAAAAAMA4jmE8U2uGkYUeKoYSlb22tfrRq2VlhV1Jq1kn4hV9BXMAIAAAAADG4fLeJUcINPSb1pMfAASJkuYsgS/59Eq/51mET/Y7RQVsACAAAAAAmwwcWOnzvpxm4pROXOL+BlxjEG/7v7hIautb2ubFT44AAzIxAH0AAAAFZAAgAAAAAK8/E3VHzHM6Kjp39GjFy+ci1IiUG5oxh0W6elV+oiX2BXMAIAAAAAA4/F4Q94xxb2TvZcMcji/DVTFrZlH8BL/HzD86RRmqNAVsACAAAAAAif3HPf6B1dTX/W+Vlp6ohadEQk/GAmHYzXfJia2zHeIAAzIyAH0AAAAFZAAgAAAAAGUX9ttLN1cCrOjlzsl/E6jEzQottNDw8Zo94nbO1133BXMAIAAAAAA7uVthFvXH+pbBrgQmnkPcpiHFEVCAi0WA7sAt9tlt3gVsACAAAAAAznaMStSbtGXU1Pb5z9KDTvEd79s6gmWYCKOKdzeijpEAAzIzAH0AAAAFZAAgAAAAAKnT/qg8N85Q9EQvpH7FBqUooxHFgrIjqLlIDheva2QSBXMAIAAAAABGAKkFMKoSIrvClWF7filoYM6fI9xSqOJVNS3dv4lxYwVsACAAAAAAgITE31hQA4ZOxpUFYSYv0mzWbd/6RKgbUXiUY96fBQEAAzI0AH0AAAAFZAAgAAAAAHRDRDT2hJrJ8X9zB9ELT28q8ZsfkYr92chaZYakiLlqBXMAIAAAAAAT0Le67ObldDta/Qb17dYfdslPsJTfGj3bWAgC0JIingVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoAAzI1AH0AAAAFZAAgAAAAAOOJcUjYOE0KqcYS1yZ363zglQXfr3XSD+R5fWLSivDoBXMAIAAAAABjeLe+tg37lNa+DdVxtlCtY77tV9PqfJ5X4XEKrfwu0AVsACAAAAAAlbpHiQAPLLTvSF+u58RBCLnYQKB5wciIQmANV9bkzsoAAzI2AH0AAAAFZAAgAAAAAMwWOOaWDDYUusdA1nyoaEB3C4/9GRpFNGags95Ddp4LBXMAIAAAAACLrsQXGWK15fW4mPEUXJ/90by13aG+727qWJep8QJ/WgVsACAAAAAAuThwsAsKUB56QAXC0MjJsZ9736atbiHPlK2tE0urf9QAAzI3AH0AAAAFZAAgAAAAABPRXBK0z8UANcvMDWntBjN9yF7iGMPLbhbaKrvHwcplBXMAIAAAAACZlqWsYPIb+ydmH03BxD3TqSGsSNoI7EVCy0VgW0TpYgVsACAAAAAAD2uaBv8oc7l4EeC5PWx5sfeyGZoas0JdFJ33M3jjgjMAAzI4AH0AAAAFZAAgAAAAAOn9/6pbzjIxFEApugaVOvVKXq23sDCJELv5UtLPDZI3BXMAIAAAAACHIwSDTlof0vFoigF4drbeM/8rdlj/4U386zQsNLtPGwVsACAAAAAAsYt/rXnpL55J9rlWSFRA4seaU6ggix7RgxbrJPu6gO4AAzI5AH0AAAAFZAAgAAAAAIMCESykv5b5d6mYjU5DlnO709lOFCaNoJBLtzBIqmg4BXMAIAAAAADs1Bfuaun4Es3nQ4kr29BzheLRDcFv+9a0gOGkSEcrDgVsACAAAAAA5kW6i/jOBSdoGAsZEZxVNRvt6miv86bP8JfUT+1KJg8AAzMwAH0AAAAFZAAgAAAAAFSPmr27XgKhUkbEvvC6Br5K1w7280NZrrhdzfYF+YGjBXMAIAAAAADv2h+Xq6kM7MHYTLMACRwbe2MzGHu4sdB67FGzDR6H4QVsACAAAAAAKII0MMC7o6GKVfGo2qBW/p35NupBp7MI6Gp0zXYwJOcAAzMxAH0AAAAFZAAgAAAAAPSV9qprvlNZK6OSQZNxKhJmBMs6QCKFESB/oeIvAS0iBXMAIAAAAAA835Jh22/pvZgKoYH6KjE+RRpYkaM1G35TWq6uplk/rgVsACAAAAAA162IdSb079yVlS7GkuSdHU3dOw03a+NS55ZPVBxbD08AAzMyAH0AAAAFZAAgAAAAAGsadEBJFax/UltPXB86G/YPxo6h353ZT+rC62iGy7qqBXMAIAAAAADs9TP3h91f6bTuG8QCQMA3atAVGs8k0ZjVzX3pM8HNAgVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sAAzMzAH0AAAAFZAAgAAAAAAxgeclNl09H7HvzD1oLwb2YpFca5eaX90uStYXHilqKBXMAIAAAAACMU5pSxzIzWlQxHyW170Xs9EhD1hURASQk+qkx7K5Y6AVsACAAAAAAJbMMwJfNftA7Xom8Bw/ghuZmSa3x12vTZxBUbV8m888AAzM0AH0AAAAFZAAgAAAAAMsF+h3Cqgrzmao79EFlt7XUVDVYxDlOuJp9fd2njUULBXMAIAAAAAD2XTbeYZdu87zL+ANjGEuK9itwm1oHIucqHxLnWhplmgVsACAAAAAAyJgZZ3MyP4IYKT2NtJq8DBkzz9PaxrK9vaR+XTsRyHkAAzM1AH0AAAAFZAAgAAAAAO9plUESIDneHgrY/f9U5lTjbWRSZntI8KlP6TXZF/9UBXMAIAAAAAB9XQ9jWdACGGIZZJiYisZoSPo9Pj3Rvh8oKD4VxtctfAVsACAAAAAA5F1w5HpoC4eHQWRgRJBj//WVTD43hJcGb3sh0bojJOMAAzM2AH0AAAAFZAAgAAAAANoXAlsVp44X14O/0ueeHYCCxsC5JmayKccwWgP28CeaBXMAIAAAAABbUhEE/E5or4+4I7alXpoNyu1xjNVrzCi5Yrwzg8IrbwVsACAAAAAAQDw5ok73ljnvljHGWgBU/oEkLq89nOIMufpAWJXZuTAAAzM3AH0AAAAFZAAgAAAAAKcyk7RbzydoBOTPIFBt6EtTMjVyGsUhx4BagUsRna6aBXMAIAAAAACXafcHWSM/heoFSFGYPn7Gla7ZcMcMfsucP/tDuGhOqAVsACAAAAAAUU6eINEpD/VlrAy2jh+5+baQ9cvclLglDA7w045G0c8AAzM4AH0AAAAFZAAgAAAAAAluaRdNRjUWvKqF8hBzAbi/PQo2X4xFpZtmltnA5WZ6BXMAIAAAAADPTpEz3N5BvgrfWg0cSexVcIT9QBcxXDUGeljLfn7roAVsACAAAAAAG8wIC5cXfbeMZn6jGD6Yy+F4ZbTTCjSsSS/iZM7IzJMAAzM5AH0AAAAFZAAgAAAAAPGEl1VBrxJgj7cgOGjNoOD0xJ0Tq+pvyalhT8/yN3zZBXMAIAAAAAAINdKtKw14MRjU/dycHNXSGRFB7d7PqG9v/P2nfGPkjQVsACAAAAAAq6K5bQZHywUk3XR/0l4qerhz4JTOTQ+bIsVGbSA1Ec0AAzQwAH0AAAAFZAAgAAAAAOaoAaheMsPE4WwsAzI9c6S3cQkXdEqpEFmUpgQF5z4rBXMAIAAAAABQLpIkaUKhTp05ZI8AA/v1oBv8ohmcmm7K+/sjqHMYgwVsACAAAAAADOtEUjUQlvKf6WdIrZleVwma3gjKcLshzajtLP9pXUMAAzQxAH0AAAAFZAAgAAAAAN/vipid0wavMuYm6q0Qej8I+0MZPIO/sysW2fAMMRgtBXMAIAAAAABhgUDVegsnV78ZNmD8Ad+G+L7bhvFs+T+LY3zDTjEchAVsACAAAAAAfazxCcVs9n83k3NINdlC6941pcksLfINz1Xtj5kS9DkAAzQyAH0AAAAFZAAgAAAAAJzIrJiV6IdouJe+T3EVegGE973goofh4QLPb3I4F+cqBXMAIAAAAAB56y67x2z8Pd/3ChjAw69wuFnahdqfyxfnvW7QvOsEKAVsACAAAAAAabBeIk2CWz/j1Yp4M4Q5Js5KMWyP6MID/hd3twPl4IQAAzQzAH0AAAAFZAAgAAAAABZvmCekYSVglzFe5FL+CoV0i5KjmDwvqMHVGApZe5vTBXMAIAAAAAAC91Pt03x+pRD80ZVX/7smx2TyCpnqAy4lVvF3TyspaAVsACAAAAAAALfFZEgQuV6swMxx0OpDfNj3GwhyxBfJr1tM+6CgjhIAAzQ0AH0AAAAFZAAgAAAAANzRchBZFlSsxneCNq4hBVa6QRPrYW+tB8AV+SQ/evNLBXMAIAAAAADMCXCbGCnmG0B8hyy5/vqT5TaA+0eQv9ihkS3uyELDJgVsACAAAAAA4hhse5B6zxYRGh30Dp6fHMWzXjgSx3KnE8bGxNFfM0kAAzQ1AH0AAAAFZAAgAAAAAJlfE4PVSVhiV0MUCkADagHeWmJXo3a3fokzKYIhIVeJBXMAIAAAAACr2IKAxCfGSCun4izvMYY3Y9Fs4wh6dtTmi2bW0DvZ2wVsACAAAAAAjL3zF8sDH7w9Vwqz5coXS26qyC7VwfiHjxU6GZ57Q3oAAzQ2AH0AAAAFZAAgAAAAAARBx5KrGgivFwv1HncHI5PakuJTFlFbQpgx8GLDoTjQBXMAIAAAAABLs2xM85oCKAFHh8f9a6ZI53LcCd1b4+RwqeCvU3Y8XAVsACAAAAAAftQ+WuNEgdAB8dypZB0wrE27S1lQ6BXavmwWZv6z81YAAzQ3AH0AAAAFZAAgAAAAADa3sc1oraS1k+f5kz0bP/VAI8whsZmKx61wL9H92XicBXMAIAAAAAAnwxVX0gAxYFOoP14PKCtOPDksSfR++d/CjffYBVurdwVsACAAAAAA3DIA+ylb/SNP3HFQV1hPJa5mf4gqucN/z8c5bbPrGTsAAzQ4AH0AAAAFZAAgAAAAAIVM7+M9JwZPQACwNIHkD/ZqlNA2FZQFdXJdo8bfUX4PBXMAIAAAAABpG3yn1N8JwanquFi3vUaLWKjzi1HUSMfIPM/xlCBqnAVsACAAAAAADbOJkKlJj9KHbCszGgiGZFCQsJj8QpV8MskeZThK4rUAAzQ5AH0AAAAFZAAgAAAAAKYYm2QnEs1cBoODhk/EUTIypw+w/Y6f+K1sgpcZDLQmBXMAIAAAAAB+95xrWxV4hFxipmtk8sLCkc1pLMB7vS4j8CNw8/+HOwVsACAAAAAArmDB4qxPUdH3oeyPQhCgcZkPJ2hR7Z6Ek3DieJl0JPoAAzUwAH0AAAAFZAAgAAAAAOBpiNsiKVq1JOV5dWF0I+nUJ2sH9vy0ewv62mnGtLFaBXMAIAAAAABe6yzBO7bUcbs+RzTzLr9BGeY12mLXpCStrMB0urFteAVsACAAAAAAyfxyA8IXLbXWP/56Mc6CH9Ph1mnbLVlsRtkh4ixfde8AAzUxAH0AAAAFZAAgAAAAAP2IBZxLNspoTPMraca0CfKhEocv/NcyyQQq9ZTGoNv/BXMAIAAAAACoq5/5PJ1s1LRwmrtanxViywKp9jERF1mGcGrhjUkX2AVsACAAAAAA03Wle00K7gOjKH5mUoB/s82KWvxoX9JW6hyjHQ1DdQ0AAzUyAH0AAAAFZAAgAAAAALBNlrMGNjhHwk8Lx97PYFZXx7QfDQhxKBYR3kLHr1IyBXMAIAAAAAA2jChQugSYMZBSW7hV5sEL9Hwp6jWd5Sucdwb1ByzS3gVsACAAAAAA2gY7FULKEIh15JMuGnzxqhR9VMIPjrKSdo6BoKydB0gAAzUzAH0AAAAFZAAgAAAAAEnZRVntb1MzQky6eD0K3tCySiGf4FLoyRlW56KnCog7BXMAIAAAAAAd1n7PJbc7BiAU3yvDmy4Ht51tJqE6papne4i/xxlkfQVsACAAAAAAu+it7o9jiENMeE97YjCinWpdRUTYCReVmz8OCvzOD3AAAzU0AH0AAAAFZAAgAAAAAKhexbhP3PeY6EBPmujthzaosaSSZFIMq5HnQwtFWWveBXMAIAAAAADMqCakj5/y4l6RZH7VjFd6cgHeE1NlLyRdCOFpHxGXQQVsACAAAAAA3dVm62UQ9+XpFKBAFI6Z08V+M4ZvYDqxqN/vRhDkiccAAzU1AH0AAAAFZAAgAAAAABnSfdBCATnGexxKQV7swwfxf2nk/wxIfjvffm3k4coFBXMAIAAAAADaPqt/BvDx4Nt6U22nZDYIBGR5jvRR/ba0UkqFYGOscQVsACAAAAAAwt8xn9mDRpjHeVqVGa4gpfZggqGRPEgOKxSiAKcF1v8AAzU2AH0AAAAFZAAgAAAAAPmG04KhEpPRHPRRnfAwWJ5k2wbAjLkqt6yHwMKeV6A2BXMAIAAAAABpgK5RiOaz3M0GgZrDahUXZwGxolwCh9hEp+/G2VI9GwVsACAAAAAAylgs3BiBjUnyOvBl3IK4yQxgBgZm8MBLtleFCKyZ19oAAzU3AH0AAAAFZAAgAAAAAFIxOj92Wi83wPyQ02iWdWdurZwzFndnx5pq3pY1w/4nBXMAIAAAAAASXgOXGFzKOyaetG1s/ZUGyNdW0X+5wRE8ln0fFudoxQVsACAAAAAAu34md3q5knY3mz6krEAsxVzXgHy8EnmWKdEwyIkcBfkAABJjbQAAAAAAAAAAAAAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAEnNwAAEAAAAAAAAAEHRmAAYAAAASbW4AAAAAAAAAAAASbXgAFYHpffQQIhEA",
                "subType": "06"
            }
        }
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "long",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-find-range/int64-v2/mongocryptd-reply.json000066400000000000000000000034651521103432300271060ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "find": "test",
        "filter": {
            "encrypted": {
                "$binary": {
                    "base64": "A/wAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAnQAAAANlZGdlc0luZm8AawAAABJsb3dlckJvdW5kAAAAAAAAAAAACGxiSW5jbHVkZWQAARJ1cHBlckJvdW5kABWB6X30ECIRCHViSW5jbHVkZWQAARJpbmRleE1pbgAAAAAAAAAAABJpbmRleE1heAAVgel99BAiEQAQcGF5bG9hZElkANIEAAAQZmlyc3RPcGVyYXRvcgABAAAAABJjbQAAAAAAAAAAABJzAAEAAAAAAAAAAA==",
                    "subType": "6"
                }
            }
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/000077500000000000000000000000001521103432300234175ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double-precision/000077500000000000000000000000001521103432300266625ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double-precision/RNG_DATA.h000066400000000000000000000044421521103432300302560ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xda\x16\x8b\x32\xc5\xeb\x17\x3d\x6a\x02\xb8\x27\x04\x24\xd3\xb8"                                                 \
    "\x50\x8d\x80\xf9\xfb\xc9\x7f\x40\xa2\x76\xc6\xda\x45\x98\xfc\x13"                                                 \
    "\xd6\x36\x28\x2a\x80\xa3\xdd\xf8\xa1\x15\x0a\xcc\x72\xe5\x8f\x1a"                                                 \
    "\xea\x81\x5f\x2c\xb2\xbc\x4e\x09\xaa\xac\xe0\x4f\x1d\x5d\x91\xc1"                                                 \
    "\x5d\x84\x81\x60\xca\x0f\x02\x9c\x66\xa6\xa1\x80\xe7\x8b\xb5\xca"                                                 \
    "\xdd\x1c\xfd\x9a\xd6\x2a\x3e\x03\xf7\x21\x92\x9d\xac\x47\x01\x7c"                                                 \
    "\x3c\x5e\xd4\x96\xfd\x13\x06\x11\xfe\x5a\x61\xcf\x42\x3b\xd9\x05"                                                 \
    "\xea\x37\x99\x12\x5f\xe8\x8c\x86\x33\xef\x9c\x6d\xa5\xc2\x51\x2a"                                                 \
    "\xda\x56\x83\x53\x62\x01\x76\x96\x18\x3b\x30\x97\xaa\xd3\xc8\x19"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xec\x86\xfd\x48\xa5\x15\xb5\x02\x3b\xef\xd9\x32\x01\xa8\x5c\x2a"
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double-precision/encrypted-payload-v2.json000066400000000000000000000060661521103432300335360ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C9YIAAAFZAAgAAAAAKGC7e7Khb0eNdd0PGMhz88wjaRo8hhtloqXiO3RlPPgBXMAIAAAAACO5kHsnT2RtRzNVr/dZqqJBOql9vFQQt8tx7XFgkbdGQVwADEAAAAA2haLMsXrFz1qArgnBCTTuGS4HYPxt4n9AhD0tPqb+SA7LEva9ufIwi9D6B653QCrvgV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAEAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBJQjYD5+8l/QKJ2xtpFmPwTaO3cbT10hGSsG3avRpR1nAhKYs+DGSKoj9SonSc4JSvVmUqkPauQ/9ncmPCI7UHFBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAA2ybUFdeMB+ujbbvEqABQti0mVh2gidMf/lbSMxCnqIISawAAAAAAAAAAAARnAD8HAAADMAC2AAAABWQAIAAAAACCTEu6TTXfvs9twJDgH6+xdukb+sStdrChBLo/nWA30gVzACAAAAAAtQ3t4esWC7DbkQOgsTYVr3hge5CtGicFUks/MM4vL7QFbAAgAAAAACbBD5/yFo+aOMivjfGplV/Sl57BC2RtidXOlmtgHwROBXAAMQAAAADWNigqgKPd+KEVCsxy5Y8aW66/VRLwkIAbiVzOgDQ2lo7p0NrWzapCUknDe/1QJ7VZAAMxALYAAAAFZAAgAAAAANk91642dYB0wQK5m6VTkXhkq4lZzIzAzj9NqK7EljaMBXMAIAAAAACkGWqEutN6uJYX2tsss5twvEf64+vOm8LXhJDR7XMxowVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoFcAAxAAAAAOqBXyyyvE4JqqzgTx1dkcHICc2NsV67dEIW0ulpyw8HXZja2YOJbNpkKUd98HooMCUAAzIAtgAAAAVkACAAAAAA3uYLtxjo1etEc5MJ0fF6FaR8CqVtO6o2Tzlc/EqHUeMFcwAgAAAAACBU2rXaSwz9yZu1PeD+BpuqjizHDyT8G4sQBZUnKFxeBWwAIAAAAAC0+fcRgZMiid1CmuZgqc91xJ+mU8dIyy2/+4HJPNbEoQVwADEAAAAAXYSBYMoPApxmpqGA54u1yj+8b8A58vviYOujO60pwuZXGcfN5xHBo8pHZAIwYfG3fAADMwC2AAAABWQAIAAAAAAZ6kS+6LBFKXZKhFEXeYWqDS7nPCgPs0jNKODRaq2M4wVzACAAAAAAy4S9uTqZJm+oah6eizOVBPYXBhyBRlleh7q4ZofwuHUFbAAgAAAAAOgl/lqbUkJCdW1b5ArQiWSO+stq7xAENAsD/SBJd1svBXAAMQAAAADdHP2a1io+A/chkp2sRwF8KZjfeWu3Tb83NfH4IFLfhAq9cjRZ8LqnGnQ9E93UCZudAAM0ALYAAAAFZAAgAAAAAESrZ23i6fpJHqg+L8sc2b92AWz7eknvuK28gRPkNDChBXMAIAAAAAD0sxeWkLvt7yeUp3t2dqDJhSQ/Vi08HNlgs4tvpbd+DAVsACAAAAAAPP+wWZPSJIaqTs7kytoS66wO5+0Xb7pkjLZMMOfLt+0FcAAxAAAAADxe1Jb9EwYR/lphz0I72QX2MR5uVmKIZyPjvuYcKnahXSHBu2aFwYKR3TLsJ+wBsl8AAzUAtgAAAAVkACAAAAAAQXIPscm4wHxEay2glEaoZhYS8hVAaixigvTGTt/4A0wFcwAgAAAAADyPkP0b6lgXEu0wsx8EZnaAhpT+2zcm/O01eWxSv0iXBWwAIAAAAABSg8CZPZ+6d04xOsu5j4KPMlF1c3QI2Rc55GGboazyNwVwADEAAAAA6jeZEl/ojIYz75xtpcJRKqO2hzSz1YF2uRi/BZ5wESj2jzRT1NQOrOOAYedlIYKzdwADNgC2AAAABWQAIAAAAADQBLJ1L/dZvwIaqalrEZpXLs09art9ITcAWt94TYNK0QVzACAAAAAAIrBi8HKX7gEjNmN/Uq9vC90EWnPJVq0/KhgYFu7WH6oFbAAgAAAAAC04/aY75Yt5N0tBkJyVSZ4AYGxMClGMErG4wf2f2tbWBXAAMQAAAADaVoNTYgF2lhg7MJeq08gZiIeLWQ/5PDO2+S2xlp/Rf2DqvvLzOLXBR6Hc491STPaWAAM3ALYAAAAFZAAgAAAAACfOptT81z28/ogO3ldBDvRJfrp1UOKGrXHbu3E7SC5DBXMAIAAAAACHO0rm2+uq0kbILJ5Wsh2nng12wb+ajvTsxe/zOFI3uwVsACAAAAAAKHN9iXrNVBXVQS4dk9IxaaGFbjbEj2NuS31krRgXxmgFcAAxAAAAAPj/lDFjnTNIVhXVBvMDG3Sfk6Zy/4gX/DPyKNxkWrzk9ShUwcAdRTvNpA17F0TPEVgAAzgAtgAAAAVkACAAAAAAsg2W884aVdqaiDCIFZR0GYDU7Bpa5UDTxGTPbVa5/X4FcwAgAAAAAHuO7J9X6WylXxVrmepRQSDWU0aDPUBG0wWDnT6sR6h+BWwAIAAAAAAjAnZcNon7oV1P5+QdiwiJRUnsxYTXLtfBrlmlcXJodwVwADEAAAAA+2FJY248ycTnEZ9YiC+te8NlRbhUbmoTJmYAyDwQ+WwDAuR3zyJNHidwojV7UaExZQADOQC2AAAABWQAIAAAAAC7cb9sJzT/6ZtXJbgBI+4PQF7nu0y84fdbRku4X6gtrQVzACAAAAAAU/9+6ZI3g1LQmyE82/FQInKd7YU2XLWK39m48FE8jfMFbAAgAAAAAOTD1HhJ8RrwRF1w3su434I4PV3hSyX7cuZqNLnfswEHBXAAMQAAAADB6voEKAgf0uyivHp9x7H8ukBL5AScHoRJXt8hKdWJtD6VhCq3fiek0kYhphc+Lge4AAASc3AAAQAAAAAAAAAQcG4AAgAAABB0ZgAGAAAAAW1uAAAAAAAAAAAAAW14AAAAAAAAAGlAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double-precision/rangeopts.json000066400000000000000000000003231521103432300315550ustar00rootroot00000000000000{
    "min": {
        "$numberDouble": "0.0"
    },
    "max": {
        "$numberDouble": "200.0"
    },
    "sparsity": {
        "$numberLong": "1"
    },
    "precision": {
        "$numberInt": "2"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double-precision/value-to-encrypt.json000066400000000000000000000000701521103432300327700ustar00rootroot00000000000000{
    "v": {
        "$numberDouble": "123.456"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double/000077500000000000000000000000001521103432300246715ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double/RNG_DATA.h000066400000000000000000000201131521103432300262560ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xda\x16\x8b\x32\xc5\xeb\x17\x3d\x6a\x02\xb8\x27\x04\x24\xd3\xb8"                                                 \
    "\x50\x8d\x80\xf9\xfb\xc9\x7f\x40\xa2\x76\xc6\xda\x45\x98\xfc\x13"                                                 \
    "\xd6\x36\x28\x2a\x80\xa3\xdd\xf8\xa1\x15\x0a\xcc\x72\xe5\x8f\x1a"                                                 \
    "\xea\x81\x5f\x2c\xb2\xbc\x4e\x09\xaa\xac\xe0\x4f\x1d\x5d\x91\xc1"                                                 \
    "\x5d\x84\x81\x60\xca\x0f\x02\x9c\x66\xa6\xa1\x80\xe7\x8b\xb5\xca"                                                 \
    "\xdd\x1c\xfd\x9a\xd6\x2a\x3e\x03\xf7\x21\x92\x9d\xac\x47\x01\x7c"                                                 \
    "\x3c\x5e\xd4\x96\xfd\x13\x06\x11\xfe\x5a\x61\xcf\x42\x3b\xd9\x05"                                                 \
    "\xea\x37\x99\x12\x5f\xe8\x8c\x86\x33\xef\x9c\x6d\xa5\xc2\x51\x2a"                                                 \
    "\xda\x56\x83\x53\x62\x01\x76\x96\x18\x3b\x30\x97\xaa\xd3\xc8\x19"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xda\x16\x8b\x32\xc5\xeb\x17\x3d\x6a\x02\xb8\x27\x04\x24\xd3\xb8"                                                 \
    "\x50\x8d\x80\xf9\xfb\xc9\x7f\x40\xa2\x76\xc6\xda\x45\x98\xfc\x13"                                                 \
    "\xd6\x36\x28\x2a\x80\xa3\xdd\xf8\xa1\x15\x0a\xcc\x72\xe5\x8f\x1a"                                                 \
    "\xea\x81\x5f\x2c\xb2\xbc\x4e\x09\xaa\xac\xe0\x4f\x1d\x5d\x91\xc1"                                                 \
    "\x5d\x84\x81\x60\xca\x0f\x02\x9c\x66\xa6\xa1\x80\xe7\x8b\xb5\xca"                                                 \
    "\xdd\x1c\xfd\x9a\xd6\x2a\x3e\x03\xf7\x21\x92\x9d\xac\x47\x01\x7c"                                                 \
    "\x3c\x5e\xd4\x96\xfd\x13\x06\x11\xfe\x5a\x61\xcf\x42\x3b\xd9\x05"                                                 \
    "\xea\x37\x99\x12\x5f\xe8\x8c\x86\x33\xef\x9c\x6d\xa5\xc2\x51\x2a"                                                 \
    "\xda\x56\x83\x53\x62\x01\x76\x96\x18\x3b\x30\x97\xaa\xd3\xc8\x19"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xda\x16\x8b\x32\xc5\xeb\x17\x3d\x6a\x02\xb8\x27\x04\x24\xd3\xb8"                                                 \
    "\x50\x8d\x80\xf9\xfb\xc9\x7f\x40\xa2\x76\xc6\xda\x45\x98\xfc\x13"                                                 \
    "\xd6\x36\x28\x2a\x80\xa3\xdd\xf8\xa1\x15\x0a\xcc\x72\xe5\x8f\x1a"                                                 \
    "\xea\x81\x5f\x2c\xb2\xbc\x4e\x09\xaa\xac\xe0\x4f\x1d\x5d\x91\xc1"                                                 \
    "\x5d\x84\x81\x60\xca\x0f\x02\x9c\x66\xa6\xa1\x80\xe7\x8b\xb5\xca"                                                 \
    "\xdd\x1c\xfd\x9a\xd6\x2a\x3e\x03\xf7\x21\x92\x9d\xac\x47\x01\x7c"                                                 \
    "\x3c\x5e\xd4\x96\xfd\x13\x06\x11\xfe\x5a\x61\xcf\x42\x3b\xd9\x05"                                                 \
    "\xea\x37\x99\x12\x5f\xe8\x8c\x86\x33\xef\x9c\x6d\xa5\xc2\x51\x2a"                                                 \
    "\xda\x56\x83\x53\x62\x01\x76\x96\x18\x3b\x30\x97\xaa\xd3\xc8\x19"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double/encrypted-payload-v2.json000066400000000000000000000356421521103432300315470ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C2gsAAAFZAAgAAAAAKGC7e7Khb0eNdd0PGMhz88wjaRo8hhtloqXiO3RlPPgBXMAIAAAAACO5kHsnT2RtRzNVr/dZqqJBOql9vFQQt8tx7XFgkbdGQVwADEAAAAA2haLMsXrFz1qArgnBCTTuGS4HYPxt4n9AhD0tPqb+SA7LEva9ufIwi9D6B653QCrvgV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAEAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBJQjYD5+8l/QKJ2xtpFmPwTaO3cbT10hGSsG3avRpR1nAhKYs+DGSKoj9SonSc4JSvVmUqkPauQ/9ncmPCI7UHFBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAA2ybUFdeMB+ujbbvEqABQti0mVh2gidMf/lbSMxCnqIISawAAAAAAAAAAAARnANkqAAADMAC2AAAABWQAIAAAAACft0Ct7p0d2AiXZFc1xs3H2sG/z5OYtPQYrgXt27Q8tQVzACAAAAAAJR/O8D1VkB1R7Uow+HrTfkOTc+5koZOCzImvSl7FZlUFbAAgAAAAAEgMw1JNsiPUWtdfY1TaBRwe5j8Lv9KKK2nNwrr4nD2LBXAAMQAAAADWNigqgKPd+KEVCsxy5Y8ay7ycRMSzCy2R9RVeyXjwR7Ua2KQfdh7FzItTAW26blRZAAMxALYAAAAFZAAgAAAAAOh1vj4LUxJiIlkf1EpaOgAi6JJSc5q11Zvz+ZLYsZ+3BXMAIAAAAABxwqZ/jiglaW24HIUAWC4WLtOCHQ6XmMrFPsVvYWmXyQVsACAAAAAA2+FaAbl8JZogfNCI0FFbmZZPy/KLF1u16FGrPspSbEIFcAAxAAAAAOqBXyyyvE4JqqzgTx1dkcEd0gF2haXkpbm5FLdFILphzwyiJ2bQb9J2kxLDfGCOWiUAAzIAtgAAAAVkACAAAAAAJFUjWhc4GZjtdGYZH1GdxK8pv+MaRb0Yk9Tu4B6RRwoFcwAgAAAAAEvWzEZ/t73uP1iM0HofkXVIpmjyR4yDZxbGGuuCPZi9BWwAIAAAAABKzb4SCGGUyL2JzcXeIkmlqiy3NPEFnwUHVlib/A63vgVwADEAAAAAXYSBYMoPApxmpqGA54u1ylQ+eTOcDkrxliia1jfIVQi1MYP4r7m+31eRe3yVdDVUfAADMwC2AAAABWQAIAAAAAD8wiPmMZE6vE3laX7gKlWvjd8NXhGbnikR9BDWJqxcMAVzACAAAAAAQX50GO+DqpBcvqMEl9foEPGTf93v+v4roRDImm24j58FbAAgAAAAAH8D9aNK/8LdK6NechstQcXIqz9wyXGdrlXUPR083u5MBXAAMQAAAADdHP2a1io+A/chkp2sRwF8o2IW2L6twUDD4UxiPLaikA05C/U3TB3SPN5N7zecPnGdAAM0ALYAAAAFZAAgAAAAAHWnpOFpeAiW2BEt06bzPQ8wAh7KpkVkA8gQXohRAgM3BXMAIAAAAAChKZF0FP7IKjtLMZvQmnLuOk45XsBMb3Czvn3AAKmedQVsACAAAAAAQsiZcRrjk4AmBUiAWzDcGTwaQ2vXfgtFPHcLFD1of2UFcAAxAAAAADxe1Jb9EwYR/lphz0I72QWjq5iM0ietoj88KAa6xqSG4kvHs4v1sitC0MRDgvLhy18AAzUAtgAAAAVkACAAAAAAMUMaOvVryMgIRIyXxsnm2izPXUxjcxgVbPL2n5YVx6IFcwAgAAAAADCF9Cn4wuKAwno2sRd3Wc1StVQ/S2J1lgxmKua1wLgABWwAIAAAAACLW9g1z9QR0Pb4JciaOVh/IwwXXKmMya3zfN2LNpbk9QVwADEAAAAA6jeZEl/ojIYz75xtpcJRKq+84+BQ/TvhaY+5B5YDLpMkvPSSRIFdxgLTMm2CXnIkdwADNgC2AAAABWQAIAAAAADvLphVYEowRTGCsbZKyjZexkPaY6thwrQuBiN95wqftwVzACAAAAAAN20plHtuSHtBkF9DoQrOCX/aJop5XDy4EGmAxnqvUmUFbAAgAAAAAJIg8ym3GP7hjj/xP6eKbx+/0rKFSkC4jNeguAnxkqFJBXAAMQAAAADaVoNTYgF2lhg7MJeq08gZnVrAPQYAmknUXxGNZTpwfcI0wgtDMiRGfdBEM0krATmWAAM3ALYAAAAFZAAgAAAAAATqicDXukUTs23e14xzNRAHBlO2n8/GGKV/WNpbsJHfBXMAIAAAAABpQea7gq6rparOWr7J/0L5dNkkXvLZ2NTlxRsHYwhE1gVsACAAAAAAq55jDvPqq+WJKJePk6qQ3fXeWgO7PB70tQKdrkfNI6AFcAAxAAAAAPj/lDFjnTNIVhXVBvMDG3Rx6Qovps0Wi9/0Xvz7F+O6H/wGXo1eExvEpPmPTB68fFgAAzgAtgAAAAVkACAAAAAAybRXgbQ2H2gtK19nqqwApxmQZNMh6L9GqUOv7s9UL3cFcwAgAAAAAMiun7gwdxtWZrdpzOaTVATHXbXjVZ3LKM4cFLjZchP0BWwAIAAAAAD87dp+dGY8ClK/LId55BVy8v9GPObMo/I8jjlK8Ip3tAVwADEAAAAA+2FJY248ycTnEZ9YiC+te3BFNp8z8B3gH8QCnTDS7EgSDBcXp//A5ezvK7MOZBq7ZQADOQC2AAAABWQAIAAAAADOoSGpX5zlJ7Wc4B7Tw5YSSBk3EUhaoe0SWJ4D90gXlAVzACAAAAAAC70WdZjKYqtwStRU30BVDkRcOdmX5L9pKUykB3+ZfWgFbAAgAAAAAEXqasg8ahA7XD4tiZRPC9t5LqftW/t7IHQiRBS/KrXaBXAAMQAAAADB6voEKAgf0uyivHp9x7H84gIjeA5h/33pjypJLWSMmAhUUHYWxi1HJNM9UTmb3py4AAMxMAC2AAAABWQAIAAAAAAiyZuSuu2E6K+UnXQKtpQkk2aM5v0Yq2J2hjlNnygtXgVzACAAAAAAX4t2jaZ8EnzdpiARpP0nj2Co+4C0Hop2VQnVKuUMYV0FbAAgAAAAAJlRDonwmpyhZCENjik3pJ3GpfOcJNR4IKsjUWYXGdbjBXAAMQAAAADlIbTnnCfHd2JTCSrDnVFc67lL+U45rHUS4IGr53+bJ/8rpTu8Z84EPZ9sZvm+K0mCAAMxMQC2AAAABWQAIAAAAAA9FyOTumgu3RkgzhshigJD83vYX8XC/0EJPAatleP/DwVzACAAAAAAK1tLjYq3CzckODIZgIGzSA9U43oLg0bI25scdQmL5/UFbAAgAAAAALi1+1R6lo40+ZLh0Q7TNw02NMIHKFl/zjZqWOklTj57BXAAMQAAAACsfanKUnP/dRLj+ZQY79s7gUIehO+czy3SIlaDr3EPZbPEKdiQ603T2nezv0iEM88uAAMxMgC2AAAABWQAIAAAAABfWzEmQOxZX/+aX3U94bHKPB1He+RxgqaMuZETRiV/+wVzACAAAAAAXaKjcwXSaSFRGJF3Qjp5lH6jIWnVKHIVyYxUtlECc9QFbAAgAAAAAGPuTgl4wtGBadkSU8YTJiDoijCN9hOdIf6aW+G1YLiPBXAAMQAAAADsL6nN8YuAYaq9lnLGO71pIEvNKaVDbg28Obrxb7s18hRZlTUeKhNV/qIaMG3H/Bi2AAMxMwC2AAAABWQAIAAAAABnrG5k7VV/caeBiIs2jRL1lkKl4JpF8e/YidREqxzFfQVzACAAAAAAIYIJ/PjhAns+Hu4ZjGA5O8R8W5AyAMny1z3j9Oh8NREFbAAgAAAAAGy57hRbjZ4dcsWaAzUG/9BJFzJNfwURBIAmY35TQO6UBXAAMQAAAAAadWGPpOHaMqWtbI8WBT3YNd/E318Pr/otVVbGpg/zy9R426OXGIKapvTCItF52OMNAAMxNAC2AAAABWQAIAAAAAD6f3YBcAsS7KUUgwEYKMqpbzlPJwHiXj0t5O3i9pmg1wVzACAAAAAAw4PVDxk3yfR6MhiCaEs7eWKMtug5LHTIGmzAvPiTlFIFbAAgAAAAAGOz99qNc2arAxKiIxjGtNNkHAUF1Q1xlUfS9ErzTuWbBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wcsoxoXoii27cwS6V3psgdHCw20wEaU29sYNpWbKol7buAAMxNQC2AAAABWQAIAAAAABnn/RUnwnNfcWlWnxXPKY2E/FdzZ5ZRCgv8LwxLxpQqQVzACAAAAAAn/URi1Foja7My0XWNQxy7HOF00sCpLCbzMnDMvIWuoUFbAAgAAAAANKebpRbQfwsgbqWBkKiXK9mIBQD75FLprPyocBxLEotBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wLrz1JTJ9zzRqOHPBg9xp4WG5vu8/4YnuZyZq17gtuWHuAAMxNgC2AAAABWQAIAAAAADAcBN3nRPSw2/77njGaQyDC44OAMnxI4rwEj2VXfCnbQVzACAAAAAAgzG8aGEgq1fQPS+X5bDSiHx3XwHQu6w5EY4RHT93QeIFbAAgAAAAABwnmSmvN6DzEVrArPXNq1GPrnK16HLsM0KkGs3FzHvTBXAAMQAAAAD4/5QxY50zSFYV1QbzAxt0m5lQ/EVDFnmlByvV11hzyxdSfQGvPGf2MO/zlRBhuUhYAAMxNwC2AAAABWQAIAAAAAAQt9sKaYXCm5DyxDctaexa+07dtvJfQ3fAZJ0Ywc32jQVzACAAAAAAibWGcvS3QrKeZu7OvNYZNZmGYFC3EhgbYJZAMjZqWNAFbAAgAAAAADl8eL8YijDL25DyKDl2DG02O2Kf/BmdPgkKgjNfcMjgBXAAMQAAAAD7YUljbjzJxOcRn1iIL617MV4vVfcwRATnFYWfapeheUzXwqRFcBPWQmV/OeF8UZ9lAAMxOAC2AAAABWQAIAAAAAC1fwzGIsoXmnEFN4a+kvgh4d1k5ZVDWQHLJ4U5Bp6y9QVzACAAAAAAXai5aSBYcpHjj5sDel92AeyOLsDiWhloiS9sU9jWUYcFbAAgAAAAAEMT+F7TmIJ75KvQ0+3565l7E2TvwNvbJChFzciK+Mm4BXAAMQAAAADB6voEKAgf0uyivHp9x7H8tBeMZLbz70d6SmUeiHuvl6CGR29jeItGhLD1BZ7U8nO4AAMxOQC2AAAABWQAIAAAAACCRWe0lz7hmCCa7aVQVUmxeF4KoS2aalC4g5J2gIbUewVzACAAAAAAXaDOXcbiujpwQWq7WPK5Wyh4F2VXkdPE3kmaoNpTyNsFbAAgAAAAAHgVSRLDYD/BndsCcGyDBTPxf+oAnTJwVEmqDVlrRThgBXAAMQAAAADlIbTnnCfHd2JTCSrDnVFc6ZLzKS6nBDO/B8sBG3AF87f7Sd5f6Je2tt8j7Mbhgs+CAAMyMAC2AAAABWQAIAAAAAA4SLZKWmCVFvfRh9SkPkh1+c/uy5xrpiSoMejpEFsSswVzACAAAAAALqHr8e2EjJAyBsCBTOOzFMqT2DOgH1oJf4UWSzY3dwcFbAAgAAAAAOSUyv8griWIe7simqf50GUG9Ka9zDBAuA3nKdg62/73BXAAMQAAAACsfanKUnP/dRLj+ZQY79s7hLi++IivSIrEHKQbYxMPOXYDEpE7d1ESfmm5gXc4oz0uAAMyMQC2AAAABWQAIAAAAAArdfWpMgm6SypDo7WYBg5mdI/yz9KWb8oM212iA6/2NwVzACAAAAAAImWFAmBhae2DKznZPUJxjTWQpbkjN0cCkgOn704EiVIFbAAgAAAAAJynZHeNKqUmOGQAJ+r7woCbKxnZFrH57XZRRqWcPf0NBXAAMQAAAADsL6nN8YuAYaq9lnLGO71pX4zrWMDwbsFuChJfEMM9619qEeXoNSZCpS3paXLBBp62AAMyMgC2AAAABWQAIAAAAACay4jcSSevnHaXvFgGPUnEZv5/sfbtVpNyzDTQ+iy9EQVzACAAAAAAsv3aeQutsQ+2mAO8wPZ3zTfEjqRb9YllSj7yChaPFqUFbAAgAAAAAKDcUDPFuzuuurroi13xasZGEF4fOU3ZJ/mHFTzJV0TLBXAAMQAAAAAadWGPpOHaMqWtbI8WBT3YpqAXWqxDHI6l07tj6pm9PSfADpf+7cINO/fT3C+K+1cNAAMyMwC2AAAABWQAIAAAAADwtZp3zYY+/8ZSoS7H+s+yZg0fePlamjbZzIAS201dLgVzACAAAAAA/I5abFnF4d7vKatjoLRhl82iGc1CfVUYt0fjs9NREhcFbAAgAAAAAKUP8QV7RGSQCRKIlEVESin8eQ4MTsZYmTJwTH97onVnBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wTce+wjrQo0RJ2p10FmR6mt+edGl/OGxtHKhKVplqEfPuAAMyNAC2AAAABWQAIAAAAABNJ6p/FzgC4+PfhNgB1ZSelRfr7LjANm4uVYN/YBmhGQVzACAAAAAAdrkwAMdkUX0nxlN4G8EcNzBEGR+YxxEb16awNem4qPMFbAAgAAAAAC+nkWgov3YxS07Try6PhW0ad6lStrmHha9I5GALbAM9BXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wx/DUrqRxE+eBNWVvrREHOiJ4dLulgihufEkZ0KODqxfuAAMyNQC2AAAABWQAIAAAAAAxLRkzBAr9sqlZFVc7fcqiOBZM/SJ2Pk712W0KtiFXbwVzACAAAAAA86F/FCYD2pywJLXDnJP6OzC/tyu7Tyxq8g8UN+uHOEMFbAAgAAAAACaIBISU03FRNOEFmutoabsoK839UNVHwjl+wvAGc5oXBXAAMQAAAADaFosyxesXPWoCuCcEJNO4Gf8je0qJwtSu+RfIu26pkg95WQe8+KZ38ItJ7NAc5fG+AAMyNgC2AAAABWQAIAAAAAC7XSWz7nm+NFQuBA7x6xWyobk8kPH7pI69GRt1V4A0bQVzACAAAAAABdMveWPZ8WZT1yGwd2z/Yx+U+DwxmNTsC0deNQlWmRIFbAAgAAAAACd0npQbW5WzmXHZUfQvfYWYdM8bEL6NdcEX4ztDp0MkBXAAMQAAAABQjYD5+8l/QKJ2xtpFmPwTaIU+8sb88sCR9ZpPCrCw3K92/ObnRZLt6EMwMDWlQWPKAAMyNwC2AAAABWQAIAAAAABHKN4WLYNudd3K9UwH33ipum8dQUKzty4W3jBggyRtYgVzACAAAAAAulVEC1MEHe8gdx1pFU7TZxDFBrEFZSVqqYX3ZeGMBxAFbAAgAAAAAIyX+YxBnZHOS23qQHa+48x7ZsEkLsMVAXd/EKPAbq3YBXAAMQAAAADWNigqgKPd+KEVCsxy5Y8aVPYWv6riht/gb0IHJEzwXuZMrft+sqgtqYcLLtLzDxFYAAMyOAC2AAAABWQAIAAAAAAEFGGlo8uxeNdkGJh1M8j/B9ZU3qzLZGysAHlaUgjPYAVzACAAAAAAZ2u4JlCTvaajzXBOJ6x1R4Ur+apurhRJOXs9IWftm9sFbAAgAAAAANQgbUOLIo77svEdrkW+zUBmRE6T49sgK0mL/Vay67dkBXAAMQAAAADqgV8ssrxOCaqs4E8dXZHBC3sfL1sefGp3zHh8YtThMGT02ZAG6eNRitbqjXrkgkglAAMyOQC2AAAABWQAIAAAAAAJb1EYgtU5RJwp1FEVQC3jwucATB6sY5HA8HcxgfZSxgVzACAAAAAAWD2jWpiMzS/WmAbNA5tu/esBFnR8YOigzKxk3oYR47MFbAAgAAAAAKigscMJIfYqAMsJr/VaHCLku+nqyfKf1WM+aOsQ+8KVBXAAMQAAAABdhIFgyg8CnGamoYDni7XKR9UWL3s1OjB/6BDLTkyqgBaW/X6UVdUYjfsFSZFYTlp8AAMzMAC2AAAABWQAIAAAAAAHM4JqrcItNeN2gAwMMFzZ/A5rAxD9AoOw0DoLT2XjpwVzACAAAAAAPtuyTly5f5x5ErZeQTMjciPx3rWNKnHSY+dA9rTErRoFbAAgAAAAAA+JgsYR4QxXA9wXrr6WXdw4cmfJy6U4prjnSiR2SooaBXAAMQAAAADdHP2a1io+A/chkp2sRwF83MfQjg2XFEzmTVk46lJp8t9bqp1VnJIr/inFg+7gHPSdAAMzMQC2AAAABWQAIAAAAACoa1IeOxOIX1E4TKmdl4bAAm7WVktFBwnjZbg1uT3E0AVzACAAAAAAtneQomUUrnsa3e1FyQWdmh7Ly4LuZ4aNJuK2bw/9ResFbAAgAAAAAJSjzQ3vTh2fPlQGrBIYd2bwhxZIlgxspDkMqjPqAT0KBXAAMQAAAAA8XtSW/RMGEf5aYc9CO9kFtPWZWqPNy/MeqvTYo1lL8sbONW+l3lvW14wP7I2mOlVfAAMzMgC2AAAABWQAIAAAAACLNqfx4NDEdyaOrvP5RYSd7SK1o72CsDZ1ju2SER23cwVzACAAAAAAIe8IgfZxuZmC6l2GvVX2EkNj8vhzvXXBSpL2zNPyJ0wFbAAgAAAAAIbS+tRZTOnY4YDCSF7q7qht4dWEJPsqY1iYKQTluO4QBXAAMQAAAADqN5kSX+iMhjPvnG2lwlEqvtYfSF5OYPgpH9IwPCGBTDVqUlV8Xl2RRCfuR+Rs7Wh3AAMzMwC2AAAABWQAIAAAAACnkG+1tDv0mxgKF11PNnnbY3ES4pb1kQDMzWFm84uXWgVzACAAAAAAme2UfmOga5gokVOU8jQWmmtYq6BtOl/uqcTajlmo+cYFbAAgAAAAANp7bQZY5sz0vQgBBs6HuR0A41r1hfeSL1jMkaSNSgfiBXAAMQAAAADaVoNTYgF2lhg7MJeq08gZM9p91x7Ouaq9Xh1aNgSo7ta2TyFXVEcQxH0ee2osqpqWAAMzNAC2AAAABWQAIAAAAADUrB0QN+wxXhcqupnkiuRY5x9peq7WryozkzyfU1WuXwVzACAAAAAAHXs0X3y/BjRnUH5br8IAk4VjhnVOIx2wbmoBJ9XimmMFbAAgAAAAAA7ePVmfiOYxi+h98yAgVSQeRCY86L3IPJhkrxYgCK4uBXAAMQAAAAD4/5QxY50zSFYV1QbzAxt0BdPYy1jcuxoSanoZnSqh0O5GpHUxpNZ/Twvjr/r0YslYAAMzNQC2AAAABWQAIAAAAAC89ib2XLPQvTvSV96dvMQ/+P6cNg9JI3bz5GybvMoMWQVzACAAAAAAzppVYEzsloxQ+Oy9nnZkme0B3i9ZAfOlFGiItWn0U2EFbAAgAAAAAFlHfkMkTbo1kPADqGCh/zBepuxgoJdsXDo/FZ+eKQh/BXAAMQAAAAD7YUljbjzJxOcRn1iIL617dnH8R09rkDopi4fsSDfc1ThQfNurY/hoNpu3vr7iWi5lAAMzNgC2AAAABWQAIAAAAAD3/4OJLA/hGgSmLMZC0Vzm/kPU4aGg9yuOB4PACgzN+QVzACAAAAAA1keGfZcScISM0pi22qYdqNfG88QbYMDAXgMpKmwUBpsFbAAgAAAAAPl4p2ihsHIwPCCaSeB5E3c+PpCxklxCEBWMr6ZwbTpWBXAAMQAAAADB6voEKAgf0uyivHp9x7H8P/izcAG57VIVF2arKILEPpvOmmuaQlLuU5ywfCoWpW+4AAMzNwC2AAAABWQAIAAAAABEXVHib/9HjrechHci9wNa+t8oVU1j2Q77Vf67Yhip6gVzACAAAAAAn2ze0q/ca7kJ+lUKtcrGXTw+Za1kF3JqakBUhS+OosIFbAAgAAAAAPXBSHi3rTwho5S5ZvzrknF0Wen/+odPK2PBUaiqAnuhBXAAMQAAAADlIbTnnCfHd2JTCSrDnVFcK17jpkeZ1bDGvPSw9kh69aO9OxZsbjYYAtbtyTM86NaCAAMzOAC2AAAABWQAIAAAAACZxbsChjUZTMgnzhMklgl8S5bViCJ4/+atHE6ILjcZYgVzACAAAAAAnf7xU6+NfbooANg/OmfpHnGBd07Kcvgs21h8D+DTK7IFbAAgAAAAAOh6mXUPXnElV5ro5YdCx5UbXuE2tnbZUq2GDgpdMNBlBXAAMQAAAACsfanKUnP/dRLj+ZQY79s7N+ekWsqmuaDeGrylFZdVM80RvexRGvM32rTTxaHc/4guAAMzOQC2AAAABWQAIAAAAADA56eD2uZTqXGeYiM4mDXiV1hXQ4Qd0zlzqIamlGTc+AVzACAAAAAAALZLV4nwTQ37aIux2oeEvXvAHfVmhHZZQbsUF99sFk0FbAAgAAAAAJTbTFvW366kTmCPfSyqxv8UBPzJ3UEhTkMm/+9QF2nOBXAAMQAAAADsL6nN8YuAYaq9lnLGO71pfV8lDSlhSiEWSaA39wbI2xE6qamthhcZdpVakeOpmYG2AAM0MAC2AAAABWQAIAAAAACVnmPXAlf10mh6KCvIrlywZvBQJ3GKI556G7CIGRd+KgVzACAAAAAAFMrUpp0M7NAwOkDkTL3kw1BMh13Jxpvx7BU+RQtGCGYFbAAgAAAAAH3K0V5Fo/cxbyCeApSEegQ6cj+RiXa9G0lY9yBXFcv3BXAAMQAAAAAadWGPpOHaMqWtbI8WBT3YAJcZhTriQVEjcfg7ZtIuM0BIB25s3tCZndwfkzJD5ZQNAAM0MQC2AAAABWQAIAAAAACs2S/v3rQYOyzmVyc0nGJIfb8h0aTVIajn5tgPGSuSVQVzACAAAAAApQnSvZxdJe77IoPX6kvab5cK4xidJNJ4KUBhfqwvG20FbAAgAAAAAGKAaUUYuGzJR8Y+6bdA0OmuOpPpzeFHj3eDNACo8MkTBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wFEA2E/9IZ3Rd0bXAXJvBYoU2jrygYesNgq/Im+YUGInuAAM0MgC2AAAABWQAIAAAAAD56IQlX7UhCleBWHQSp/JA/edcxoTg0sWdHbbks4JdBQVzACAAAAAAyINDxywXgCkX62DnU029TTcYWT4LQ7Y4GOaRHAcpNzEFbAAgAAAAAAZh0EGehyQNsAwx1UyEbTdRjf/y4HDL3Q1w+18BdeIfBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wecqnaU8CwrOxGFbw5Z2mQCUkNJo2Bo9Nswk4+U0SNNXuAAM0MwC2AAAABWQAIAAAAADUMJR09ziZujjdEPDd5fhfOLtF7NfxPefXW2ZnuOmJKAVzACAAAAAAx5nM1KXGDCnivhUNwShNC2GYMN/Rho/86Uth+nfXNiQFbAAgAAAAADW5ys1ONRxT+ydSwLzSK4VACbVyJ9OWzMeboMYKbqePBXAAMQAAAAD4/5QxY50zSFYV1QbzAxt03zEgQIGlsQeXhBFP88DsSAq9Et+uAUQzyCqDcljBzo5YAAM0NAC2AAAABWQAIAAAAACTP+FRBr3zDN8IZQpqDHCznfhxxs3bbE6uAJlgRHHdWgVzACAAAAAAvdXLVzSYwnpHd5pe9JBZfLQMMS1sWI84UZj0LOwZi0MFbAAgAAAAAFEx9mHbnFu21KNK8RQlk6fxcGnhJ2KCwv7WzZmAXQS8BXAAMQAAAAD7YUljbjzJxOcRn1iIL617BT5icDcfxMw+BPEPItHhMGFdk9meOoT1c2vLJzsPggxlAAM0NQC2AAAABWQAIAAAAAAZbHIFEJI2EyD0AXCIKYrnibmJCXze9U35t8cAtkz1LwVzACAAAAAAxY/MrgqVf9W0zy4keJI4zJFKUMZsvy4cxqw6qvY2uGoFbAAgAAAAAHWzwCyWgGSVgtjqLEyY8XczhDiIUgV/FSmvPaIFGR9NBXAAMQAAAADB6voEKAgf0uyivHp9x7H8LDD5o5w+4gMtCtA5irbhWt1COWntnbwyyzOj/LA0G564AAM0NgC2AAAABWQAIAAAAAAjCy3VgA4qfecXFADgURacy8klmZppF70e7oACeEt77AVzACAAAAAAKnlblZyfpcXIUm1DPSKCKIvj3LdyFMUZCJuIvqyfyJkFbAAgAAAAANdM1Ah+QJAJyknxcWFGuDvRqKrGKdKTyk4jmy6CrDQSBXAAMQAAAADlIbTnnCfHd2JTCSrDnVFcnktm4XTaG8wHFMz5fqA+gBRgggx6bYFrYA0x8rAtgo2CAAM0NwC2AAAABWQAIAAAAACVCIez4H03xjjTxdoeeaMKbB2q3RPf6miYzs+rrD4o7AVzACAAAAAAUVj32/GhBRJJzmMDeD0VRIS1NTEb2G8fhDRYNlsSxEYFbAAgAAAAAOclkW0ojUZtjkzl5XA8J1wGUPt8eyXjlVw3rZ9vTu45BXAAMQAAAACsfanKUnP/dRLj+ZQY79s7+0Gi0pSKwQi/1AeZV82paTgl/5OAsGQEhdj3/BodEHwuAAM0OAC2AAAABWQAIAAAAAC5EntiCpTldThXw+asqYcNJ03/0BeTiy3BehDGQ2ApCwVzACAAAAAAN1T43hRWT/XAWPp+AcAZvjyxjJxqgCbTh3SAMsyDs2gFbAAgAAAAAG1RpV/I+wsTR7N3W2EDP2Nfw5Ta+qya4JG0S4YcBMT6BXAAMQAAAADsL6nN8YuAYaq9lnLGO71pSr2WhLTHSNktedH4LEFV2FZLOMChgkeTsFrOtPBGPKS2AAM0OQC2AAAABWQAIAAAAAATwDL8VPtPI6ab0bY/1ZRYBl8/y44exZmM5HeXWwXx1AVzACAAAAAAzsh0XK9fu4x4RpB4CmnlbU8HK+yNSO1GVz8yl8gLNpkFbAAgAAAAAGxiK01DFN24K43NFO6reYDokblI2VYmpXu43vDxUXrfBXAAMQAAAAAadWGPpOHaMqWtbI8WBT3Y2pW5fwixFg1rDSinIAYvnV8Dq98oUKYuJvYTQfEO22sNAAM1MAC2AAAABWQAIAAAAAB9HHjcQJFeFiOSTlpTiWhlc0zfaA0X96HggZQxKB96bAVzACAAAAAAZdQM2aV6xENTgIIICOKj6aLhtr4eacmieEiAxNY/1KkFbAAgAAAAAMVycQOKlIrHMws3+wl+XheYxR+n5uShDHqj2pUrsqJQBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0w1J3od8Zvhtn1c7QfvjK45LDd2xojLPDX06cpIZwE103uAAM1MQC2AAAABWQAIAAAAAATHJ8YtKLwDbYF1Eym5sDbGJjGvLYhdL7dMv+OMBKyowVzACAAAAAAjRimUOIzij8BP4q8krZdIHJxRQnYMd4KXWNSU4SfCQgFbAAgAAAAAF3ZDSegL/ctC9HyHCRVoB3QDkB/jXy+q7E/jdyZtumtBXAAMQAAAAAAlcZs/jB1xVN29yi/zH0wPFFC/oEmyKWnzLyrJGZGLWBNKK3ldOd/9oz7ts6kCuzuAAM1MgC2AAAABWQAIAAAAACRaorXgwSBnVzA5LiD3JbrAQxIsFw05LXdE075hDmjNQVzACAAAAAAKf6e6IIsKU9F9RfkursMoWfVI0l75yzL2Rm+Oauy+98FbAAgAAAAAJofqRQMpBpc3uEj5pRbK3SBPnx4xx/7yTs7HyZ+4vDaBXAAMQAAAADaFosyxesXPWoCuCcEJNO4w6DCh+6mMQdbKLXvnUZfCFgTzWV8UKbW253j4pApJm2+AAM1MwC2AAAABWQAIAAAAABq/dBlUW2OCiEHJH/DqgqTpk69lMyDSNfevRkQWzKP5wVzACAAAAAAZurfos9KMZuZYvT3KwvUKxS7mWlxxXqZPyqmVO1bL7AFbAAgAAAAAAle3VrzgJ+uMR67iy3aJd8xNwCtPBA65BoXa9iQlktvBXAAMQAAAABQjYD5+8l/QKJ2xtpFmPwTC7zOKWpvMj1bQE8IVteblKRZnbOnGDyY3C7IUdGo98HKAAM1NAC2AAAABWQAIAAAAACkDPTbW292/7sKSeLL2NamuWTwHF/1Q96LXO4vt2DYbQVzACAAAAAASGKPvjooX2y52AuNgpPkZUFsM9uU74Dh2JnGnnz3dWAFbAAgAAAAANUoxTn6+RIZi7g8Wm+ocZr5RLFeoDspELXKkxb9za0gBXAAMQAAAADWNigqgKPd+KEVCsxy5Y8apsHdCsPOxFx5wFTjs5HHXLflmJHvOA2m2Js61U+IfWFYAAM1NQC2AAAABWQAIAAAAAASRskHLtBNN3Zg7n3gudRKRWlxr6xFGDn9P7djm2lmbQVzACAAAAAAAyxWCS0yxNp1vRXpKY0g0sEMc3HFXQEcKCYMXz6jQEEFbAAgAAAAAF6QnUHjjxDUmzl4XbVN/0EVUp6YZ2IXedj+2pVUvzDIBXAAMQAAAADqgV8ssrxOCaqs4E8dXZHBbzzxACa/BRahvB3bbPW0pSDTU0utGvYEm4vb8yOqWdIlAAM1NgC2AAAABWQAIAAAAAAUTc52Auixs5RBcpWFtkA9YqdaFWKfYC760YknHuHT4gVzACAAAAAA9i1ZUY+EKYUy15ZL9B/6IziWwaQ+T4NZvs60lwUdfQwFbAAgAAAAAB5tAY3koFZaHfmzA8PmYBHE8ji1XVWqfZfpZmGQkMe1BXAAMQAAAABdhIFgyg8CnGamoYDni7XK6cXsJGw93pqbp4BNucg+XsUBKq7Wer7h/5nVABJU0OV8AAM1NwC2AAAABWQAIAAAAAD4aemeYTsW438FDmaTWiIMJKqJLcdQbOSTHbV5VRjQFQVzACAAAAAAlE7UqP/7i6tpnkUWi3y1Pm3Qlbw+gsMPuJshygiVr48FbAAgAAAAAIPOM+ursUIg99ECHO+iPiYTGqZl9FI7pbDrDn9qp8RfBXAAMQAAAADdHP2a1io+A/chkp2sRwF8dlK2aK7V4Hv2wapwIB3/vpF64ZTmNCD2JVWkv1KxHmGdAAM1OAC2AAAABWQAIAAAAACi3E77uLPIfnKkjitfOIl17RSDIuQs+jASqOOagn1qygVzACAAAAAAI2ksEaNKkdjC0Z8x4Nx+YSQ4sjIKNFQ1QiiN/uz+FikFbAAgAAAAAPv9DTFg6bxT4TcBw4aXd1WWUli6RluyP9f8/ynb2xhdBXAAMQAAAAA8XtSW/RMGEf5aYc9CO9kFIesl6WWT9FDGpoasioCoCfw9TN9BjYlus0Y0fW6laZdfAAASc3AAAQAAAAAAAAAQdGYABgAAAAFtbgD////////v/wFteAD////////vfwA=",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double/rangeopts.json000066400000000000000000000000671521103432300275710ustar00rootroot00000000000000{
    "sparsity": {
        "$numberLong": "1"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/double/value-to-encrypt.json000066400000000000000000000000701521103432300307770ustar00rootroot00000000000000{
    "v": {
        "$numberDouble": "123.456"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/000077500000000000000000000000001521103432300243565ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/RNG_DATA.h000066400000000000000000000057701521103432300257570ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x9d\xf2\x97\xd9\x64\xab\xde\x8f\x12\xdb\x36\x9a\x83\xc8\x0c\x22"                                                 \
    "\xd1\xb7\x83\xef\x0a\xf6\x7b\x37\xcb\x37\x5d\xa9\x2d\xd1\x2a\xca"                                                 \
    "\xf4\xc6\xf9\x10\x5b\x31\xc3\x68\xd7\x1c\x58\x38\xf1\x6a\x9a\x89"                                                 \
    "\xe0\xfa\x49\x8f\x54\x98\x93\xcb\x87\xc5\xbd\xad\x8f\x96\x39\xc1"                                                 \
    "\x87\xf8\x95\xa3\x6d\x4c\x0c\x5e\x9a\xe3\x76\x81\x50\xe7\x6f\xa1"                                                 \
    "\xc7\xde\x90\xac\xf7\xa9\x33\x20\x12\x47\x3d\x99\x65\xb3\xa1\x2b"                                                 \
    "\x7e\x4a\xe7\xbc\x28\xd6\xfe\x85\xdc\xf5\x4f\x76\xe9\x86\xe9\xc3"                                                 \
    "\xbf\x1f\x48\x33\xa6\x9c\xa8\x3f\x16\x61\x12\x48\x6c\xbf\x9b\xc2"                                                 \
    "\xea\xeb\x1a\xa7\xc7\xf8\x1b\xbc\xa5\x30\x83\xa2\xec\x64\x43\xda"                                                 \
    "\x86\xab\x0f\x27\x42\xa5\x21\x68\xfc\xda\xa5\xcd\xa3\x02\xe8\x4f"                                                 \
    "\x5c\x4c\x80\xa4\x82\x81\x6b\xb8\xbf\x8e\x2a\xb9\x21\x23\xb0\x01"                                                 \
    "\x66\x7c\x39\xfc\xa9\xb7\x61\x04\xb2\x61\x8f\x5f\xeb\x6f\xd2\xf2"                                                 \
    "\xcb\x05\x54\x02\x5b\xdb\x35\x8b\x7f\x3b\x65\x18\x00\x6a\x66\x2b"                                                 \
    "\xf8\x8c\x8c\x54\x41\x19\xf9\x33\xe3\x37\x68\x40\x7c\xcb\xc8\x12"                                                 \
    "\x63\xf8\x60\xc3\x62\xe7\x71\x3d\x20\x8a\x46\xfa\x5d\xd8\x90\xf8"                                                 \
    "\xe9\xa6\x14\x2a\xd2\xad\xc9\xe5\x50\xd0\x7d\xf3\x22\x01\xd0\x4c"                                                 \
    "\xda\x00\xf5\xd0\x97\x2a\xec\xb3\xf9\xac\x81\x73\xc2\x8d\x36\x1c"                                                 \
    "\x14\xb3\x2e\xa1\xea\xeb\x13\x85\x01\xb7\x3f\xde\xed\x30\x14\xa8"                                                 \
    "\x9c\x4c\x4d\xcd\xe5\x31\xb1\xf1\xb9\x0e\x45\x1f\xc8\x6b\x4c\xb8"                                                 \
    "\x79\x25\x44\x91\xf3\x5b\xdc\x63\xac\x91\x93\x2c\xbc\x74\xa7\x5d"                                                 \
    "\xcb\x46\x47\x79\xd5\x69\x1f\x66\x52\xf8\x39\x49\x33\xa7\xa6\x16"                                                 \
    "\xde\x19\x7b\x47\x52\x07\x18\xe7\xc3\xe2\x3d\x88\x4f\xcf\x77\x0d"                                                 \
    "\x56\xd5\x9e\x0a\x1f\xa1\xac\x7e\x83\x99\x65\xaf\x9b\x76\x31\xa4"                                                 \
    "\x19\xa3\xc0\x26\x2f\x98\x61\x5c\x8c\x83\x54\x2c\xbd\x47\x31\xa3"
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/encrypted-payload-v2.json000066400000000000000000000107561521103432300312330ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "CyINAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVwADEAAAAAnfKX2WSr3o8S2zaag8gMImdCNfErp8BkxXFlb1aNkPcwyDuSLm7gIXzlP992WjKF3wV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBLRt4PvCvZ7N8s3Xakt0SrKupID4FsPQTVZXoX1fBkDnk0irfALQzHOt9kvd3ZurzzZXCJPfQTlZIlq+MGiG3gxBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAAAAAAAAAAAARnAJsLAAADMAC2AAAABWQAIAAAAABT+uzCz7yxp7uN3+z202vxQ5NOV985A80zAqx86317iAVzACAAAAAAotJPQKqQGOGUkMWaWW9GlbqGpHdx8Y9mSX7tBhUz8YMFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAAD0xvkQWzHDaNccWDjxapqJsQQJxIpJjSzH2N1sMl52I8VCGmhbZ7eiv1GA6+MQxOtVAAMxALYAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAOD6SY9UmJPLh8W9rY+WOcGfBhR2IbREcrPOlvcWitCDpgifW8LNgrL1DtsD+aUrX0sAAzIAtgAAAAVkACAAAAAA4ZMiLagfridRRfmhKbzkDw5T5V0S5kyhqfBvZn5dPlYFcwAgAAAAANo9G7ZRhNvttdgY6+vg49XT1TXWf5UE6DAFlX/AP82RBWwAIAAAAACHR4WfBFBn/Zv0d5gD1QJv1LAe7z5dfOKsdcEJaup2rgVwADEAAAAAh/iVo21MDF6a43aBUOdvobfFrmz0OxcKEl87Qr/9ulWQYnliZ2RZvlhXHLqxkpgtiAADMwC2AAAABWQAIAAAAAChShCDXpnDH8wgL+pVXLX6iPO5/XkcAyVINSwPJHWMEAVzACAAAAAAaNNLGMgUd2cbkY4lzPfLGl6J5Qc2Il4H90QDcnu40WUFbAAgAAAAADnNH06cbj1eufeCtTvMEOif6iP7/UVQg6m0u/B6xZS9BXAAMQAAAADH3pCs96kzIBJHPZlls6ErYAktA2yuwLKyPFi9iXdea9rTY7PxuUQoZyLXHnuwLgjwAAM0ALYAAAAFZAAgAAAAAO+3mZV828ldgLhwSL9H7PSvmJlv7WrvByHAAAGSof4LBXMAIAAAAAD/ELnpWf3l/SNoUNP0S338kKIp5E1V+3Z99E/3FPXh1QVsACAAAAAAEC+jQrwZIUPbpQq8GjVCzoL+1IX6d2YbIkx0jheRskoFcAAxAAAAAH5K57wo1v6F3PVPdumG6cN3hShAXObXElzOyoLNfG0Uu5KyU7ilPAVwJ5NWL9jZBMwAAzUAtgAAAAVkACAAAAAAE+06hBFHl4f7CHQFGGj2ZZuT3WZEMqTj4CzUM2e9PeoFcwAgAAAAAIIlGRvBtZCaXT4OWFMXF+3VC8jzuJFe3mR5RpM/T47OBWwAIAAAAACBRJPSwvsKXZ7fostzk7s73P1v+MuxlsoSzfGCBVqQWQVwADEAAAAAvx9IM6acqD8WYRJIbL+bwmpzAcv2J0i3W8dOyHdHo5GaGbhUbQs+2XJAsKBk/qIFyAADNgC2AAAABWQAIAAAAACvCIk3Z/O0aqysrOwm+wSmvNpZFsMJBXBhSIBBLiT52AVzACAAAAAA9dRAOydZMcNo1yNh9XP7JS1cA4EEBD95wN3t6NZ8pNUFbAAgAAAAAOV9V/DlJ/McpfffRxJGPtgN5IgReZMW5hjPPCC5wfh8BXAAMQAAAADq6xqnx/gbvKUwg6LsZEPah3z3EDqqUUHd2yTo4Uc8boUF1TAl0lfj6Eu3fu6k0qTcAAM3ALYAAAAFZAAgAAAAAGKJ35r7U30MbUBQIvdcgcQW7MG8KbYldN4PCdrQRRcRBXMAIAAAAACwwmMiXwHvD7BEzhBgfDj4L0AajySh/Qhtz42i5ogkbAVsACAAAAAABcm2+tbs0VSGFn9rDvWyvhuIRVDepRdoIvNa6TRv3zkFcAAxAAAAAIarDydCpSFo/NqlzaMC6E+eBx7LiXrB/fG2Z9szcbLmXjUcSJT2iKLj1tFILB/vmisAAzgAtgAAAAVkACAAAAAAYLNccfL4s3DMHDChNpne5is7B1RfWUpctFbO/r2JTAMFcwAgAAAAAPOdU0Owvp1pFujAWWt7nKFVJxcXTMoVw3ZZiheO1DB2BWwAIAAAAAB1bCFHfdyYaPnhvEnOmfNQZIQRVqhxDSwj79IqNXdlmgVwADEAAAAAXEyApIKBa7i/jiq5ISOwAbfHhwxnDXWMoi46jtZK5PL0TFYnvfIW0kItQIYnrygZ6QADOQC2AAAABWQAIAAAAABLprrCZQNYrlCa1tiktkFmgLViqEn2CEz2GYfCozEMxAVzACAAAAAAI9l/JeiixzTr2mna2nwoaxaJ1oHe+if0AJrtviapy3gFbAAgAAAAAIsIWi7l4L3h7alFc2Mnyg+QQ1EIMZ3D15k/sq+BvaYIBXAAMQAAAABmfDn8qbdhBLJhj1/rb9Ly/kz+GbT1WpkxhezKcoB04iBaBYKJrfSeqH+7Pk8SIN5LAAMxMAC2AAAABWQAIAAAAADUq7UIfHJv/mGbsXxGaSnrk1w2OaF1XIQOdL3Ibv7jAQVzACAAAAAAVYSgjDnwcfQ937v4LtelZhXljZRVaPPKpZ72Eg6Qp5wFbAAgAAAAAJnqBNMfgotr0Bs83VunOklSDAVnoO8FAAs2mjULfoP2BXAAMQAAAADLBVQCW9s1i387ZRgAamYrfC6rt+uPHYFBrs6eJ9LWU8G2/EULjJeAga30urUmqCfTAAMxMQC2AAAABWQAIAAAAAAIz6wOrQqBXWAOmerbQckRy4fLI5AC9k5DhBJ3kHJYJgVzACAAAAAA4c2luh9MO6xSNqffZoH4SwI19i3ffdJ9iyAaGitoZxkFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMQAAAAD4jIxUQRn5M+M3aEB8y8gSZVxGM9DWUnOXnxW4nWvwVBy6M6dsIbMKK0ntDsHoUK5wAAMxMgC2AAAABWQAIAAAAABy6E2SiWcuWFUFZF7u1+9q36Bidd7cB0PMwxN4qUm/iQVzACAAAAAAfXXRP8HSe9qTC4Mhpwm/nGty0swMvdl3Ly7ZwgQ5jjIFbAAgAAAAADPuDapB79oBpsUKLXh9oeESX6227xBMIb3JHWmpWCcvBXAAMQAAAABj+GDDYudxPSCKRvpd2JD43W6j6WiKDj87cuSFutQ1viBwlIcebxdNQCrQk2lc4mTZAAMxMwC2AAAABWQAIAAAAADdcutpKLPVhdR6vTKi+/9b/ngd8WF/gvYzoSOKPGwhLgVzACAAAAAAWm36BOmL6gbNykGY7pHDbJZZEHDxt+WMyNRCYL6dKX0FbAAgAAAAAMd299Bokg6hihom7FZAtMDxLS3VJ+aIngv5kdIdD36KBXAAMQAAAADpphQq0q3J5VDQffMiAdBMfNbxzAka6gLTsD/8iu//3n5Co1mSi/PpkEYj7vm7G/xnAAMxNAC2AAAABWQAIAAAAABMvmSKiuV9tNx7pKKhKPEBb4RlXlptLoUC6XzzqwZM/wVzACAAAAAAQNp806yyelkCYf3mmkfm6CZgaegIJok0BRG3T4wihSUFbAAgAAAAAPsI3ldHLkKkInpttNugj3cAyjtn8+aYWSppd6eF4IP6BXAAMQAAAADaAPXQlyrss/msgXPCjTYcuJoAMlULfUlYiGl8glGovkB5QT5Dyp3Z42oSnLxkuYXiAAMxNQC2AAAABWQAIAAAAACWCYL1HRq7/1vOBbRFajjqpNxf3fVtYNrMKH0CWMxlqQVzACAAAAAAgrBrcFAmjTruvGSRfTuTnuF2+uXz/Vtr3g60RqkG4bYFbAAgAAAAAIQEH2axT3qVk3JPY6vLwoE1x/AdRl7Die4AwXOlTSRPBXAAMQAAAAAUsy6h6usThQG3P97tMBSo39YOF/xc2h+xndzc6fbNpbMRUPw0GhpRxIcQgigl6IF8AAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAAAAEG14AIfWEgAA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/encrypted-payload.json000066400000000000000000000154451521103432300307060ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "BAwUAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVjACAAAAAAcbn1OIyvGiS6W/5leV27c3oDoTgAztFUyVRUBlIxC+UFcABQAAAAAJ3yl9lkq96PEts2moPIDCJnQjXxK6fAZMVxZW9WjZD3MMg7ki5u4CF85T/fdloyha74DDPjn1Os5ser2/D9hdpMRQWVtHJA8ZmjgousnQzvBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAEAAAAAV2AEQAAAAAq83vqxI0mHYSNBI0VniQEtG3g+8K9ns3yzddqS3RKsr6MaNc+IWACwxRIwkr+2l4I9DNIjltuvtmbOTj/WvyjOwhHuYFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBGcAoRIAAAMwANUAAAAFZAAgAAAAAEXyPll8pDgONFsvw5VnzyCe0TNwEePYnQZeRFNW0fTqBXMAIAAAAADNAYCK38aHlQp6Vb784Eip1hnuvLCHL+EKg/cqOkSFfQVjACAAAAAAoXykEqXZ1AkJfX3MDyNouG1Q6TgmlhnFAMABVsUhKvIFcABQAAAAAPTG+RBbMcNo1xxYOPFqmone18YO/x8SWFkyTUiX0Xgfqd1Qo5oRFyX8rJrHzGewFfUQTJBI84TJcpeN96KDdmwNHBN+wbnHqhax1JdFIBSVAAMxANUAAAAFZAAgAAAAAFP67MLPvLGnu43f7PbTa/FDk05X3zkDzTMCrHzrfXuIBXMAIAAAAACi0k9AqpAY4ZSQxZpZb0aVuoakd3Hxj2ZJfu0GFTPxgwVjACAAAAAA4j6rpV8TuAI93NTrRBJ8anTCzhf1nCjZ8lY/CYhxlU8FcABQAAAAAOD6SY9UmJPLh8W9rY+WOcGROFQBbv74nKxs/RZo5vz4keuC1bxi8oW30mXlZZOefKn3DcCnGc+wcZu+HhM2oR3xmeNmUNoZ//zXGg6a+qgkAAMyANUAAAAFZAAgAAAAAICoxcn9LaNlkM9Ak4YTDkg9xmcW9c1W6sKiuMw/Xb85BXMAIAAAAAAbywcEbo+80MFUNGgd7BCenXYJvF+wwXnSj8adVq4UjQVjACAAAAAAsnVl8o25cgDc2D3GXaZcalRLg5U5LT93XUWZhiswx3sFcABQAAAAAIf4laNtTAxemuN2gVDnb6F2M7LeyzBwN2bTF8FJ8Uke3sFFCEdBnC+63U9YJwNBMTrn+HRbWipUubgeHLzhzPlQ/dVppK8aQIFMBz/GMUP/AAMzANUAAAAFZAAgAAAAABa91Qu2KZFQjidDvZdxtbiJgcgk5T81y3pdlHtXY0xcBXMAIAAAAAB+3EKOsVfcR0d87sZwagcu2biF+eZ6Fb7+fhIR2BSaCwVjACAAAAAAri5RnYYbOuGV02gJVninpStPMfrKWo9qQRSKQ3UUhcEFcABQAAAAAMfekKz3qTMgEkc9mWWzoSt2BiSVFe1rku7ROF416pJfXeIDTSHhD5FuGMZ92BxlZl6LYlPhqY6VjrVAM+hMhIN3eC/JQGff+f8TwMrsKm0LAAM0ANUAAAAFZAAgAAAAAFL4aHYc/8mpwuAcBxcPcShnLqV74mFex6P0zv2G6qZNBXMAIAAAAAALtlteiH0O+hP/CQV59KCyPE4AFlbbdXCrQYkyUu1bowVjACAAAAAAgE8p4vJm3X2KseHhM9eRGBMaBIYc0AMZ9GBLIB2KAYsFcABQAAAAAH5K57wo1v6F3PVPdumG6cODI8r3jWY8FWxZk1RAw7BaF36boaMrsgOmklWTacBjckwz267Qdhnp5QhZ+15cTII7SmUBMiutESYer3z8TF76AAM1ANUAAAAFZAAgAAAAAB2icZVZfz2wRrrQ/2xAZU+fVk//9fCDMQl0UvqwXuMSBXMAIAAAAAC8M1zqSVhPqWqlH4Tlctn7yzv641vsNoxeX4BItnKbNwVjACAAAAAAdA8ua5LVZB7r+d10pV4q0GXPSgmNbaE5Y3mWwMihr+oFcABQAAAAAL8fSDOmnKg/FmESSGy/m8JUZUQ6fsqXhGxcXxTBIm2HhCmKRI52VotIZnZ77cO3/Lxl21dnHUg3743UDK1PwXMB50vak4o6k4JyrQuM99lMAAM2ANUAAAAFZAAgAAAAAJiezAizo5OYttRYE9ywvbfKDaGxX6hP/4B1CvWoKxX4BXMAIAAAAACYJbdQKs0dHVK3A5308Q21fYD6htM3arFOPa0OFWHFpQVjACAAAAAAI1NMx/WKK5VL1xY824jIklsksHgonlzIWD7CN3w/MIwFcABQAAAAAOrrGqfH+Bu8pTCDouxkQ9rqjQB7Nz59n+e7BBTgxcr+1dksN/LhAitmq/eYLbmz1P96RtWdk+cBlDQLFnqsmn41b/j2ABs+4X7bb20S3yJKAAM3ANUAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVjACAAAAAAQICwcJmriXc4AQIaNS40F0lLZqx3wvJLRt20PXr3f9cFcABQAAAAAIarDydCpSFo/NqlzaMC6E+CKXLeM6GK/crAB7B0DuDw/BC/Pr8JivuFuw8KQ5KPVmuD9lR+UlgsEhmex8SHcMBr0FSgxgDDEVnhrJ9RLqSJAAM4ANUAAAAFZAAgAAAAAOGTIi2oH64nUUX5oSm85A8OU+VdEuZMoanwb2Z+XT5WBXMAIAAAAADaPRu2UYTb7bXYGOvr4OPV09U11n+VBOgwBZV/wD/NkQVjACAAAAAAWBmWpKZl7Wk4CxJSLX0Vl4q8H/6qK+tYm5EnFph+VX8FcABQAAAAAFxMgKSCgWu4v44quSEjsAGeZ8/5hjczCAEe4jxW0ZuGcr505o6tB/kEcV/uaUTV/rGxYoml0QSHUoUv+Z4QS+movoxqvVZBLHk307ZbwlCcAAM5ANUAAAAFZAAgAAAAAKFKEINemcMfzCAv6lVctfqI87n9eRwDJUg1LA8kdYwQBXMAIAAAAABo00sYyBR3ZxuRjiXM98saXonlBzYiXgf3RANye7jRZQVjACAAAAAA0AArQZukaIcCh0oF55wyCDF5+R6PO/lR2FZ2Y4BFJakFcABQAAAAAGZ8Ofypt2EEsmGPX+tv0vK1RsoklEPqysHOCzVkC5eTaFo2BGF1jW1foVXyEgM6w5tHtgcK+k9FdFOpYzzb3lfkQBkWfb5NgpXUyTo/HBYmAAMxMADVAAAABWQAIAAAAADvt5mVfNvJXYC4cEi/R+z0r5iZb+1q7wchwAABkqH+CwVzACAAAAAA/xC56Vn95f0jaFDT9Et9/JCiKeRNVft2ffRP9xT14dUFYwAgAAAAADfEAX4qKIueyJeHerFXH/nUphIbk49SXdWN+DbpnX5WBXAAUAAAAADLBVQCW9s1i387ZRgAamYr1rqy0ouCiYhfGSW1/U4OyUTxWDUTsZ88WcdNX69D7m7kxgVlgO4cwtFtWd9uoTburkrel2hHG6+ale0n2nq/9QADMTEA1QAAAAVkACAAAAAAE+06hBFHl4f7CHQFGGj2ZZuT3WZEMqTj4CzUM2e9PeoFcwAgAAAAAIIlGRvBtZCaXT4OWFMXF+3VC8jzuJFe3mR5RpM/T47OBWMAIAAAAAAlxGwgJ4opwcXsuQz8Zg0DT+BsaCqmHZPmLl3oA49W6wVwAFAAAAAA+IyMVEEZ+TPjN2hAfMvIEga0+pIOL/lFmJe8P6j9H/LLhA15C80/qcQQsYfVz7l5VUuPZ9G+22Vx3TJtK2dnt9DAqc2w1LYeUQnnNqBcCQIAAzEyANUAAAAFZAAgAAAAAK8IiTdn87RqrKys7Cb7BKa82lkWwwkFcGFIgEEuJPnYBXMAIAAAAAD11EA7J1kxw2jXI2H1c/slLVwDgQQEP3nA3e3o1nyk1QVjACAAAAAAKYlbcijR4iBbLQs+Q39c8KXcm2V2QII4h7bWmxBS9RkFcABQAAAAAGP4YMNi53E9IIpG+l3YkPhVzzLtjgFEJsCuRMXornEHZl5FyhbW8UOv2eS5uxnIg/CboBkKwZhMnr+26SRCfiBrSZT0XVnDwEShWaqt1F1LAAMxMwDVAAAABWQAIAAAAABiid+a+1N9DG1AUCL3XIHEFuzBvCm2JXTeDwna0EUXEQVzACAAAAAAsMJjIl8B7w+wRM4QYHw4+C9AGo8kof0Ibc+NouaIJGwFYwAgAAAAAOxJdVZQA/NJuL94O2pqI5bYXJIft+RUXjlbWz0dGhkEBXAAUAAAAADpphQq0q3J5VDQffMiAdBMlnlo6r+Q7wuuPrB0BAIESsdbqaZHnettNV3sLKGuFu2L8ELHIWgD1vjvxjg+mN3b8yhwycpMarUBDM9JtDEnjQADMTQA1QAAAAVkACAAAAAAYLNccfL4s3DMHDChNpne5is7B1RfWUpctFbO/r2JTAMFcwAgAAAAAPOdU0Owvp1pFujAWWt7nKFVJxcXTMoVw3ZZiheO1DB2BWMAIAAAAAB0DabCvUfAGj6K6Ry6WaWsVlaZeslmA9OWTl5kgvchnwVwAFAAAAAA2gD10Jcq7LP5rIFzwo02HAvdL6JJB5p5TAFUw3Nt0vczPj/BByYBLpAiL8S+kgzWlio8tgt8SQ6CL0TmJfIVUFty7E5zUh7Z1ZIJPtjPjYYAAzE1ANUAAAAFZAAgAAAAAEumusJlA1iuUJrW2KS2QWaAtWKoSfYITPYZh8KjMQzEBXMAIAAAAAAj2X8l6KLHNOvaadrafChrFonWgd76J/QAmu2+JqnLeAVjACAAAAAAiSplqMUi/3btwQp4dNvnfvwEVfOMefpEgxZiJLcwf2oFcABQAAAAABSzLqHq6xOFAbc/3u0wFKh+vxpCRNiQEbT70ZdOsXZQRO58mBkdZs4aE0l6p4rCT/XDkHFnKxSABaJSwYrX7+ok7rXLCPcdts0jHoGWt2ubAAMxNgDVAAAABWQAIAAAAADUq7UIfHJv/mGbsXxGaSnrk1w2OaF1XIQOdL3Ibv7jAQVzACAAAAAAVYSgjDnwcfQ937v4LtelZhXljZRVaPPKpZ72Eg6Qp5wFYwAgAAAAAIULgFieXwksSKKSQ9E38lNj5TNqwuc9iMiH8EWn3AcuBXAAUAAAAACcTE3N5TGx8bkORR/Ia0y4uhYovi207Txb3LlHV3oiy9569XaF6DqC2N/uE2c104pmICu3tm22Q//PSxZK8jpnNmwQX9xpkhpKkyTbg+sIRwADMTcA1QAAAAVkACAAAAAACM+sDq0KgV1gDpnq20HJEcuHyyOQAvZOQ4QSd5ByWCYFcwAgAAAAAOHNpbofTDusUjan32aB+EsCNfYt333SfYsgGhoraGcZBWMAIAAAAAC9mttUKF7kto9nk7cCYVJSrNb0KORWWG+WEcHGb0bUlAVwAFAAAAAAeSVEkfNb3GOskZMsvHSnXfej9PFNop6OW3cd5v6oxUNXi6vOZnpMoU3iaB6yxyFZoD8tqflWMilQ0Wz7xG5qsfraV6RQvEZtZvuTYzaKmAIAAzE4ANUAAAAFZAAgAAAAAHLoTZKJZy5YVQVkXu7X72rfoGJ13twHQ8zDE3ipSb+JBXMAIAAAAAB9ddE/wdJ72pMLgyGnCb+ca3LSzAy92XcvLtnCBDmOMgVjACAAAAAAYHLbyOeKwYLum4Mh2GlrGBvuHsDCY1p/NwT6hiT5o+oFcABQAAAAAMtGR3nVaR9mUvg5STOnphZFMPWv1NTKcHR5/YpliLLP3aXCpkoZQbR2lnbI4AZGQW6VbBZcYN+7t7GOUoBElFxEpUdNkW7Ih56YoEg7PQkFAAMxOQDVAAAABWQAIAAAAADdcutpKLPVhdR6vTKi+/9b/ngd8WF/gvYzoSOKPGwhLgVzACAAAAAAWm36BOmL6gbNykGY7pHDbJZZEHDxt+WMyNRCYL6dKX0FYwAgAAAAAP4bzR6xzN8nVkz4TD0L15Q9stgAYpjaAWnWe4GUfaFeBXAAUAAAAADeGXtHUgcY58PiPYhPz3cNhByYMp97SrjKHNll6rE9SAazGhLNMEVsBYl9TWuUUDqM2GFynVJsPOTMVuqRZS+0hLs1/0ZKUZN7mymtAFZP7AADMjAA1QAAAAVkACAAAAAATL5kiorlfbTce6SioSjxAW+EZV5abS6FAul886sGTP8FcwAgAAAAAEDafNOssnpZAmH95ppH5ugmYGnoCCaJNAURt0+MIoUlBWMAIAAAAAB0MUvEoSdgGXnBu1+mfTcOiQ3A1eE4FH1nj0Tylm2stQVwAFAAAAAAVtWeCh+hrH6DmWWvm3YxpANb9hhaQv7wDesCAKRCR34sjLV7x2iW2cmANDak09+M1LdPmpYsReD5WbZBi61RgpW2v28BVU3pTiSVwSpwoUQAAzIxANUAAAAFZAAgAAAAAJYJgvUdGrv/W84FtEVqOOqk3F/d9W1g2swofQJYzGWpBXMAIAAAAACCsGtwUCaNOu68ZJF9O5Oe4Xb65fP9W2veDrRGqQbhtgVjACAAAAAA9F+4LBoDXc8tAQCe20b7X5/IRnA/vzN4XDsEbk2tfFMFcABQAAAAABmjwCYvmGFcjINULL1HMaOOH+OUNQzJbXlwSymyfII5FgbNkdj94Sm4Mk2K+At1OgZPgS2dNYiPYegxAFyE3PGlCDuDPOJ3i3Gf5ZXmyK0uAAAA",
            "subType": "6"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/rangeopts.json000066400000000000000000000002311521103432300272470ustar00rootroot00000000000000{
    "min": {
        "$numberInt": "0"
    },
    "max": {
        "$numberInt": "1234567"
    },
    "sparsity": {
        "$numberLong": "1"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/int32/value-to-encrypt.json000066400000000000000000000000641521103432300304670ustar00rootroot00000000000000{
    "v": {
        "$numberInt": "123456"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/sparsity-2/000077500000000000000000000000001521103432300254345ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/sparsity-2/RNG_DATA.h000066400000000000000000000034761521103432300270360ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x82\x1d\xde\x43\xe0\xd7\x03\xe6\xd5\x97\xb6\x3a\x5f\x17\xe7\x8a"                                                 \
    "\x92\xf3\xa3\x00\x34\xae\xda\xce\x0c\x65\x1a\x6f\xec\xb1\x11\xf2"                                                 \
    "\x90\xc7\xba\xb8\xef\x3a\xda\xc7\xb9\x84\x59\x7a\xfb\x84\xde\x4f"                                                 \
    "\xe0\xe3\x1f\x38\x97\xfe\x33\x84\xab\x51\x30\x59\xb9\x79\xbd\xb7"                                                 \
    "\x30\xa7\xe0\xe7\x3d\x1c\x36\x41\x98\xba\xa8\xcf\x5e\x5e\xd3\xc4"                                                 \
    "\x89\xe8\xd0\x7a\x09\x4e\x21\xec\x9f\x1d\xf1\x5c\x15\x79\x93\x9e"                                                 \
    "\x56\xce\xc9\x6a\x88\x97\xce\x8d\xf0\x61\xec\xa1\x4d\x02\xb0\x76"                                                 \
    "\xb9\x78\x56\x79\x5b\x91\xc9\xed\x71\xac\x6e\x43\xdb\x57\x49\x2b"                                                 \
    "\x58\x48\x55\x0e\xae\x4e\x99\x01\x54\x14\xc7\xec\xc8\xa9\xf7\x8a"                                                 \
    "\xaf\xa1\x90\xac\x71\x70\x01\x04\x19\x98\xff\x9d\x2c\x5d\xf9\xf3"                                                 \
    "\xc2\xc1\xb6\x07\x3a\x8c\x21\x6d\x98\xe5\x43\x74\xaf\xc9\xe3\x17"                                                 \
    "\x91\xc9\x37\xf4\x46\x13\x12\xe4\xcb\x86\xd0\x4b\x97\x2e\x5f\x8e"                                                 \
    "\x2f\x39\x43\x1a\x2f\xb2\x20\xec\x21\x30\xcd\x9d\x96\xac\x16\x57"                                                 \
    "\x18\xf4\xa5\x50\x41\xdc\x33\xce\x3e\xc2\x19\xd4\x86\xd8\x8b\xd6"
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/sparsity-2/encrypted-payload-v2.json000066400000000000000000000054521521103432300323060ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "Cw0IAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVwADEAAAAAgh3eQ+DXA+bVl7Y6XxfninmZIQZ+sQ7WyLlws3WpBj2V9JefzsCOiuT6KQOhMYepewV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBKS86MANK7azgxlGm/ssRHyq8/Uyo0dkvdkrAmXKIeVGpaXHgPrkQPaku3m2Hoyo/m6dk4NRwzn+qnLc5ZeOCTBBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAAAAAAAAAAAARnAIYGAAADMAC2AAAABWQAIAAAAABT+uzCz7yxp7uN3+z202vxQ5NOV985A80zAqx86317iAVzACAAAAAAotJPQKqQGOGUkMWaWW9GlbqGpHdx8Y9mSX7tBhUz8YMFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAACQx7q47zrax7mEWXr7hN5PTp0iteKt69e9FYcDvVXWTleTFW9tJNgqJDQWuU/RUS3RAAMxALYAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAODjHziX/jOEq1EwWbl5vbejutxP/Bf9gDs/aPda7K0C+EKlLvC36ubca4vIESrf/S0AAzIAtgAAAAVkACAAAAAAoUoQg16Zwx/MIC/qVVy1+ojzuf15HAMlSDUsDyR1jBAFcwAgAAAAAGjTSxjIFHdnG5GOJcz3yxpeieUHNiJeB/dEA3J7uNFlBWwAIAAAAAA5zR9OnG49Xrn3grU7zBDon+oj+/1FUIOptLvwesWUvQVwADEAAAAAMKfg5z0cNkGYuqjPXl7TxEQ2O460tHDZWQnksxhfHSbQeevCruptBsK/IZIOue6YBgADMwC2AAAABWQAIAAAAAAT7TqEEUeXh/sIdAUYaPZlm5PdZkQypOPgLNQzZ7096gVzACAAAAAAgiUZG8G1kJpdPg5YUxcX7dULyPO4kV7eZHlGkz9Pjs4FbAAgAAAAAIFEk9LC+wpdnt+iy3OTuzvc/W/4y7GWyhLN8YIFWpBZBXAAMQAAAACJ6NB6CU4h7J8d8VwVeZOenFQxk0CGMM2n+BIS515zjVXXwGoYnm2oMJTnK0gdALpwAAM0ALYAAAAFZAAgAAAAAGKJ35r7U30MbUBQIvdcgcQW7MG8KbYldN4PCdrQRRcRBXMAIAAAAACwwmMiXwHvD7BEzhBgfDj4L0AajySh/Qhtz42i5ogkbAVsACAAAAAABcm2+tbs0VSGFn9rDvWyvhuIRVDepRdoIvNa6TRv3zkFcAAxAAAAAFbOyWqIl86N8GHsoU0CsHbMhy+E+/XyNZ1tXAQf9TT/xLFZQh82dG/bScdnVDxaYgsAAzUAtgAAAAVkACAAAAAAS6a6wmUDWK5QmtbYpLZBZoC1YqhJ9ghM9hmHwqMxDMQFcwAgAAAAACPZfyXoosc069pp2tp8KGsWidaB3von9ACa7b4mqct4BWwAIAAAAACLCFou5eC94e2pRXNjJ8oPkENRCDGdw9eZP7Kvgb2mCAVwADEAAAAAuXhWeVuRye1xrG5D21dJK/+kDae+zJZWl3XTmtJRpiHP/ZvwLOX2YOaWQg7xOxjWPgADNgC2AAAABWQAIAAAAAAIz6wOrQqBXWAOmerbQckRy4fLI5AC9k5DhBJ3kHJYJgVzACAAAAAA4c2luh9MO6xSNqffZoH4SwI19i3ffdJ9iyAaGitoZxkFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMQAAAABYSFUOrk6ZAVQUx+zIqfeKhTieWPwdQO2VjJzbglvxj8UJCOUtTHgXrmU7pokhrX3zAAM3ALYAAAAFZAAgAAAAAN1y62kos9WF1Hq9MqL7/1v+eB3xYX+C9jOhI4o8bCEuBXMAIAAAAABabfoE6YvqBs3KQZjukcNsllkQcPG35YzI1EJgvp0pfQVsACAAAAAAx3b30GiSDqGKGibsVkC0wPEtLdUn5oieC/mR0h0PfooFcAAxAAAAAK+hkKxxcAEEGZj/nSxd+fMkfh9FaI18ylqvr8o/r8GXdYCOlChCH3qMCgyZaWVrmtkAAzgAtgAAAAVkACAAAAAAlgmC9R0au/9bzgW0RWo46qTcX931bWDazCh9AljMZakFcwAgAAAAAIKwa3BQJo067rxkkX07k57hdvrl8/1ba94OtEapBuG2BWwAIAAAAACEBB9msU96lZNyT2Ory8KBNcfwHUZew4nuAMFzpU0kTwVwADEAAAAAwsG2BzqMIW2Y5UN0r8njF5uRRaD1QoYr+tEoCmkZeEn+koE/a+dG3UHNqrlvybXqcgAAEnNwAAIAAAAAAAAAEHRmAAYAAAAQbW4AAAAAABBteACH1hIAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/sparsity-2/rangeopts.json000066400000000000000000000002311521103432300303250ustar00rootroot00000000000000{
    "min": {
        "$numberInt": "0"
    },
    "max": {
        "$numberInt": "1234567"
    },
    "sparsity": {
        "$numberLong": "2"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range-explicit/sparsity-2/value-to-encrypt.json000066400000000000000000000000641521103432300315450ustar00rootroot00000000000000{
    "v": {
        "$numberInt": "123456"
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/000077500000000000000000000000001521103432300216005ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/README.md000066400000000000000000000003541521103432300230610ustar00rootroot00000000000000The expected payloads in fle2-insert-range were obtained by printing data from the Range_Allowed_Types test:
https://github.com/mongodb/mongo/blob/ad4a7ae485fd0189542e59b3350e798b42b42d1b/src/mongo/crypto/fle_crypto_test.cpp#L1003-L1021libmongocrypt-1.19.0/test/data/fle2-insert-range/date-v2/000077500000000000000000000000001521103432300230425ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/date-v2/cmd.json000066400000000000000000000003531521103432300245010ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$date": {
                    "$numberLong": "12345"
                }
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/date-v2/encrypted-field-map.json000066400000000000000000000011741521103432300275710ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "date",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/date-v2/encrypted-payload.json000066400000000000000000000365721521103432300273760ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "CzoqAAAFZAAgAAAAACxUAzRVk8NErNBCwzKRaTy3/xun2/1RDV658SGvJN07BXMAIAAAAACEurCf7Rmlp9BSlue1Yws6jtkvIRca4FO4ah9Cj64nSQVwADEAAAAAIDH9QTKXt1Z+rEPDqkiTSCQV8mpXm8H75lZyNqoTnNeNHAYoq1al1MtaANY2qPEb8wV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAkAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBK2SUdsP789oj0+i7tWMdGMgvdGqgC5brqJawXV7Udo0YeMNUi8JryXhUvdY7Gesn2FeyZ09TMKLMFKtoyrV6OgBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAWrTENSXeHuAFLMfZfWcr0BoceR7+AFPu8u6mvu6jyDYSawAAAAAAAAAAAARnAKsoAAADMAC2AAAABWQAIAAAAABtBdb3QX2WJCsITu2mTWK+IJeXmkxdVfvV1QG2BCZgwwVzACAAAAAA5cqU8NODm0xq0/DpXKpnkarfWgvJrX4bAnalkwdDVPwFbAAgAAAAAAdMqrfuwkg9m1BjDlsIZG7Ruuy+PM7R2t5r9wvVs+5+BXAAMQAAAACsKR8j/0KMxVVJ/i/Wou21DCQX9eluDVgLnO65jNLwHr4Y/2GOLwVo/btiSCuwlNXCAAMxALYAAAAFZAAgAAAAABkUzDlTUF7/0HLvWhtDfutHq3uST8Hk8O/Mfb8LgFfNBXMAIAAAAAD11twfbtpOWFUqQFXS6P7TaXobAhu46STaaYWePYOJGAVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwFcAAxAAAAALaQI8KJXWbptCcz+ZAC0oeuNH1TKEkFFsY4VNXG/T3jx+YLEhximth40UQxVWrGdxsAAzIAtgAAAAVkACAAAAAAbfXWI0mudr9ZVWBKFn22/utVf5u6g726PURHBbaJQGQFcwAgAAAAAIlshJPXAu11tfCNCUi/LUbvrpx3juQCTxU0UPa7Y5raBWwAIAAAAAC2y2AT3AUAIH+THXhbHeSIOiHwC3SYGtcfu/+AR+Pw9AVwADEAAAAANaKrA5XH+LtBqcsobxY1YNBSdS3tq7UKOl2GIa6/jAq9NwjGyXfjM7uKNfB0N+EwZQADMwC2AAAABWQAIAAAAADZOyEGcAF4JghkBbbLL2bpfPXQXYSJH7sCwBmU5x8jnQVzACAAAAAAtx5lK5DJc64PXFLy5RhDSWzHQgJbPtGFZwG5Z3X7vp8FbAAgAAAAAJVJLlomdCfrwUo0BiYj1gLUWbmXbtpXpCNIaklpSybNBXAAMQAAAAD7ibQdKMCghVq8gAzLgRfkTfQZ2Dio1iT5bwZRmiiHwyb3m71Zs2mBxvQD8/CA8JIaAAM0ALYAAAAFZAAgAAAAAAfhXpq8CIJ5QopFc+cxk7mBFxVvMrBzg9+imWGFekUaBXMAIAAAAAC6+fZfve+VRAZcqaH2epeBGR/3C+x0jiHq57yCXeWMTwVsACAAAAAAlKCvNqLgQ8qRUmFl3qcXIzO3Dd0dt+Ki5RZ+pV4sdsMFcAAxAAAAADbIWafpa2ycEPfZAHaKyAmaJyGxVwHcLRKkcR0A+/BBdknfuDwVrsnWautWoqtFl6wAAzUAtgAAAAVkACAAAAAAtRRQ7NnJVHYiqYnfjWiV+rLDuH/b6XXvjQpR/MaWFKwFcwAgAAAAADsj6IcnWajT284OviCDCy4JrrJ4x8pGEyToKpvI0F/QBWwAIAAAAACzFioGR0V2QocQSlne51Hg5I0r/HnZhv/FTqVrsJNQhQVwADEAAAAA44ayYNJ+ZvToS+4E5v1l3gxW30kX8YF3SKICBU3m9OUyT/k9y+RivXRcFcwwZnq2hwADNgC2AAAABWQAIAAAAABFkObx9tHsA2pO9xssfYF4FY8bOtfROdkbh9LPkRtNQgVzACAAAAAAu+KihV7MzuyavpyywhghJttPCHCuExwbmEVeBfgsiNQFbAAgAAAAAKml0YhgsnwZaLYfV+pRLNjp/3iGh/OPGutPU3qsbCpFBXAAMQAAAABobsPcWwQz9NurvrpK3X82EWjDGTZWcfosWXJt5yXFvjdtD8b+lErRP+oem5ksZy1GAAM3ALYAAAAFZAAgAAAAADUs9mdMn6W7ipQIc0pCyeLcH8nnfaCR3NWT8FW70G7ZBXMAIAAAAACJQ6BLo/n9M6IKEJG0qkCwEHDq+e66FI144nJmQu6BJwVsACAAAAAAgl0mrrmNBiqNI3HmbpQ9RD5uLGJxOP82xgIC9S8f4FgFcAAxAAAAAA3ap38f/dTzEvkY6y5kHRNLTvKpB3jIu6t+eRRl4DWjaojo2otTl6p4iZCUBRwFDkIAAzgAtgAAAAVkACAAAAAAZsRlDopg1j7CKkKPiF/FBhY8EfjbpoTMjVU62nux1rwFcwAgAAAAAFBtT/2O+G+w7CkmR0qQpts/WjwFFc0nSWoceR//TpXxBWwAIAAAAADd1al/mqUZmU7gjhcn4oOc2nWzHnGO9oIssCkjLsCI3wVwADEAAAAALCaGaSsKKqLjgXHg3hh1T6MdRE3t8idD9dIMV4Il132htsddeyuvCOC5M+xk6wJ9owADOQC2AAAABWQAIAAAAABCY/arAmB0dhPdgSsD2O2MKS8Zm9w/NM14Ul4/t2m0QQVzACAAAAAA1hAC/S4aPVgJZEy44ccuZEXtUfvJqhel36PoDvJTaMgFbAAgAAAAAPJ76Y4pNKf4wkzMzrH7aTOFlyxrlOC3xm87G2tksNeqBXAAMQAAAABSmlcROalQd2vVvj22J7fl8BNKpwdDOi0o5SABPe+PeTVTdXcmxlDnz0hZFYshhHJLAAMxMAC2AAAABWQAIAAAAAAD5ap4Zz/9ppwEPoNI7SCQXI4775qCe93P1g4QugvM1gVzACAAAAAA733Q7KcRX03qqJgqhvvhpw2wNh8AeNC5YerSR0lN61kFbAAgAAAAABMenDPSCUH2VtMJXCeeW/cAYEzkx077+VbdtaXEU9yfBXAAMQAAAAD5QfpfOiFvpL/fGb+pDYgRsRl+cBbuFfo1/OYUXdY3ZNa6euDp4pMnOVTDlh6N/mAiAAMxMQC2AAAABWQAIAAAAAD39XzqGv68dsH5ySh5LWfeH7lUts4G/6x7VUDdVPTxKQVzACAAAAAAyTcchuWpTT+P96GJIVMDKo5MQx+zMLAxf41Cuk14MWIFbAAgAAAAANOvCNUkvYx67mnsyJEJtuVOPPjpFOPwfKUit4MpaJ7IBXAAMQAAAAD5ohCfiYr1x5oJJvygre/BD1bMGaDa5s5decA5TeloC18srgLSHUjUoxjfrbYnxoPMAAMxMgC2AAAABWQAIAAAAADxiRdMzUKZc1r2hZRUFqX0LdgKRHqDmVlPbs0bUeX50AVzACAAAAAA9wpXUxV1OEmuc0dHzXspXZXHVoWfXBcLxEWLQeo6cMAFbAAgAAAAANHVa+gOEk04jpiDz9ULrq6F9P8pbK2XK13Jo/51O0WHBXAAMQAAAACt0mYArr6Q0TEzPDImziX8gjWWluUVOszRZ2xdGxu29+PWhpQduMw2ljV6iI6v8Y4RAAMxMwC2AAAABWQAIAAAAADyQzjt4QY3zi3k8Ah9fBGr81aad5IgujDdcS0MYN+3IAVzACAAAAAAYL95wR9p1+TV3sHMrt1JJUHUyeT9WBXRIpaYsGVlD5UFbAAgAAAAAHWca2n387o8zWu9Dpyb8bQrFzOoOjqvn/TLENPna01eBXAAMQAAAADMH/cbk5YeR38ipv9B1rx+YyBhsFWuVlCi8hK8br2PO/idEPal8/bsBxeLICzo90GuAAMxNAC2AAAABWQAIAAAAABu1Dm41RKKNpDTT4Dy5M4WkrEZBbnX+qPRArq3CzJDtgVzACAAAAAA0TZqY/JBg3jF3JWHiP/UaSyeyPyYQ5NelvKrkRzfeI0FbAAgAAAAAKs3WXer1MGm0HjS1HvZL4T4M4AUwbibvwPqn14Z2wiDBXAAMQAAAACQETUrlNTV3JynVglk/zsJK4jwx8QbZDn2uEuKj+PYS7L15HATkbas/mJ73eAvVLhpAAMxNQC2AAAABWQAIAAAAAAjwCumu/AAqhybP4qQuxnTRQQrwUE9pYfPAhoS4GtrgAVzACAAAAAA+RqDeKGmdzZazkZYjIqnmiEB72Guj93ZAxxRYkXv7hQFbAAgAAAAAB4VCRTOM/d5515b70RJICiud9h5EkOZZPxqTIDWdC9YBXAAMQAAAABmADqdUbWvhDYoVhQbdgRhttHCt4VK2nJ9j98f8WtdDm50wsHyeBAkE7TsDRKEal/4AAMxNgC2AAAABWQAIAAAAACwKOIjgTVb5QsqN37DBd9GFeCITbLEW0fuz+U3yk+yVAVzACAAAAAAeCrvHLUSAgi+uXbJkT8mlVz+aDfm4ctNZbWE1ba0oD4FbAAgAAAAAEcrExN3Q2XKr6A6PbtzDw1wo2XpfKR1x1KnvUxoOjafBXAAMQAAAABPSDWT5HC3OjKXbmU4lmV9Oz8hm1f5geN8M8DGyybTZ6wCE2qry7vTrcGs3be0RRD5AAMxNwC2AAAABWQAIAAAAAAbVE3rEkNDFOPwU4qV/p5YXNlndlQlpRJxja49qu/b6gVzACAAAAAAA97ms43SrXShC2SN2vCKLNqJvK8F9um0tgBj8xusewgFbAAgAAAAACZYAkIoiNruIpglaYJnUf4Gp8Ak0UoE3BtJrBQgKvktBXAAMQAAAADCzjqeWzK6t3fIsC5i1yhCooMg1nW56xtudE0652OML8OHz5+x5KL8m6oi4lZe+yQFAAMxOAC2AAAABWQAIAAAAADC0jLaxGyMjTDe4ehaY1gjsZF/xMGi9MeTqbq2QvA5AAVzACAAAAAAQUTZYQIjxGq+9U/VdbrzEWfg8GXNiALTC3wDrT/jJjIFbAAgAAAAAHvfQ+SUlS5qgwLZHG7fplgx0c6FsePgwQp1BWi34mIDBXAAMQAAAAAin6A6xYTr2f6PyYFqso8/DmxTaZPrKRpzM/MNxjJLw0GkcOhrE5TQbKXFVVpXVynnAAMxOQC2AAAABWQAIAAAAADPdazrIZrgwi6QoZ8J9ljahL/KcNx7Wk2emkf40tJerAVzACAAAAAAVuLG4ONsvQw+Q/AjkZv99tSXsEyAhw6bzvXnj5682pEFbAAgAAAAAM92c5AB6olkZNQSPGMHPdisd9atRA8dSDPqQ9O60k29BXAAMQAAAABjKpEslTJiCZY0cYHe5daTyyYarMwaiQKD/udAo2b3pmwMQZLpqqtuNlIYVCoKGNWsAAMyMAC2AAAABWQAIAAAAAAcmtT7X80NPa+Iw1WmpNGjoXI6g0s87VhdkpzNreJPDgVzACAAAAAA6ZDCwGG/W+lHkB2LoCAfUSWbdZkWikAMNYZsZCzunvsFbAAgAAAAAFKv7tdW86NhiHgOgH90sXPv+s+Y5IKYTd/jtWE8uQ6wBXAAMQAAAADjKm5Vip57hzXBDVsDxACXbKfwVNJlHPVvz967mYnaRvkTMp3Pfkr1wTYQotd7fMXaAAMyMQC2AAAABWQAIAAAAAA6aMJ/MI87IIoIiEF2vFpJfGv66TPM2A8LdNMy8nbOSgVzACAAAAAANfWvqKbxj+M/SEy4Fe7NjCEV9Lsx6lpwRZlpxfJ0e78FbAAgAAAAAKBzKJ1mGtwkPGyhBFUHqq2D9+d1i0cXx8LO7Mu6+WGBBXAAMQAAAACYNMbl1mVUNLUqXoKOtAmKretEBTXfZmm/Wy7ilMSwc+O0EaCqyUqafTKh00BmTsKNAAMyMgC2AAAABWQAIAAAAADWPNwzmiHrt6ZQlP0Ut3LF6kvCK223qw+WCWtBqkweMQVzACAAAAAAcZPKDdjFkwz/lHNLaryAAfKwSUiGaX/m1uMfDzRFEZgFbAAgAAAAAFvEgwvkP/zItTfoSS6kbuykvVpZF4zy5kLONbUIehASBXAAMQAAAABGTJzHEd3nlYeU5Cfbrgkj3ga8nlwp2Rwau3oMXyTrxdLfIuLub1Ud7JU3+L/vKLdUAAMyMwC2AAAABWQAIAAAAAC89sOm2YsdUm+bR+oiN7gmSH0y2HmTp+kg41/qCWDjtAVzACAAAAAAbfXRNDGOtQe7Vf/jOfg9AHYTfRxjI8KawxjZUGo8LJQFbAAgAAAAAFmJ4r1eo2ezXhN+tFBs49B/ydvxpsVVl0r7E9ODBbvjBXAAMQAAAABeOWbMfg/BmFtcILLRZY5Ovfllcfm08dQYKKxmuk1jnL0kgqIFr9MzofNQ2rmufNtMAAMyNAC2AAAABWQAIAAAAADV1RA6Z4gTmH+8SwKNniZEDwyWDp3L6NQQn7dmHbzvOwVzACAAAAAAX1bNGPPM1ieTMwAzjn39vp6HmDhVHXdG0r7WmaOPosIFbAAgAAAAAOZ+khfop8s9hIN8IKZAiA73TU4TQ3PbRpusTW1PuVM4BXAAMQAAAAD2+aGCv9XZje8PIIB4YLlo4RkN0K9D1TYfBaaH50nkaclx233LjVS5z1VGZlthYUJxAAMyNQC2AAAABWQAIAAAAADjl9wh/M8Ws+s+f3uZZ78ReLw6Tw5A2+Xes3bnLavenwVzACAAAAAA7GbEHMLkksxpz5gRASyWTzk+mmUi4OjHp00fmh63TioFbAAgAAAAAAGMpWq1izDU+8i10mYuKr2PTkt2AxfJuTWr2mSC92ECBXAAMQAAAACin2EAlKenk2O1Zi/CFq/gIxvPwQq3I0d/zyCnYCnhCuRxqjFjBHnVzEd5LsSk4Nj/AAMyNgC2AAAABWQAIAAAAAA211J44HUaihjnd5lhKL2NBYg3EKnqsO+nX8ImVbciOgVzACAAAAAAxCGinNVKQIi+ILP3uS9Gf6dzxOkTXlEQ2SPN1QtIfkAFbAAgAAAAAL0b2SnYDSAK7yTWg2JOXsIcJbo+HjHf19M7Z/fd0s4XBXAAMQAAAACw920mUJfVseJQC18DnrSJd9P/bc5FaoDQJTqwu0zXoRl1ePAgmzIEVASo1G6y5/vGAAMyNwC2AAAABWQAIAAAAADFI+3B5vg/FM8Yo/Q+uw0FGpytqO5fNaQCwcQDkxK0VAVzACAAAAAAz35xWYH02sb27aj/3FRKV/TvXTg6LXm1iK32Z8vi2bYFbAAgAAAAAEti5Gx+5MQ8hSXjScWnZtAyTZRWDiW9hNL2mTKVMb36BXAAMQAAAADef7Zyjk/MMK8EEVRetU3Xhlc1viBzuL9gv2MB0bAjpFaz5CYy5YYf75GremzhUMTSAAMyOAC2AAAABWQAIAAAAADI//fkuQoNhthgL4BSXtoiLEGlPD5DH6oxhmCljxAFWwVzACAAAAAADHw/flwvQjJV07/MS9TgNm/WGKd1nCpEx6YfSgG5n5wFbAAgAAAAAFIio7cgaeaxhiso3zKlAkxWUO9U/P/DPKyYb/8uSnLeBXAAMQAAAAAO4t521QTIDETst60fUPbHQ2WM+H744JAR0WmlAMX1k29tRi7eTC0gtNebA+Zl+nQjAAMyOQC2AAAABWQAIAAAAAC4RFIyTn9Hwe9mKlZ1rDnDP2qsexic0aWblXoMjXJjHwVzACAAAAAAgejsQ5XLM728jIAZFN7ur1GQL9a3+W0eIEPlqoBDR2oFbAAgAAAAANI+/+6cRMwnRsi34UZwnkx9voDxNdieDUtK7LxqRJYnBXAAMQAAAACtp1em52SZnRYgXnGULjgFahSB4xs9AVceHJJ6fTrdTtPGzOszJiGa+lsm92h44O11AAMzMAC2AAAABWQAIAAAAACyYI7pAkJn0ZLpb/STBzivuIUOQtslGoWoy7GUBNiZgAVzACAAAAAAhRt/fCj1MiL+o0tKVERzuq+Jzlcn5PbihJDpgVB45YYFbAAgAAAAAGTqW8GjK17otIjlTIzxrV1xQga4FcBrA7A+xdtl0tr2BXAAMQAAAAD/CJ0QrgNT7sANBDH9CN1jmuHM/HMkMlOHb6xrDCZOO+i4+Qpma+cNNJNfVDrAVVhsAAMzMQC2AAAABWQAIAAAAABhKCCJcHv6PPoSeeWkIgpbGBvd1AIPqec0uOaIC0TLeAVzACAAAAAA9Nycpzjz5U38hFDgDJudRRShlIu/rQ7LWAeOwZqoUYoFbAAgAAAAAAbly8X+Mub6NBWXINvidid2QcxZ4m10egFLFAi3eGdvBXAAMQAAAAA+XWWSDiOTq/4ks+bpayxZ7nMm9AD2DLDmSsNqTSw25DfwgOJBQvNx7FKL4Rwyvl/EAAMzMgC2AAAABWQAIAAAAABNZuRlihtfaqNnoBDydeY8UKLS23SeC6wzKSYQcucB2gVzACAAAAAAgZeZOxt1HgTFpllxCL+dyHQ9sS90yIJP5zArtbOXK58FbAAgAAAAAAVKyk5o3jfTT9fNKoxv4bAI4IUh7b731/cxq8bTHw71BXAAMQAAAABCOgN1F8amTc5CZI4XGRqF0Deq3bqn0J7uLwvidzmH3MY6IAbWbR8wqKp6q6jfYGsvAAMzMwC2AAAABWQAIAAAAAA4VgHa8nkZDpB+mFGpP1f/3zkDZBMOZZODHB5MNVTGyQVzACAAAAAAY2WxEKSWasTdEArnyPUYSC+t3nP7XFBidbyBbpTLwUAFbAAgAAAAAGD+HrTf+Dm2pec7sfQE0sTBn56Xe5mGa9uRktv507f+BXAAMQAAAAAEJfsyCQ8a7Zw4juwP1FbLLnDckn9Cy339LK/dFAbSS++dH2TupBeg35FKBfs+5JtyAAMzNAC2AAAABWQAIAAAAAA4UJ9wrwca3HXMlqxg6unQQ15riCXUbj1W/DdORdPlwwVzACAAAAAAhN47p2OR1eXxTA4LKrLHclhz1ZFkL48FIcM8nccCdkgFbAAgAAAAAOa7e4VjPLXsgWpHjx59Jjp0lRtNQnCiHD7m+h/4WH7WBXAAMQAAAAB0ozEV/+j0MzIosBzgH1m6QIvp2/1di8bshigJr0S76x1rhsvpv8UbqX/DZ/gV2U5qAAMzNQC2AAAABWQAIAAAAAAJ0Pt4dKtjnyVb1thTaISla/i4jiPhz8N0Ghd5LiE1GAVzACAAAAAAWCYTGX/n3zuBS2H+SzR3vbrioEpBdJg8fFqkn+1UyqYFbAAgAAAAAFbFeb6nQHc6xA/0pWDxSwAg8ggzKnDQsEKBVgVYA0X3BXAAMQAAAABKJ8y7nNdiD2PHZlhMIPHrUxoCDkK3DlOsChW2IWjZRGwqzcPIl/VQt1elHBfbOwovAAMzNgC2AAAABWQAIAAAAADPwlAIo2eWIRIcL/STZF8XBpP6m6ho1NXryfst25/5RwVzACAAAAAAvpx8JeFUvRH+KIz5iPBsQtGLGYm2nYCdfflgwYp/OLkFbAAgAAAAABWSHwnZuA/xpexeFHQjY84GtN5uqCeAoWzZVGx9rc+lBXAAMQAAAAAh8vbV5y7s/25dajl9Wdk8GGVV867p1/u8K5uGRuiymRhXE54NDGfVfYcXWyBb4wuwAAMzNwC2AAAABWQAIAAAAACrKv8yJyuCSN/sF868TmUMyGm56cgUw8K00A7l7A909QVzACAAAAAAO/ci8K2AzdeZGSQOZDsFxV1bkUHI4arCUbayCXKy0+AFbAAgAAAAAGrYu/cBp+fmbERxKL//Nk5n1POA3JTibF6Hb3TAdif9BXAAMQAAAAC70iu6sfzd2VDLiPgLA/AQw6Cz/G3GC6n8uNRaKqA4bx/778NB8VE1YAd8NrfQ2+MhAAMzOAC2AAAABWQAIAAAAADLCGMjdFr57IaVOXEo+FXv3UCRlx0ZhPc118RhJhwBCQVzACAAAAAAhj+vNK1jvrY/gsj5F4qj/a1Brkz60qT/UtfNLZFpdzkFbAAgAAAAABsOQvPkTk3CuAnzT9R0s9pgX5MAW6IFyp8/uZg9aD85BXAAMQAAAADw3ctzqWvdsVv+T5GCv0aRpLlSWZy1b3lLcSGJE1Jw6A8R9DeARaQMRjhXyAFwXpEcAAMzOQC2AAAABWQAIAAAAACxUCjKA0EABi1SyZPj+YZUt8fXiJE/LCmGFGyLtQsG1QVzACAAAAAA1Fsvj77Sny5WJA7CqGn7JzlAYi/MhSJkmXxF5Sbs9ZoFbAAgAAAAAGAAvNzKxMApZhZm6OCEMXQsH+nvLVF2PzhdJz75OjLJBXAAMQAAAACZDus+h56Cf/nYwh7mrmk+WrsA5ZwP9w6fEA3cjMIsnKEQbDWNhS0NBaLsCC68feB8AAM0MAC2AAAABWQAIAAAAACi6krX4ScMD0CtZIZfw3YF3Nx3aTS/vhOJbtyb82cZDgVzACAAAAAAhB6OENn01zyJVvXw/kzUB75iPmRy15CvAbjflCMvVv4FbAAgAAAAALjkljRc2NfpD/+RkvCPjuHHqjlYtJrQfoeuqGYcNa/jBXAAMQAAAABPjZ4SLlOed9N+Hwm+cAAP+XTbMlNDGjs3tYWv/m2BHV4pBGzqY7J2Wab689h2L9c8AAM0MQC2AAAABWQAIAAAAADATIi32gVPnZZTXzTchaT8BWgmNmlkHoOs0Rylgf2mhwVzACAAAAAAZQjhCr7k5Sf4aRT1BfLLI08gKOwEzHTe76/CxkyqRd0FbAAgAAAAAKFlx5lbI3A6PG8Bi5qFE5wglixslsU864zyAlgLITz1BXAAMQAAAACzi8cKy62+3wQlO0iTTWpIunjioQ8IIT+UtFx5q1B5lnf8BN3+Ny5x0cdIsrsQlDNXAAM0MgC2AAAABWQAIAAAAAAYFmFG+cOQ8tU6mGQqWCHSozWNr5De99aTNgYUjWL0wQVzACAAAAAAu34K9K6LYHVQ+6ZNCxgbn7ocCOySO0b2yhwRT46Zt5UFbAAgAAAAAL8ModrlQFhh+HoiLnshix6FWVZ6vj/u3Hf/tqz0ILhjBXAAMQAAAAAyvlPdQKHb90pPkOstM7Jl4djlNi7npZCPSWd2GdTg/JrO8NRQQgN65ftJsoztBt1BAAM0MwC2AAAABWQAIAAAAABE43z15SkGkbVaeq/8T1M/Qj6HH1iRzc5Nk/RqTmzqjwVzACAAAAAAiemqPXqiRIF7KrhFvcAg8fe7RpWFZjTvpv/wxKS7Dk4FbAAgAAAAAD94TI66OUeTv4sF7sRWTZevpKem4QuNZpCLvdEYJV/9BXAAMQAAAACdOo51YfZ4iEApv8wYS0uCCGIqwW9sYgv+BtoYea89PhCd983BRtoCfpt33Pu9irLiAAM0NAC2AAAABWQAIAAAAAD04iBvtqQJ0sxvaQY1F8f7G7JaOzTUlG5YqUjskemKZQVzACAAAAAAg22vw20VMiSa37C3Y1B8L0SeJH3Pajn3DVJ+ZXA9lGQFbAAgAAAAAP+/4X7+RtZ91rSfsyKn/ZtwrehGvtccd7sTeehtjoXHBXAAMQAAAADfvX6suupGMyNICW4dytkTecWRihF7KmGNRbTDyxMIsGIvRgSXW/bH5vgdgOptMhmZAAM0NQC2AAAABWQAIAAAAACaYobIuD/VItT68Wfsp9n6InFYq5WUQ1F6p6SRR1OZdgVzACAAAAAAsTOOl0dOlqIB0+IxKZYg2nR6b2RDmxgA20/tDA+LRvYFbAAgAAAAAAM0Urow0V+VVb3wfCD+JsQL9JmactxvImLKDc67gC/HBXAAMQAAAACpu3w4fZSzo0RbaMznBUHDDC3BQBW9B5fJcUhOu7AtV4iTJ3okqGrWsmMo0I68nG/wAAM0NgC2AAAABWQAIAAAAAB9sHgtNaEVup2QWw/L8Ni9Z18C8woIlMevSAPSV59wygVzACAAAAAAnD3VrxzjsKeLpVAuZWUwIFAGKRvbXIYsn0XDrATAgWYFbAAgAAAAAOWxM+qBKt7M2YiMOJmKufV3+igKet9HCB7D0gFYN+dTBXAAMQAAAABUeBgBdftFRA8QFjdu5M6j5w1aBnFV/HnE1j02w7dtbHLB+Ma6r+/qhy2nvJhzNU0WAAM0NwC2AAAABWQAIAAAAACYasy6Hu3s8ZzhGfdr0zkM9SzqoxzAh5r3tj4CIiyp7AVzACAAAAAA+UCObKt5eV/Sj7Qw3ohPXNdCObn6wmiBnFvtgZjjCy0FbAAgAAAAAA+OeRacUyADCFfALtevyR/eKOnRlFYG4hD/0gJQzim9BXAAMQAAAADkgo1zVuAHyZOFzCuWCkPNFHZhE/1jAyEWDo+0/xkqBf0AeeviY6HyhsvRFwiVxS3HAAM0OAC2AAAABWQAIAAAAADQ0fbnr71YFxcgR2Nqoy5cRn8Rh0r1q0dGGSLBUz32vwVzACAAAAAA6A1/d2XJLCuo18ubi5GB56qaGzeZdhNy7jk25r8SiWkFbAAgAAAAALA3Wr6IBcul7H3/Vi8dVYnsJS5hG6vADpF7TxR2byn/BXAAMQAAAAALFsuABgGMvpIudlqB0CzoBibrQT7KEQYGcC9Aacpaw49lgvQY+d48qgfWPqXJSFC7AAM0OQC2AAAABWQAIAAAAADSHr1UaiJie591ZH76qy4m6dz/QtGpPibbxxBHIh0J+AVzACAAAAAAyMXuGbQXqzO5UdJbKtzl7rWmxY5FN6bE7vzBkSb+TYIFbAAgAAAAAKX0B3z9Z2uE/uy3I3eWTDAbgCQeK3wqogbRuxLayIXtBXAAMQAAAADZWG+GWuU6F8aeGtRgAH4ze3YjeD0wk+tIeSQcHk2wYVlCPwplOHg1xtZ7dMBwwYvNAAM1MAC2AAAABWQAIAAAAAC+M/YcC8OFXEBakek/l0JE0umfIWAOXL8IK8QGnjYEGQVzACAAAAAALU3KD1+KdQD53xC+HEBfZcfnreZMdWn4Z2ym9w7+PIIFbAAgAAAAANDgc6pzXF+EMTQf5RuLH4zH6Qi4cFdAUUtAAccjBsyCBXAAMQAAAACZgQIIqfeGSvuJx/1gdFcHbS7pUDQA4fza0Jx/5DXQKDYJn/3FDizD9aWyygyV1ntNAAM1MQC2AAAABWQAIAAAAAB57gwojrlYBFEkkaaN4he+PKIpF5Z8nClvE58RbFLdKQVzACAAAAAA58LrR+y1vyThNlgZDd4VXYOdVC0kHSimnnLDk9N0jEsFbAAgAAAAACuJKk5jpD3TtLeaocnSHC6OrccKVrk4NZ2qDV6D896bBXAAMQAAAADIoOBXTpCJ05sWSVNPAYCbGOCjn5B+c73QXzIHzFXr97Jn2GjqlgGGxW+1PywznA1FAAM1MgC2AAAABWQAIAAAAABXG7NNfXwq2YVE0nsYOE4pJXUb+H+Ui828VOtD/kPc9AVzACAAAAAACp9e0Mcqrjp0x3IGlvPVVl0wxD2x++PKX998bsSsZVMFbAAgAAAAANMD5lFgOaIXlH4hnSutrapZFO5rVAsPhXok7c2IVDg2BXAAMQAAAADRUI7+NoYxc8x+MjHjFIOdMvqOmk0XKhyvzHRJPVti/TWOl77W3VJIucl0B1Nv8TuQAAM1MwC2AAAABWQAIAAAAADjHp5zRYwtMp2SvsEvCW7D5BGk55LdMVlQshDAJLYp2gVzACAAAAAA8lCknIPc9beoZu572/g2+3jT/0SpVDyf5trgqiiAzWYFbAAgAAAAADJ7Q0gMy7N4cZ6tQiVLfRUBGK6mCxOEofq0yTxnlLzNBXAAMQAAAAA06n1TUN+d+CJBtXP6pG2NNMREWo5eKjSEkmCD5/l2vjKd1O0zuQ04i2Ilo7hqbDMEAAM1NAC2AAAABWQAIAAAAABada0u3jyKuPh7cgm53Ksa52TCzihyEAQ4rFW1pIP/zQVzACAAAAAAUpe3MVoJZNWkTOVPJw3DGrnVW0ZnnjfivtAj1SXsHvEFbAAgAAAAAINI0eTxgqtyTHjFUJax5eSqyKkJaQQeFQedINIXsiAxBXAAMQAAAABauLdFh6UdJHt8MudnMUixOPGBp9zPPj6fU3GC0I2gkd5M5ouJg0sjk1s9jQqGh2brAAM1NQC2AAAABWQAIAAAAACbBGUbUU8sEjLsD1B4rWnkUybcADFr1FphLepRFcvcWgVzACAAAAAAM4IJJqINwLj0DGi6dTJrqnQdFovxRk3seJUNUhTn2EkFbAAgAAAAAJj8F6Vq5FB7TexntKvDJxeaEJu/E6Y2PbKxsonQJDc6BXAAMQAAAAC4QVFiX/AaSL28XvXDnsqW84eJRn+LuLj+zohJA6Nih+TiXRJWwbk6IgTOEe4A2LjmAAASc3AAAQAAAAAAAAAQdGYABgAAAAltbgAAAAAAAAAAAAlteAAVgel99BAiEQA=",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "date",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/date-v2/mongocryptd-reply.json000066400000000000000000000033721521103432300274400ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A4kAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAKgAAAAl2ADkwAAAAAAAACW1pbgAAAAAAAAAAAAltYXgAFYHpffQQIhEAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "long",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/date/000077500000000000000000000000001521103432300225155ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/date/RNG_DATA.h000066400000000000000000000173401521103432300241120ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x20\x31\xfd\x41\x32\x97\xb7\x56\x7e\xac\x43\xc3\xaa\x48\x93\x48"                                                 \
    "\xb6\x49\x47\x6c\x3f\xbf\x3d\xa2\x3d\x3e\x8b\xbb\x56\x31\xd1\x8c"                                                 \
    "\xac\x29\x1f\x23\xff\x42\x8c\xc5\x55\x49\xfe\x2f\xd6\xa2\xed\xb5"                                                 \
    "\xb6\x90\x23\xc2\x89\x5d\x66\xe9\xb4\x27\x33\xf9\x90\x02\xd2\x87"                                                 \
    "\x35\xa2\xab\x03\x95\xc7\xf8\xbb\x41\xa9\xcb\x28\x6f\x16\x35\x60"                                                 \
    "\xfb\x89\xb4\x1d\x28\xc0\xa0\x85\x5a\xbc\x80\x0c\xcb\x81\x17\xe4"                                                 \
    "\x36\xc8\x59\xa7\xe9\x6b\x6c\x9c\x10\xf7\xd9\x00\x76\x8a\xc8\x09"                                                 \
    "\xe3\x86\xb2\x60\xd2\x7e\x66\xf4\xe8\x4b\xee\x04\xe6\xfd\x65\xde"                                                 \
    "\x68\x6e\xc3\xdc\x5b\x04\x33\xf4\xdb\xab\xbe\xba\x4a\xdd\x7f\x36"                                                 \
    "\x0d\xda\xa7\x7f\x1f\xfd\xd4\xf3\x12\xf9\x18\xeb\x2e\x64\x1d\x13"                                                 \
    "\x2c\x26\x86\x69\x2b\x0a\x2a\xa2\xe3\x81\x71\xe0\xde\x18\x75\x4f"                                                 \
    "\x52\x9a\x57\x11\x39\xa9\x50\x77\x6b\xd5\xbe\x3d\xb6\x27\xb7\xe5"                                                 \
    "\xf9\x41\xfa\x5f\x3a\x21\x6f\xa4\xbf\xdf\x19\xbf\xa9\x0d\x88\x11"                                                 \
    "\xf9\xa2\x10\x9f\x89\x8a\xf5\xc7\x9a\x09\x26\xfc\xa0\xad\xef\xc1"                                                 \
    "\xad\xd2\x66\x00\xae\xbe\x90\xd1\x31\x33\x3c\x32\x26\xce\x25\xfc"                                                 \
    "\xcc\x1f\xf7\x1b\x93\x96\x1e\x47\x7f\x22\xa6\xff\x41\xd6\xbc\x7e"                                                 \
    "\x90\x11\x35\x2b\x94\xd4\xd5\xdc\x9c\xa7\x56\x09\x64\xff\x3b\x09"                                                 \
    "\x66\x00\x3a\x9d\x51\xb5\xaf\x84\x36\x28\x56\x14\x1b\x76\x04\x61"                                                 \
    "\x4f\x48\x35\x93\xe4\x70\xb7\x3a\x32\x97\x6e\x65\x38\x96\x65\x7d"                                                 \
    "\xc2\xce\x3a\x9e\x5b\x32\xba\xb7\x77\xc8\xb0\x2e\x62\xd7\x28\x42"                                                 \
    "\x22\x9f\xa0\x3a\xc5\x84\xeb\xd9\xfe\x8f\xc9\x81\x6a\xb2\x8f\x3f"                                                 \
    "\x63\x2a\x91\x2c\x95\x32\x62\x09\x96\x34\x71\x81\xde\xe5\xd6\x93"                                                 \
    "\xe3\x2a\x6e\x55\x8a\x9e\x7b\x87\x35\xc1\x0d\x5b\x03\xc4\x00\x97"                                                 \
    "\x98\x34\xc6\xe5\xd6\x65\x54\x34\xb5\x2a\x5e\x82\x8e\xb4\x09\x8a"                                                 \
    "\x46\x4c\x9c\xc7\x11\xdd\xe7\x95\x87\x94\xe4\x27\xdb\xae\x09\x23"                                                 \
    "\x5e\x39\x66\xcc\x7e\x0f\xc1\x98\x5b\x5c\x20\xb2\xd1\x65\x8e\x4e"                                                 \
    "\xf6\xf9\xa1\x82\xbf\xd5\xd9\x8d\xef\x0f\x20\x80\x78\x60\xb9\x68"                                                 \
    "\xa2\x9f\x61\x00\x94\xa7\xa7\x93\x63\xb5\x66\x2f\xc2\x16\xaf\xe0"                                                 \
    "\xb0\xf7\x6d\x26\x50\x97\xd5\xb1\xe2\x50\x0b\x5f\x03\x9e\xb4\x89"                                                 \
    "\xde\x7f\xb6\x72\x8e\x4f\xcc\x30\xaf\x04\x11\x54\x5e\xb5\x4d\xd7"                                                 \
    "\x0e\xe2\xde\x76\xd5\x04\xc8\x0c\x44\xec\xb7\xad\x1f\x50\xf6\xc7"                                                 \
    "\xad\xa7\x57\xa6\xe7\x64\x99\x9d\x16\x20\x5e\x71\x94\x2e\x38\x05"                                                 \
    "\xff\x08\x9d\x10\xae\x03\x53\xee\xc0\x0d\x04\x31\xfd\x08\xdd\x63"                                                 \
    "\x3e\x5d\x65\x92\x0e\x23\x93\xab\xfe\x24\xb3\xe6\xe9\x6b\x2c\x59"                                                 \
    "\x42\x3a\x03\x75\x17\xc6\xa6\x4d\xce\x42\x64\x8e\x17\x19\x1a\x85"                                                 \
    "\x04\x25\xfb\x32\x09\x0f\x1a\xed\x9c\x38\x8e\xec\x0f\xd4\x56\xcb"                                                 \
    "\x74\xa3\x31\x15\xff\xe8\xf4\x33\x32\x28\xb0\x1c\xe0\x1f\x59\xba"                                                 \
    "\x4a\x27\xcc\xbb\x9c\xd7\x62\x0f\x63\xc7\x66\x58\x4c\x20\xf1\xeb"                                                 \
    "\x21\xf2\xf6\xd5\xe7\x2e\xec\xff\x6e\x5d\x6a\x39\x7d\x59\xd9\x3c"                                                 \
    "\xbb\xd2\x2b\xba\xb1\xfc\xdd\xd9\x50\xcb\x88\xf8\x0b\x03\xf0\x10"                                                 \
    "\xf0\xdd\xcb\x73\xa9\x6b\xdd\xb1\x5b\xfe\x4f\x91\x82\xbf\x46\x91"                                                 \
    "\x99\x0e\xeb\x3e\x87\x9e\x82\x7f\xf9\xd8\xc2\x1e\xe6\xae\x69\x3e"                                                 \
    "\x4f\x8d\x9e\x12\x2e\x53\x9e\x77\xd3\x7e\x1f\x09\xbe\x70\x00\x0f"                                                 \
    "\xb3\x8b\xc7\x0a\xcb\xad\xbe\xdf\x04\x25\x3b\x48\x93\x4d\x6a\x48"                                                 \
    "\x32\xbe\x53\xdd\x40\xa1\xdb\xf7\x4a\x4f\x90\xeb\x2d\x33\xb2\x65"                                                 \
    "\x9d\x3a\x8e\x75\x61\xf6\x78\x88\x40\x29\xbf\xcc\x18\x4b\x4b\x82"                                                 \
    "\xdf\xbd\x7e\xac\xba\xea\x46\x33\x23\x48\x09\x6e\x1d\xca\xd9\x13"                                                 \
    "\xa9\xbb\x7c\x38\x7d\x94\xb3\xa3\x44\x5b\x68\xcc\xe7\x05\x41\xc3"                                                 \
    "\x54\x78\x18\x01\x75\xfb\x45\x44\x0f\x10\x16\x37\x6e\xe4\xce\xa3"                                                 \
    "\xe4\x82\x8d\x73\x56\xe0\x07\xc9\x93\x85\xcc\x2b\x96\x0a\x43\xcd"                                                 \
    "\x0b\x16\xcb\x80\x06\x01\x8c\xbe\x92\x2e\x76\x5a\x81\xd0\x2c\xe8"                                                 \
    "\xd9\x58\x6f\x86\x5a\xe5\x3a\x17\xc6\x9e\x1a\xd4\x60\x00\x7e\x33"                                                 \
    "\x99\x81\x02\x08\xa9\xf7\x86\x4a\xfb\x89\xc7\xfd\x60\x74\x57\x07"                                                 \
    "\xc8\xa0\xe0\x57\x4e\x90\x89\xd3\x9b\x16\x49\x53\x4f\x01\x80\x9b"                                                 \
    "\xd1\x50\x8e\xfe\x36\x86\x31\x73\xcc\x7e\x32\x31\xe3\x14\x83\x9d"                                                 \
    "\x34\xea\x7d\x53\x50\xdf\x9d\xf8\x22\x41\xb5\x73\xfa\xa4\x6d\x8d"                                                 \
    "\x5a\xb8\xb7\x45\x87\xa5\x1d\x24\x7b\x7c\x32\xe7\x67\x31\x48\xb1"                                                 \
    "\xb8\x41\x51\x62\x5f\xf0\x1a\x48\xbd\xbc\x5e\xf5\xc3\x9e\xca\x96"                                                 \
    "\x7b\x0d\x99\x90\x09\x41\xce\xf7\x4c\x43\xe3\x3f\x46\xe1\xef\xb3"                                                 \
    "\x72\xf1\x8e\x9d\x15\x3b\xb6\xe5\x23\x82\x87\xbd\xfa\xf2\x77\xcf"                                                 \
    "\x16\xe0\x8f\x09\xcb\xf6\x18\xbf\xc6\xd1\x2a\x71\xac\xeb\xf3\x10"                                                 \
    "\x36\x1b\xdf\xc4\x6c\x71\x32\xa5\xe1\xb2\xa6\x70\x2d\x26\xcf\x7f"                                                 \
    "\x5a\x84\xbd\xff\x20\x2c\x8b\xd8\x93\x3c\x2e\x37\x35\x6d\x70\xf1"                                                 \
    "\x7b\x58\x23\x68\x61\x0b\x46\x33\x29\xb0\xee\x40\xb7\xc9\x34\x55"
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision-v2/000077500000000000000000000000001521103432300257475ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision-v2/cmd.json000066400000000000000000000002511521103432300274030ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {"$numberDecimal":"4.56000000000000"}
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision-v2/encrypted-field-map.json000066400000000000000000000014331521103432300324740ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "decimal",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               },
               "min": {"$numberDecimal":"0"},
               "max":{"$numberDecimal":"1234567890123456789"},
               "precision":{"$numberInt":"2"}
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision-v2/encrypted-payload.json000066400000000000000000000424031521103432300322710ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "C74uAAAFZAAgAAAAAJPgxng5d9BvF2NmJhq6HebD4r8SSeIKxMO3k5PkUCXYBXMAIAAAAABdjidA/oHhq1QP8nzQznCnuiB80Sub1533aHdYO1w1OgVwADEAAAAAUdvWRovLQ43+5yScmS5BOfztpibQF4NTt++mYwxvD3GAux5IDTkjsLVhrq6DTWP7JAV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABMAAAAFdgBgAAAAAKvN76sSNJh2EjQSNFZ4kBIYE8+bqZS4O4HBJeunsMA71I/3tQ+w9Sd9cVekqCHFMj388AW/rQckoMc2ItoLkK3EQ/JVKWlpHxS9CCz/5LJWgRJLTawpeH1NzkHiYGFY1wVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wFbAAgAAAAAFcOOyq/k5TYQ3jWP4zv9vU/eQ1a25FpvKmlRNNV4gUBEmsAAAAAAAAAAAAEZwAHLQAAAzAAtgAAAAVkACAAAAAAZ6NCkW8mT21UPhnKowS6rdai19G2/lN5T5whXVq569gFcwAgAAAAAAbBNXPUvNU7Nc5dWCEq3XCGfP4I72dUX8lE8U6NvxM3BWwAIAAAAADCPK3ldC88r2jagaUc2sZVcvIFT3BCS2sIPNudYJsiKgVwADEAAAAAFxfim3Ps2X92xE9jKDwF8hLIFBeDJ+wtn20s/UpkTbfZ5x/KnGhZW9oLPrqkJh9aGgADMQC2AAAABWQAIAAAAAAZFMw5U1Be/9By71obQ37rR6t7kk/B5PDvzH2/C4BXzQVzACAAAAAA9dbcH27aTlhVKkBV0uj+02l6GwIbuOkk2mmFnj2DiRgFbAAgAAAAAGLaTnfAXe5P29Mx/H7MZX5gf0sGNJnDqmLJ+CSCLg0MBXAAMQAAAACcfUivrmONYmAxbgyuSJ9Fpmdc9fKmPTN74aawBOoN6QEJzRtNegTQbZBR8cs+xf74AAMyALYAAAAFZAAgAAAAAG311iNJrna/WVVgShZ9tv7rVX+buoO9uj1ERwW2iUBkBXMAIAAAAACJbIST1wLtdbXwjQlIvy1G766cd47kAk8VNFD2u2Oa2gVsACAAAAAAtstgE9wFACB/kx14Wx3kiDoh8At0mBrXH7v/gEfj8PQFcAAxAAAAANLDilvh2Nkj5Dfnilpqgg6HoYQoOSawF0ywdhhKwilawQimMM6u4Pha6ndKT/acW+QAAzMAtgAAAAVkACAAAAAA2TshBnABeCYIZAW2yy9m6Xz10F2EiR+7AsAZlOcfI50FcwAgAAAAALceZSuQyXOuD1xS8uUYQ0lsx0ICWz7RhWcBuWd1+76fBWwAIAAAAACVSS5aJnQn68FKNAYmI9YC1Fm5l27aV6QjSGpJaUsmzQVwADEAAAAA8EsuhPYCrv1FR88/sSy0/VWgB3WI+FlfifPqbPakFXjEvDtj3pLRryHOS3V/ehcmTQADNAC2AAAABWQAIAAAAAAH4V6avAiCeUKKRXPnMZO5gRcVbzKwc4PfoplhhXpFGgVzACAAAAAAuvn2X73vlUQGXKmh9nqXgRkf9wvsdI4h6ue8gl3ljE8FbAAgAAAAAJSgrzai4EPKkVJhZd6nFyMztw3dHbfiouUWfqVeLHbDBXAAMQAAAACu1yjqRPmwftilkK5R9gDAFkY6Q2GeQUeBUGFicpmWEMLSteTgY1cSsZdZ5InbiaRcAAM1ALYAAAAFZAAgAAAAALUUUOzZyVR2IqmJ341olfqyw7h/2+l1740KUfzGlhSsBXMAIAAAAAA7I+iHJ1mo09vODr4ggwsuCa6yeMfKRhMk6CqbyNBf0AVsACAAAAAAsxYqBkdFdkKHEEpZ3udR4OSNK/x52Yb/xU6la7CTUIUFcAAxAAAAAGJrKj/KLy6leP764xGZoJsSgH26V6I7W0aA39O9uBUUykp5dyRfnb9AwX+UjHkfyUcAAzYAtgAAAAVkACAAAAAARZDm8fbR7ANqTvcbLH2BeBWPGzrX0TnZG4fSz5EbTUIFcwAgAAAAALviooVezM7smr6cssIYISbbTwhwrhMcG5hFXgX4LIjUBWwAIAAAAACppdGIYLJ8GWi2H1fqUSzY6f94hofzjxrrT1N6rGwqRQVwADEAAAAAd1UMrzbEitlyAIFYNNQwzB/VWK0i97h1iZjsiFnoy1G3uD6NBEX/hJu2FKn15Fo6EQADNwC2AAAABWQAIAAAAAA1LPZnTJ+lu4qUCHNKQsni3B/J532gkdzVk/BVu9Bu2QVzACAAAAAAiUOgS6P5/TOiChCRtKpAsBBw6vnuuhSNeOJyZkLugScFbAAgAAAAAIJdJq65jQYqjSNx5m6UPUQ+bixicTj/NsYCAvUvH+BYBXAAMQAAAAAZBpP5UXoUiB5EnrD2SSs7dWSeUI76dG9lh7iAlkuzBeoW4JL+4SyoQtUmT7pbAaIPAAM4ALYAAAAFZAAgAAAAAGbEZQ6KYNY+wipCj4hfxQYWPBH426aEzI1VOtp7sda8BXMAIAAAAABQbU/9jvhvsOwpJkdKkKbbP1o8BRXNJ0lqHHkf/06V8QVsACAAAAAA3dWpf5qlGZlO4I4XJ+KDnNp1sx5xjvaCLLApIy7AiN8FcAAxAAAAAPT93PFJ1IdxN+9vTE4IexxaW4adGqOO+gAqGL7PWaUom/Vi+oIs//O3PHPqWngTlSsAAzkAtgAAAAVkACAAAAAAQmP2qwJgdHYT3YErA9jtjCkvGZvcPzTNeFJeP7dptEEFcwAgAAAAANYQAv0uGj1YCWRMuOHHLmRF7VH7yaoXpd+j6A7yU2jIBWwAIAAAAADye+mOKTSn+MJMzM6x+2kzhZcsa5Tgt8ZvOxtrZLDXqgVwADEAAAAAVw2CfqcDu4NwbL9DmfZjrvywZB3oIWM7r6pjGJM34po3lAj5ArfUHtaE9s77QpD/bQADMTAAtgAAAAVkACAAAAAAA+WqeGc//aacBD6DSO0gkFyOO++agnvdz9YOELoLzNYFcwAgAAAAAO990OynEV9N6qiYKob74acNsDYfAHjQuWHq0kdJTetZBWwAIAAAAAATHpwz0glB9lbTCVwnnlv3AGBM5MdO+/lW3bWlxFPcnwVwADEAAAAAL5Y8RPNJIvfmbaYzPktbw/m+1wPFTNfgF7OmS/Zbuc4uNUYlgiqQPE7Jdw02hF7q3QADMTEAtgAAAAVkACAAAAAA9/V86hr+vHbB+ckoeS1n3h+5VLbOBv+se1VA3VT08SkFcwAgAAAAAMk3HIblqU0/j/ehiSFTAyqOTEMfszCwMX+NQrpNeDFiBWwAIAAAAADTrwjVJL2Meu5p7MiRCbblTjz46RTj8HylIreDKWieyAVwADEAAAAA9dKp/JzvMgR2v0j0pO5jt3cDWiTMKFHB0oqlSN8PIXUyFkOPbEEkxVTuAatvOBb46AADMTIAtgAAAAVkACAAAAAA8YkXTM1CmXNa9oWUVBal9C3YCkR6g5lZT27NG1Hl+dAFcwAgAAAAAPcKV1MVdThJrnNHR817KV2Vx1aFn1wXC8RFi0HqOnDABWwAIAAAAADR1WvoDhJNOI6Yg8/VC66uhfT/KWytlytdyaP+dTtFhwVwADEAAAAAh9tIsN3OaLCeaWAgY+YOhrAeQaTbXa2WhItprPYWasOgcCvrkVseJpY0AaqSk1M0CwADMTMAtgAAAAVkACAAAAAA8kM47eEGN84t5PAIfXwRq/NWmneSILow3XEtDGDftyAFcwAgAAAAAGC/ecEfadfk1d7BzK7dSSVB1Mnk/VgV0SKWmLBlZQ+VBWwAIAAAAAB1nGtp9/O6PM1rvQ6cm/G0KxczqDo6r5/0yxDT52tNXgVwADEAAAAAEr0/nVxYAVoX46OHGL6yv5KF3DXbwqyMN/umtnXIdZWnDHCQMdSnRFTO744izmUBrwADMTQAtgAAAAVkACAAAAAAbtQ5uNUSijaQ00+A8uTOFpKxGQW51/qj0QK6twsyQ7YFcwAgAAAAANE2amPyQYN4xdyVh4j/1Gksnsj8mEOTXpbyq5Ec33iNBWwAIAAAAACrN1l3q9TBptB40tR72S+E+DOAFMG4m78D6p9eGdsIgwVwADEAAAAAzYwSdAF9567ULZHnqISasJQjrfaX3gYraoBKHW5JGjOELv3a4I04uMF/aAD97GsNMAADMTUAtgAAAAVkACAAAAAAI8ArprvwAKocmz+KkLsZ00UEK8FBPaWHzwIaEuBra4AFcwAgAAAAAPkag3ihpnc2Ws5GWIyKp5ohAe9hro/d2QMcUWJF7+4UBWwAIAAAAAAeFQkUzjP3eedeW+9ESSAornfYeRJDmWT8akyA1nQvWAVwADEAAAAAyd4+Rmtmk1TXe8SkUxBzoSqwM31sRXSF5dfp/ObPat1uTPUHZ+UFrEvFeVOuC/LBfQADMTYAtgAAAAVkACAAAAAAsCjiI4E1W+ULKjd+wwXfRhXgiE2yxFtH7s/lN8pPslQFcwAgAAAAAHgq7xy1EgIIvrl2yZE/JpVc/mg35uHLTWW1hNW2tKA+BWwAIAAAAABHKxMTd0Nlyq+gOj27cw8NcKNl6XykdcdSp71MaDo2nwVwADEAAAAA7BDpqH7ntCdPu7peZ1gKMz/L9QSkRqUpRFO6Gw6dy1NsHO7vTJYa4j/+iBIkBNaXaAADMTcAtgAAAAVkACAAAAAAG1RN6xJDQxTj8FOKlf6eWFzZZ3ZUJaUScY2uParv2+oFcwAgAAAAAAPe5rON0q10oQtkjdrwiizaibyvBfbptLYAY/MbrHsIBWwAIAAAAAAmWAJCKIja7iKYJWmCZ1H+BqfAJNFKBNwbSawUICr5LQVwADEAAAAAjnAnihLxm2C7LTcDaGecNwI1R3ZrnLU3u9xK7KoinESmpy73ozdcCFT2vgLekQ4RYAADMTgAtgAAAAVkACAAAAAAwtIy2sRsjI0w3uHoWmNYI7GRf8TBovTHk6m6tkLwOQAFcwAgAAAAAEFE2WECI8RqvvVP1XW68xFn4PBlzYgC0wt8A60/4yYyBWwAIAAAAAB730PklJUuaoMC2Rxu36ZYMdHOhbHj4MEKdQVot+JiAwVwADEAAAAAav/e8moB/ZXEaNsGYtrAYkUNyIsbZ+Ske6BUZFwfohR+67OBFB5OqRLn8wsi1q0z4wADMTkAtgAAAAVkACAAAAAAz3Ws6yGa4MIukKGfCfZY2oS/ynDce1pNnppH+NLSXqwFcwAgAAAAAFbixuDjbL0MPkPwI5Gb/fbUl7BMgIcOm87154+evNqRBWwAIAAAAADPdnOQAeqJZGTUEjxjBz3YrHfWrUQPHUgz6kPTutJNvQVwADEAAAAAPyfMqJBmZoanTBL5kh5o058x0R4OaK/9M7mCEpXSUGQo6w+cCwF+5IwQuWa/C9e63gADMjAAtgAAAAVkACAAAAAAHJrU+1/NDT2viMNVpqTRo6FyOoNLPO1YXZKcza3iTw4FcwAgAAAAAOmQwsBhv1vpR5Adi6AgH1Elm3WZFopADDWGbGQs7p77BWwAIAAAAABSr+7XVvOjYYh4DoB/dLFz7/rPmOSCmE3f47VhPLkOsAVwADEAAAAAlDD0Q4U13x86r8iaXmc1oTUDwGlRammDLY6OViajGFUFvn97pjLUCskfL3+hZbYwgAADMjEAtgAAAAVkACAAAAAAOmjCfzCPOyCKCIhBdrxaSXxr+ukzzNgPC3TTMvJ2zkoFcwAgAAAAADX1r6im8Y/jP0hMuBXuzYwhFfS7MepacEWZacXydHu/BWwAIAAAAACgcyidZhrcJDxsoQRVB6qtg/fndYtHF8fCzuzLuvlhgQVwADEAAAAA/K2awps5O3kwOg4WahHZrUly/YkxCOSDd9WnyR/ewKRQpiyJZEn42CDhic+spPRfqwADMjIAtgAAAAVkACAAAAAA1jzcM5oh67emUJT9FLdyxepLwittt6sPlglrQapMHjEFcwAgAAAAAHGTyg3YxZMM/5RzS2q8gAHysElIhml/5tbjHw80RRGYBWwAIAAAAABbxIML5D/8yLU36EkupG7spL1aWReM8uZCzjW1CHoQEgVwADEAAAAA7kpebCCfTZU74qmv0EPqS1cEpnxgA63m5KFa+F4vxNZUSm5eYOpKKaSgR75JncvdhQADMjMAtgAAAAVkACAAAAAAvPbDptmLHVJvm0fqIje4Jkh9Mth5k6fpIONf6glg47QFcwAgAAAAAG310TQxjrUHu1X/4zn4PQB2E30cYyPCmsMY2VBqPCyUBWwAIAAAAABZieK9XqNns14TfrRQbOPQf8nb8abFVZdK+xPTgwW74wVwADEAAAAAvdn14+u2/lrEAj0pnTmF8u2zBAMOsvI6MQExeWWVwKTfN55goLygB/EDJNuQnGluuwADMjQAtgAAAAVkACAAAAAA1dUQOmeIE5h/vEsCjZ4mRA8Mlg6dy+jUEJ+3Zh287zsFcwAgAAAAAF9WzRjzzNYnkzMAM459/b6eh5g4VR13RtK+1pmjj6LCBWwAIAAAAADmfpIX6KfLPYSDfCCmQIgO901OE0Nz20abrE1tT7lTOAVwADEAAAAATqyoSaIikaCfG03XIbZ1R9e5x3QriXzSo50MTQemr+neQbzWeZs+k9tXgflJ/3EAVAADMjUAtgAAAAVkACAAAAAA45fcIfzPFrPrPn97mWe/EXi8Ok8OQNvl3rN25y2r3p8FcwAgAAAAAOxmxBzC5JLMac+YEQEslk85PpplIuDox6dNH5oet04qBWwAIAAAAAABjKVqtYsw1PvItdJmLiq9j05LdgMXybk1q9pkgvdhAgVwADEAAAAABulPAV/SmR3xDG/v0b+yQ8JmRwTwQoe6FrmjJcQAoeAf8yHUNMiVWBYW1kjoq++mNwADMjYAtgAAAAVkACAAAAAANtdSeOB1GooY53eZYSi9jQWINxCp6rDvp1/CJlW3IjoFcwAgAAAAAMQhopzVSkCIviCz97kvRn+nc8TpE15RENkjzdULSH5ABWwAIAAAAAC9G9kp2A0gCu8k1oNiTl7CHCW6Ph4x39fTO2f33dLOFwVwADEAAAAAxTN0s8OM/YAl/VOsyPeRgG/RHAO27cWpDz2GIy3ZvOccrP4mDf+/0FRBYJIaIJfgcwADMjcAtgAAAAVkACAAAAAAxSPtweb4PxTPGKP0PrsNBRqcrajuXzWkAsHEA5MStFQFcwAgAAAAAM9+cVmB9NrG9u2o/9xUSlf07104Oi15tYit9mfL4tm2BWwAIAAAAABLYuRsfuTEPIUl40nFp2bQMk2UVg4lvYTS9pkylTG9+gVwADEAAAAAnqBVfvcUZ3p0hPtJtFWZyAxfw3a202kl5ibPGXoSdEIhDSYbnXkmazn2cSdDYRi+NgADMjgAtgAAAAVkACAAAAAAyP/35LkKDYbYYC+AUl7aIixBpTw+Qx+qMYZgpY8QBVsFcwAgAAAAAAx8P35cL0IyVdO/zEvU4DZv1hindZwqRMemH0oBuZ+cBWwAIAAAAABSIqO3IGnmsYYrKN8ypQJMVlDvVPz/wzysmG//Lkpy3gVwADEAAAAAKEreX+wJVt00LEZ2PoKXpedvPYjLN7kBikyhkXa2RZqjacaWV5Af2HvolmP8MJqxTAADMjkAtgAAAAVkACAAAAAAuERSMk5/R8HvZipWdaw5wz9qrHsYnNGlm5V6DI1yYx8FcwAgAAAAAIHo7EOVyzO9vIyAGRTe7q9RkC/Wt/ltHiBD5aqAQ0dqBWwAIAAAAADSPv/unETMJ0bIt+FGcJ5Mfb6A8TXYng1LSuy8akSWJwVwADEAAAAAycHLlqg0rJMI+Gpk/XLJ7lwCVB1fm4M7Pia1LSTxlccE0i3aQA5XxOWCWmFqup+whwADMzAAtgAAAAVkACAAAAAAsmCO6QJCZ9GS6W/0kwc4r7iFDkLbJRqFqMuxlATYmYAFcwAgAAAAAIUbf3wo9TIi/qNLSlREc7qvic5XJ+T24oSQ6YFQeOWGBWwAIAAAAABk6lvBoyte6LSI5UyM8a1dcUIGuBXAawOwPsXbZdLa9gVwADEAAAAAOnGUY3jEXSh5/esKuySVLawxR60V683gXTtsTyLhBmQJr5nHgqfykS3Nw/e7nFCyzgADMzEAtgAAAAVkACAAAAAAYSggiXB7+jz6EnnlpCIKWxgb3dQCD6nnNLjmiAtEy3gFcwAgAAAAAPTcnKc48+VN/IRQ4AybnUUUoZSLv60Oy1gHjsGaqFGKBWwAIAAAAAAG5cvF/jLm+jQVlyDb4nYndkHMWeJtdHoBSxQIt3hnbwVwADEAAAAAwMmn1nY1UdUrKF7eT7X/0uV45cMXqQ66WX7BA5MWUGY/cMqJb1dI8VK6QYo5lTnYdwADMzIAtgAAAAVkACAAAAAATWbkZYobX2qjZ6AQ8nXmPFCi0tt0ngusMykmEHLnAdoFcwAgAAAAAIGXmTsbdR4ExaZZcQi/nch0PbEvdMiCT+cwK7WzlyufBWwAIAAAAAAFSspOaN4300/XzSqMb+GwCOCFIe2+99f3MavG0x8O9QVwADEAAAAAvhIGBMCrX3bLDyV/Ik/b181beSSejQ7kaXn10BXIgW9U8JWbHgXnIBve89HOYxxXywADMzMAtgAAAAVkACAAAAAAOFYB2vJ5GQ6QfphRqT9X/985A2QTDmWTgxweTDVUxskFcwAgAAAAAGNlsRCklmrE3RAK58j1GEgvrd5z+1xQYnW8gW6Uy8FABWwAIAAAAABg/h603/g5tqXnO7H0BNLEwZ+el3uZhmvbkZLb+dO3/gVwADEAAAAADx9zmMwkAie1koBoP3VQWfLtEdeS9+I9flEb94vG5DkniY3Xkao/utcKaAZq3fscdgADMzQAtgAAAAVkACAAAAAAOFCfcK8HGtx1zJasYOrp0ENea4gl1G49Vvw3TkXT5cMFcwAgAAAAAITeO6djkdXl8UwOCyqyx3JYc9WRZC+PBSHDPJ3HAnZIBWwAIAAAAADmu3uFYzy17IFqR48efSY6dJUbTUJwohw+5vof+Fh+1gVwADEAAAAAvOQvK8++zgZ/VDRFKaS5Wtpj7aaPAuyHnfPJxGclM+Rsto32O0L0G6bdfR+zuhtS/AADMzUAtgAAAAVkACAAAAAACdD7eHSrY58lW9bYU2iEpWv4uI4j4c/DdBoXeS4hNRgFcwAgAAAAAFgmExl/5987gUth/ks0d7264qBKQXSYPHxapJ/tVMqmBWwAIAAAAABWxXm+p0B3OsQP9KVg8UsAIPIIMypw0LBCgVYFWANF9wVwADEAAAAAp3biEt0z3999Xc2t+V+njoWbOuWikgj010My3W/qxNZeC3+Vv5HpeVmhkI7HoHr8UwADMzYAtgAAAAVkACAAAAAAz8JQCKNnliESHC/0k2RfFwaT+puoaNTV68n7Lduf+UcFcwAgAAAAAL6cfCXhVL0R/iiM+YjwbELRixmJtp2AnX35YMGKfzi5BWwAIAAAAAAVkh8J2bgP8aXsXhR0I2POBrTebqgngKFs2VRsfa3PpQVwADEAAAAAdrLCWQuU+e8RSiTrrOgIrW7idqbok2Apz9Ua2aFj/Fu9kfgY6A5nB85a5UK74ERjWQADMzcAtgAAAAVkACAAAAAAqyr/Micrgkjf7BfOvE5lDMhpuenIFMPCtNAO5ewPdPUFcwAgAAAAADv3IvCtgM3XmRkkDmQ7BcVdW5FByOGqwlG2sglystPgBWwAIAAAAABq2Lv3Aafn5mxEcSi//zZOZ9TzgNyU4mxeh290wHYn/QVwADEAAAAAuZL4w/AhzW42oDbcm7cZc1VfwwfENG/lKpNYcm+ZqKsteVcShLcCZP4ettTSHh8spQADMzgAtgAAAAVkACAAAAAAywhjI3Ra+eyGlTlxKPhV791AkZcdGYT3NdfEYSYcAQkFcwAgAAAAAIY/rzStY762P4LI+ReKo/2tQa5M+tKk/1LXzS2RaXc5BWwAIAAAAAAbDkLz5E5NwrgJ80/UdLPaYF+TAFuiBcqfP7mYPWg/OQVwADEAAAAA/kIuBWrFD307BNtbM/ICo9FiuU9u/ulXByPuMnwxGW7yuqRP9oeDdQSrIYeFReIE/gADMzkAtgAAAAVkACAAAAAAsVAoygNBAAYtUsmT4/mGVLfH14iRPywphhRsi7ULBtUFcwAgAAAAANRbL4++0p8uViQOwqhp+yc5QGIvzIUiZJl8ReUm7PWaBWwAIAAAAABgALzcysTAKWYWZujghDF0LB/p7y1Rdj84XSc++ToyyQVwADEAAAAAhuioIE1WI5sjU5C5gOkEOzzQBuSB8gXOijiNXj/SkFbHokf/cMv6euH55d7E+qIjIQADNDAAtgAAAAVkACAAAAAAoupK1+EnDA9ArWSGX8N2Bdzcd2k0v74TiW7cm/NnGQ4FcwAgAAAAAIQejhDZ9Nc8iVb18P5M1Ae+Yj5kcteQrwG435QjL1b+BWwAIAAAAAC45JY0XNjX6Q//kZLwj47hx6o5WLSa0H6HrqhmHDWv4wVwADEAAAAAd1xXAcPYQLtdtfqR5LQ9Ifs5MnR3p246FT8e95xD3QFcCMtsVPlBcUKB8ghiGacGOgADNDEAtgAAAAVkACAAAAAAwEyIt9oFT52WU1803IWk/AVoJjZpZB6DrNEcpYH9pocFcwAgAAAAAGUI4Qq+5OUn+GkU9QXyyyNPICjsBMx03u+vwsZMqkXdBWwAIAAAAAChZceZWyNwOjxvAYuahROcIJYsbJbFPOuM8gJYCyE89QVwADEAAAAApOWOHJ1nET5R2n0r2P7+MrvbP0rdflDENrf1RtnKpsrNHYv7Yi/m6/YypMGQun0zxAADNDIAtgAAAAVkACAAAAAAGBZhRvnDkPLVOphkKlgh0qM1ja+Q3vfWkzYGFI1i9MEFcwAgAAAAALt+CvSui2B1UPumTQsYG5+6HAjskjtG9socEU+OmbeVBWwAIAAAAAC/DKHa5UBYYfh6Ii57IYsehVlWer4/7tx3/7as9CC4YwVwADEAAAAAfAOdjmkHRrFZP6TaQG9WjBSHKh1kA3XKkKfAHk2PH6uN5Db2JGjSERYjbf7OLcjldAADNDMAtgAAAAVkACAAAAAAoA+GD342ebt5UxBjKM2uG7k6ey7ptQJNg49VEavTjyQFcwAgAAAAAEhSi4BX4H1N3MWLfeCrDDJrt7K2JVPR0JyMthwmYKeWBWwAIAAAAAA5D2SdvwmLZL94mbfMHM6MBOY2+CbWZZGlL6vOfOP0PQVwADEAAAAAAnemJwtx3yumpDWsiMDYo76Nx2lnHt9Y7a7Dv6XwsvzxdQuKjgvPk/V7ljYsP8QpdwADNDQAtgAAAAVkACAAAAAAxXYsnI+yHMyULWxWuzRCLKrHcNyBywm8zJb6pVoxl80FcwAgAAAAAC0eIWozLb6DqZmoCkPdQgZr70gdDUBaUjKv5/KfaPdIBWwAIAAAAADCS+xlvuqcZXZq7WtZYVqNa5gJaUg8cwGkpppFmO7JvgVwADEAAAAAQioAjRmQhid0Xi7G2fPlS8E/2DY+rEBx9P0bzPsVPM1o3/iirFAe/C/IIA+u7A+gfwADNDUAtgAAAAVkACAAAAAAL3tWrqCSssPBwII58bD5YUe3ToUAolbOFug4qyJwEgUFcwAgAAAAADR9zt/FXCBFLSgAMH2JySH0ty+Vmsit9vw8BnGufbbmBWwAIAAAAACnEYjj/63oD1OwvpNF5jkEatIqmIvQg9R3qOlCfXQAJgVwADEAAAAAAP7guaYDv3sXJVwUMh7q7BeDS7qaZ6JQTSdA1Pt3qjbJzs1STAv2aN+XPLzZTx59zAADNDYAtgAAAAVkACAAAAAAC6thAvnAW0OWj+00fkfACHqGadSKa1+Cqc2OStTvBDwFcwAgAAAAAJgsRoWXLvNvvdNmSjzskUE12B7lkkHIOylE0HGQGOOtBWwAIAAAAAC6nNt5TkC4u+yJI3+xQ+YBOTBlWSoixwx2i7nmcGzS2gVwADEAAAAAIKZIPmruhbc9rd3dHBGqGFTAdt/zR8BJO2cHYSBs8Ad+1UlYcy9o3vX32T/Tc68srQADNDcAtgAAAAVkACAAAAAAtGn0HNPjE9lfhycNn68Sio4f5xF3fd35FL4fD6dVZJEFcwAgAAAAALQi5vtrUDH2Mvcn0EKK076iwoTkr+l+qfV+XZlxn/SBBWwAIAAAAACtH6/7hkTCLxTyA9IW2TR/UE0bT+kV03NF+g89QRu4vQVwADEAAAAAZbaxHTjpGcOS09qmV9D1vIxDMa4NQBANlcllJ+wdCZnQPcVAbRTpy533K2MwZ1sWUgADNDgAtgAAAAVkACAAAAAAWm/rgGDXM7Fd0XqLxyg90m3qlpUBr+WEGRuW2oI9RNEFcwAgAAAAAI6sZWi6bZMsMHGCkfamH9A9E8rEKczQUAyxGuN5WSTFBWwAIAAAAACkairItO52RXvEhGr4kTdVpocAdS9tl32xZ8RMWqhSdwVwADEAAAAARPVY3+lUMa3Ayxd6mzjCKLLvFsUeIUFy0jCvQvCar3YAJpPY9yeRAvjUQbILELXrCwADNDkAtgAAAAVkACAAAAAAsEahPNujdV4N65efSlhz5y6mjjE3RCsXqpXG1UOQJ18FcwAgAAAAAG1Oa/wTt7OcgS8D0Vzoctb4rTsVKPap65hKEXrFNvMCBWwAIAAAAABd6K7yIPTGj0jFMyU49jbvSS72RMuC7ocoAfZZ1jlUygVwADEAAAAAOJwEBfp/NrwGa0QWmoRE1KlQylG93Hd90gcMkyFMzPdGunTCg8mb7YEnD9EoO7XmUAADNTAAtgAAAAVkACAAAAAAZKVBHJDaWQj8Rhyomqs6trfbpUQbKgMbixKOWa39p2cFcwAgAAAAADxcjiZ+iVI5SSyalDcAo3Ca+prBNdRtl8NkP219ALCFBWwAIAAAAACViEQfTP6dFbOezhrfw/GPzSVMzFe46TdUVnZPqYy/JQVwADEAAAAA82KV06lJbm4Hdz+8MTQQ5IsAMOcLNFk1AAYGqidxouKiEf7TOAjJf97NsvhjR8JJSwADNTEAtgAAAAVkACAAAAAA9M70XhXOdnwmM8ExKdaMCdhf7MSqh65S6tjBWPEE8JYFcwAgAAAAAAwE7y2wXmSvjagmlJ7UmT8LeaxCzx24d2NJ9GMaiPjmBWwAIAAAAADb4LYUBQhZDGCz877jWGUPYbIvLBCSK3/7JJ56ieDDBAVwADEAAAAAD+0PNw3bP911sGMbvJXrr6jloZ+RL1BdK2Xc8JAJMRlL3qO1E4GrF1TIOTsjzxU5FAADNTIAtgAAAAVkACAAAAAAvZMpeDGwucU+mh3LMb53qI4JjlDEXeBBq3i4jaIEekQFcwAgAAAAAKnlJx8HPDJXvyul1vIF+4jpxdXIBics2PbvU9vXtsJcBWwAIAAAAACVQorZ36/F2Mehv7PiX4dss75OXxqHA9qRMTAPPTexGgVwADEAAAAA2pxc+YpIH6q5GWReyzmZYLl2SFcS0Zla/CPRdX3o6nH8q3xVlhfP1QxYiB/SASBF4QADNTMAtgAAAAVkACAAAAAAupD2wCvt8JXd2aPAqs/Fi2wN81HUQBeHtbnTu98UcawFcwAgAAAAAHczkDdUpa/aZWqvjKCqe5G92RpbaEI2b5Ub7JOfLtj6BWwAIAAAAADxV5YoyKSoB7TRcuVheWdSqs1mDKZlJhIiOY1rVXI8jwVwADEAAAAA7KopXwvFev+59ll3h+YIExDbVxgpzIoKcWxrZm8+KK+gSvMVLGvVkAO3iSZ/NfCk7AADNTQAtgAAAAVkACAAAAAAp04CAl5fLsKmCqAIuHk5lty9W/RkE0Sv0ndhWswhQCIFcwAgAAAAAFC6VHBqtB7auYMIdezYkyZDZCJMbj/qsojNRAAoSQZJBWwAIAAAAAAGYKqud0S1j1dn6TbQgyufs3csol8UtoPFsdC31JMv0wVwADEAAAAAzPFx98NG+32oyBcP7LEWSc4XmINvv1jz9H1vUfU7Ei8rrfLm3FE3Ro7iE1NQWfq08AADNTUAtgAAAAVkACAAAAAAQz3dMb0C1Vrue824BQVHPrXPvo1RGs/UW2/XtElmOvEFcwAgAAAAADUM6gnF8oZbM8YoQlY5HL1anokZt2YBL9+gmWwNopRTBWwAIAAAAAD8PU8LeEwrE5j09zVheic1A5BontnKTWoWkf0bP45a6gVwADEAAAAAuWAGWSs4dSXcOs+MaRxuMEhrb4PW2KufUe3vRTh5aNFIenGUzulOsUenZ5X+00CfOgADNTYAtgAAAAVkACAAAAAAwGkl6UipO5DmII4QnkAv3d5zv2r4n0HD5muRZ8TVpYkFcwAgAAAAAAaAys7rXN8hBv/uRUTTTI2V5Uosq85wKG7Uc84OHoOiBWwAIAAAAAApZuigj1DkJAMJYSPgR7HAapqBQXz7XBAaynDCjlcTTwVwADEAAAAAxEQllKypQe6c7TfPqQ4SgnR9owSByoAtqIPe7FhNUQuxQlVXGlKMm6dCTs0giOuJSgADNTcAtgAAAAVkACAAAAAAZXfRhPcHgQyWTHzGyDYT+Hq080EpG91hPVDBlWgTIFwFcwAgAAAAAHID0ACJzUBF40LDPUDooitP0efmbsSj9V1EswmGScaXBWwAIAAAAAAd4akmmxL/CvIr1tsXteN+jHafn9XVaMs9yKaBcUJmAwVwADEAAAAAFdhIEnwPS0at9rmbKVhMud/qRjkla9iLmWBFCzK9EhMnyWcuChV/dmMjMrzrQQi6yQADNTgAtgAAAAVkACAAAAAA9KT8fTJ1wZvzzW3HFHPx/Byp0w6GMuOy73ezZ3z0RnYFcwAgAAAAAEVc8N3ylbVRHgtUxj2GOMHppwP22ApSOlv54o6t9GTrBWwAIAAAAACtGusV8lTkq9QqSK7KtXxVTGItyXvBhKMIZ+flJn47SwVwADEAAAAAtBGopeM4spyEN2T2xRgakYVHUIs8m6xkWUJD7T3jzWzqKSVA13DnS5NXNWtyrhjk8gADNTkAtgAAAAVkACAAAAAAz5Kn1XeOCLRoQg6AG4APUOCua7AezN/2npb9XTYhyOcFcwAgAAAAAAYFJJR481+Um5vr7ePh6XSmGDMdODAQuBLM6OjN9b1+BWwAIAAAAAAaByn46CK9a/Ja416/93Q9UsW5Y97tkvxxc+0WZrFApQVwADEAAAAAlh1+NrxaxP0I2UdUpkOL8PBNioi/XM6+HoAibVdG2k9LuCRlgUfUTMM2OAf+E4gxgQADNjAAtgAAAAVkACAAAAAAfBrc34AEkXYAvpJGWaB7HrPx6oWsK94Rokz/PLhqt4MFcwAgAAAAAP0wCmPekA77DJDpZqMgZ/Vxrr+0UG3oOA3Irsv518wiBWwAIAAAAAAiueg7C/akgH1G0XYM0bsQb4ls0FR87GjY0sPnPzGucgVwADEAAAAA+dML7wmI3spt3U+gkYP/uQ7IQgZ7AgfUIJhMrfCyrvIX7A1TlduJhKLnfYk//1xORgADNjEAtgAAAAVkACAAAAAA1HhBpFtWvRcSyWakmXuhOt7DwCsPXHN4mH/+Ns9J130FcwAgAAAAAJta6iHJQKOGRlsu6EjmIYo921pC+WkRSjkp5aYxiLegBWwAIAAAAAD1Qjjxcd/6ysPFrs+gAjcJ4VfN1wjZvTFpxVwPWOXmygVwADEAAAAAz/CjOW4p5B4tRamtY7idQdPYTbZq+zuN5LWKcvjrqFwEMgdMOFGj8+2/NWqpq1uQPQAAEnNwAAEAAAAAAAAAEHBuAAIAAAAQdGYABgAAABNtbgAAAAAAAAAAAAAAAAAAAEAwE214ABWB6X30ECIRAAAAAAAAQDAA",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "decimal",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            },
                            "min": {
                                "$numberDecimal": "0"
                            },
                            "max": {
                                "$numberDecimal": "1234567890123456789"
                            },
                            "precision": {
                                "$numberInt": "2"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision-v2/mongocryptd-reply.json000066400000000000000000000043261521103432300323450ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A7AAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAUQAAABN2AACABMa6ngEAAAAAAAAAJDATbWluAAAAAAAAAAAAAAAAAAAAQDATbWF4ABWB6X30ECIRAAAAAAAAQDAQcHJlY2lzaW9uAAIAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "decimal",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                },
                                "min": {
                                    "$numberDecimal": "0"
                                },
                                "max": {
                                    "$numberDecimal": "1234567890123456789"
                                },
                                "precision": {
                                    "$numberInt": "2"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision/000077500000000000000000000000001521103432300254225ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-precision/RNG_DATA.h000066400000000000000000000206661521103432300270240ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x51\xdb\xd6\x46\x8b\xcb\x43\x8d\xfe\xe7\x24\x9c\x99\x2e\x41\x39"                                                 \
    "\x18\x13\xcf\x9b\xa9\x94\xb8\x3b\x81\xc1\x25\xeb\xa7\xb0\xc0\x3b"                                                 \
    "\x17\x17\xe2\x9b\x73\xec\xd9\x7f\x76\xc4\x4f\x63\x28\x3c\x05\xf2"                                                 \
    "\x9c\x7d\x48\xaf\xae\x63\x8d\x62\x60\x31\x6e\x0c\xae\x48\x9f\x45"                                                 \
    "\xd2\xc3\x8a\x5b\xe1\xd8\xd9\x23\xe4\x37\xe7\x8a\x5a\x6a\x82\x0e"                                                 \
    "\xf0\x4b\x2e\x84\xf6\x02\xae\xfd\x45\x47\xcf\x3f\xb1\x2c\xb4\xfd"                                                 \
    "\xae\xd7\x28\xea\x44\xf9\xb0\x7e\xd8\xa5\x90\xae\x51\xf6\x00\xc0"                                                 \
    "\x62\x6b\x2a\x3f\xca\x2f\x2e\xa5\x78\xfe\xfa\xe3\x11\x99\xa0\x9b"                                                 \
    "\x77\x55\x0c\xaf\x36\xc4\x8a\xd9\x72\x00\x81\x58\x34\xd4\x30\xcc"                                                 \
    "\x19\x06\x93\xf9\x51\x7a\x14\x88\x1e\x44\x9e\xb0\xf6\x49\x2b\x3b"                                                 \
    "\xf4\xfd\xdc\xf1\x49\xd4\x87\x71\x37\xef\x6f\x4c\x4e\x08\x7b\x1c"                                                 \
    "\x57\x0d\x82\x7e\xa7\x03\xbb\x83\x70\x6c\xbf\x43\x99\xf6\x63\xae"                                                 \
    "\x2f\x96\x3c\x44\xf3\x49\x22\xf7\xe6\x6d\xa6\x33\x3e\x4b\x5b\xc3"                                                 \
    "\xf5\xd2\xa9\xfc\x9c\xef\x32\x04\x76\xbf\x48\xf4\xa4\xee\x63\xb7"                                                 \
    "\x87\xdb\x48\xb0\xdd\xce\x68\xb0\x9e\x69\x60\x20\x63\xe6\x0e\x86"                                                 \
    "\x12\xbd\x3f\x9d\x5c\x58\x01\x5a\x17\xe3\xa3\x87\x18\xbe\xb2\xbf"                                                 \
    "\xcd\x8c\x12\x74\x01\x7d\xe7\xae\xd4\x2d\x91\xe7\xa8\x84\x9a\xb0"                                                 \
    "\xc9\xde\x3e\x46\x6b\x66\x93\x54\xd7\x7b\xc4\xa4\x53\x10\x73\xa1"                                                 \
    "\xec\x10\xe9\xa8\x7e\xe7\xb4\x27\x4f\xbb\xba\x5e\x67\x58\x0a\x33"                                                 \
    "\x8e\x70\x27\x8a\x12\xf1\x9b\x60\xbb\x2d\x37\x03\x68\x67\x9c\x37"                                                 \
    "\x6a\xff\xde\xf2\x6a\x01\xfd\x95\xc4\x68\xdb\x06\x62\xda\xc0\x62"                                                 \
    "\x3f\x27\xcc\xa8\x90\x66\x66\x86\xa7\x4c\x12\xf9\x92\x1e\x68\xd3"                                                 \
    "\x94\x30\xf4\x43\x85\x35\xdf\x1f\x3a\xaf\xc8\x9a\x5e\x67\x35\xa1"                                                 \
    "\xfc\xad\x9a\xc2\x9b\x39\x3b\x79\x30\x3a\x0e\x16\x6a\x11\xd9\xad"                                                 \
    "\xee\x4a\x5e\x6c\x20\x9f\x4d\x95\x3b\xe2\xa9\xaf\xd0\x43\xea\x4b"                                                 \
    "\xbd\xd9\xf5\xe3\xeb\xb6\xfe\x5a\xc4\x02\x3d\x29\x9d\x39\x85\xf2"                                                 \
    "\x4e\xac\xa8\x49\xa2\x22\x91\xa0\x9f\x1b\x4d\xd7\x21\xb6\x75\x47"                                                 \
    "\x06\xe9\x4f\x01\x5f\xd2\x99\x1d\xf1\x0c\x6f\xef\xd1\xbf\xb2\x43"                                                 \
    "\xc5\x33\x74\xb3\xc3\x8c\xfd\x80\x25\xfd\x53\xac\xc8\xf7\x91\x80"                                                 \
    "\x9e\xa0\x55\x7e\xf7\x14\x67\x7a\x74\x84\xfb\x49\xb4\x55\x99\xc8"                                                 \
    "\x28\x4a\xde\x5f\xec\x09\x56\xdd\x34\x2c\x46\x76\x3e\x82\x97\xa5"                                                 \
    "\xc9\xc1\xcb\x96\xa8\x34\xac\x93\x08\xf8\x6a\x64\xfd\x72\xc9\xee"                                                 \
    "\x3a\x71\x94\x63\x78\xc4\x5d\x28\x79\xfd\xeb\x0a\xbb\x24\x95\x2d"                                                 \
    "\xc0\xc9\xa7\xd6\x76\x35\x51\xd5\x2b\x28\x5e\xde\x4f\xb5\xff\xd2"                                                 \
    "\xbe\x12\x06\x04\xc0\xab\x5f\x76\xcb\x0f\x25\x7f\x22\x4f\xdb\xd7"                                                 \
    "\x0f\x1f\x73\x98\xcc\x24\x02\x27\xb5\x92\x80\x68\x3f\x75\x50\x59"                                                 \
    "\xbc\xe4\x2f\x2b\xcf\xbe\xce\x06\x7f\x54\x34\x45\x29\xa4\xb9\x5a"                                                 \
    "\xa7\x76\xe2\x12\xdd\x33\xdf\xdf\x7d\x5d\xcd\xad\xf9\x5f\xa7\x8e"                                                 \
    "\x76\xb2\xc2\x59\x0b\x94\xf9\xef\x11\x4a\x24\xeb\xac\xe8\x08\xad"                                                 \
    "\xb9\x92\xf8\xc3\xf0\x21\xcd\x6e\x36\xa0\x36\xdc\x9b\xb7\x19\x73"                                                 \
    "\xfe\x42\x2e\x05\x6a\xc5\x0f\x7d\x3b\x04\xdb\x5b\x33\xf2\x02\xa3"                                                 \
    "\x86\xe8\xa8\x20\x4d\x56\x23\x9b\x23\x53\x90\xb9\x80\xe9\x04\x3b"                                                 \
    "\x77\x5c\x57\x01\xc3\xd8\x40\xbb\x5d\xb5\xfa\x91\xe4\xb4\x3d\x21"                                                 \
    "\xa4\xe5\x8e\x1c\x9d\x67\x11\x3e\x51\xda\x7d\x2b\xd8\xfe\xfe\x32"                                                 \
    "\x7c\x03\x9d\x8e\x69\x07\x46\xb1\x59\x3f\xa4\xda\x40\x6f\x56\x8c"                                                 \
    "\x02\x77\xa6\x27\x0b\x71\xdf\x2b\xa6\xa4\x35\xac\x88\xc0\xd8\xa3"                                                 \
    "\x42\x2a\x00\x8d\x19\x90\x86\x27\x74\x5e\x2e\xc6\xd9\xf3\xe5\x4b"                                                 \
    "\x00\xfe\xe0\xb9\xa6\x03\xbf\x7b\x17\x25\x5c\x14\x32\x1e\xea\xec"                                                 \
    "\x20\xa6\x48\x3e\x6a\xee\x85\xb7\x3d\xad\xdd\xdd\x1c\x11\xaa\x18"                                                 \
    "\x65\xb6\xb1\x1d\x38\xe9\x19\xc3\x92\xd3\xda\xa6\x57\xd0\xf5\xbc"                                                 \
    "\x44\xf5\x58\xdf\xe9\x54\x31\xad\xc0\xcb\x17\x7a\x9b\x38\xc2\x28"                                                 \
    "\x38\x9c\x04\x05\xfa\x7f\x36\xbc\x06\x6b\x44\x16\x9a\x84\x44\xd4"                                                 \
    "\xf3\x62\x95\xd3\xa9\x49\x6e\x6e\x07\x77\x3f\xbc\x31\x34\x10\xe4"                                                 \
    "\x0f\xed\x0f\x37\x0d\xdb\x3f\xdd\x75\xb0\x63\x1b\xbc\x95\xeb\xaf"                                                 \
    "\xda\x9c\x5c\xf9\x8a\x48\x1f\xaa\xb9\x19\x64\x5e\xcb\x39\x99\x60"                                                 \
    "\xec\xaa\x29\x5f\x0b\xc5\x7a\xff\xb9\xf6\x59\x77\x87\xe6\x08\x13"                                                 \
    "\xcc\xf1\x71\xf7\xc3\x46\xfb\x7d\xa8\xc8\x17\x0f\xec\xb1\x16\x49"                                                 \
    "\xb9\x60\x06\x59\x2b\x38\x75\x25\xdc\x3a\xcf\x8c\x69\x1c\x6e\x30"                                                 \
    "\xc4\x44\x25\x94\xac\xa9\x41\xee\x9c\xed\x37\xcf\xa9\x0e\x12\x82"                                                 \
    "\x15\xd8\x48\x12\x7c\x0f\x4b\x46\xad\xf6\xb9\x9b\x29\x58\x4c\xb9"                                                 \
    "\xb4\x11\xa8\xa5\xe3\x38\xb2\x9c\x84\x37\x64\xf6\xc5\x18\x1a\x91"                                                 \
    "\x96\x1d\x7e\x36\xbc\x5a\xc4\xfd\x08\xd9\x47\x54\xa6\x43\x8b\xf0"                                                 \
    "\xf9\xd3\x0b\xef\x09\x88\xde\xca\x6d\xdd\x4f\xa0\x91\x83\xff\xb9"                                                 \
    "\xcf\xf0\xa3\x39\x6e\x29\xe4\x1e\x2d\x45\xa9\xad\x63\xb8\x9d\x41"                                                 \
    "\xf8\x92\xe4\xd4\x8c\x65\xce\x30\x10\x37\xfd\x39\x53\x73\xab\x9e"                                                 \
    "\x30\x04\x62\x06\x68\x35\xbc\x30\x47\x71\x35\x83\xaa\x41\x69\x91"                                                 \
    "\x1d\xa5\x77\xb7\xd0\xb4\xd9\x27\x58\xc6\xc7\xba\x6c\xb7\x2f\x6b"                                                 \
    "\xe9\x8a\xd4\x35\x30\x42\xa4\xef\x20\xb1\x1d\x2b\x3f\x60\xf5\xa0"                                                 \
    "\x30\x2f\xf7\x38\x6e\x17\x84\x8b\x56\x14\x12\xc7\x58\x50\xa1\x33"                                                 \
    "\xdc\xb7\x56\xa8\xc3\xa6\x26\x3f\x25\xf2\xe9\x74\xe6\xf7\x3b\xb4"
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-v2/000077500000000000000000000000001521103432300237565ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-v2/cmd.json000066400000000000000000000002511521103432300254120ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {"$numberDecimal":"1.23000000000000"}
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-v2/encrypted-field-map.json000066400000000000000000000011771521103432300305100ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "decimal",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-v2/encrypted-payload.json000066400000000000000000000772551521103432300303150ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "Cx9bAAAFZAAgAAAAACm81dguhQYS2gXLYGq5LY+H4WZdCysG+w/KCoqSuJM6BXMAIAAAAAAuPz1M22vcGxotLCha9vNMtNmIvF/wy0ifpMBYySaOnAVwADEAAAAA/L++3P7CrwPgNWztsng9dPMqNKG2HNKzjP4gvOwaNKWN9yqtQWilakb9rSiJkRlnigV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABMAAAAFdgBgAAAAAKvN76sSNJh2EjQSNFZ4kBIpl7Zd8lZwByxzgM4ee0M2NyiFGc7qTVUpLqldpckaEJFSMARROf87iyzUtNC8mx8mDNT/8tEYDKVxmbUptKQsQlpmQYdIa4i9EavZkhX4MgVlACAAAAAA65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+wFbAAgAAAAADj7Omh6bLAHeEmNfcVOH9qag6YFLwoH+MAvczX88brGEmsAAAAAAAAAAAAEZwBwWQAAAzAAtgAAAAVkACAAAAAAHv1Kggg4HRk87/EfqcM5J5rX4zbLvP6jeOVnNI8UBk4FcwAgAAAAAFqLA/0uSzDg8LIvPOug86qXZNEoKjiUX84t9MGLQQbTBWwAIAAAAABu95kvBzeFxZWtk9reo4z3SVSXlyl7lhh39J2ZFmaXtgVwADEAAAAAe7War6mtpkT7ZmBpDJ5OF6nQOuqfRZ40/kC9ltpclDWeeDXYlCGt8iPeqWJkxeEjpAADMQC2AAAABWQAIAAAAACiBBRp9+nBf6zGm3P+iNX/+qxAhZgjpiT/wAfIB+fBlwVzACAAAAAAGvFzxqNcUf/5mzvjhfl4FXWmBC9115ljiO4/OfybKNcFbAAgAAAAAFPldhq6oMSWLPZsbmpP/CTBSAl8ENr5LKwXTr0sFhaaBXAAMQAAAACTPd0q33ZUxvYa8/rU0xT3ujS9MuxMgCo5nN4/rcb+Fm+WoLLsQNogCymXQTkOO6amAAMyALYAAAAFZAAgAAAAAMLqxgfxpqpIZCXPU/BP9Aa+trIw5Jb4gtCCj+Hq9jKhBXMAIAAAAACzwU5bcDhLrTgjTWm+LhdFtqQDnmyY0VpqrYUNU+XTPwVsACAAAAAAahS0ITnR/CVFjnjldmxewoig3Ie37/a5SrXPEVoTdaMFcAAxAAAAALkzu9uKHgfIXAulSuL7J40IoDcYiELO5biGs8ANutCShSnK2IhKnD2YjN/gohDA2wUAAzMAtgAAAAVkACAAAAAAj35V54bLw3Ve5g7vxqkTl9b/5ib/OaZj/YeDvBqAWmIFcwAgAAAAAOKsHSmQj+PbeRvAsLiG9+NFYWUZ14elW4rW9z/8xn00BWwAIAAAAAAIqn++rZcjHaS6pHl6fsMADVlsIDcRGTcVoBMbgI+VgwVwADEAAAAAqvI2A9XiWrwRdre2hms1TEPgIfcUOqtVv8ginHWjFK3c62dzCQPGoLcQMmn8FMgSDwADNAC2AAAABWQAIAAAAAAN5I2e3xYLxYSq+FhblqKHpB0oqiHbR0YI5rrBxZMdfQVzACAAAAAAxr9S9sBNudmr/1nRilpY+8dM1K4EvSZkoi+BbD0yC6AFbAAgAAAAAKbJf3yBr2aSt5klNTJiXVhrudBp8PlB7AkTz4s3FkgaBXAAMQAAAACpdxa9BRUZMTiKuurc0iyatd3akmXbO4TRDly78RAG1czOOEWGFQNOL/BAzRMJjq8EAAM1ALYAAAAFZAAgAAAAABlcNxBmuS2hFWIJzBPsMJy4LEW3olVlaLt6nmaJVyfsBXMAIAAAAAB5TRJWqyTFf3nDWgDB48uHsognMsVtFvpUw8HE9DJOlAVsACAAAAAAWVyPN5uCYDiUGyxbLaidgE4B4zX9QeUGou77FkD+Z0UFcAAxAAAAAIsM6mbVPSEBA+500vSNjff7urzbU5elg0rrSYYCWx44YDuT33pLuPE8XlX0nSf9EtIAAzYAtgAAAAVkACAAAAAAc73ceC4qclF8S2xEoX6SddtUcRkxPW+19IOYLg+CuFgFcwAgAAAAAMksxZenj1R8iJdtk6njxaUwwfc9xyqnlFGNxzUgyRSjBWwAIAAAAABa8jZ3UaKoOkq4XvyoDiDhJhBwAM9LM+h78XS/iVreQgVwADEAAAAAE/k15ENCui2MADpkZfc7B9BUC+cg0jmGTLeovWYuxvVNJynI/iGlwCZbpuPsXj4aDQADNwC2AAAABWQAIAAAAAAodxdPjLC4+6ZZD7OaTkciX+UeCmJitbFPcMwaksYSnAVzACAAAAAARdlMAhx4lVCEfSL4WFGzNx1SmVWG+3xlkvZDhaDYUooFbAAgAAAAAGbWrQMqntewa5GKpXE1zshRh1L4UUFFlA0C8uFo+D5bBXAAMQAAAAAD2jMujocEl/3HHwCSfBxAAx0Lmc1rHDopXB5N0fiNs4L13B0XOVsh+vsHpe89sUkAAAM4ALYAAAAFZAAgAAAAAK9Xq4/YwveWV924scqECfrnfg0n9lQa080awMrpn7q5BXMAIAAAAACYVAY0MWD0n+qad4lvz3tyfEuCH6mEpeyTnntg7VQ+AwVsACAAAAAAdmmEYKaXaPNnnIjyPbvZCjISYQ1FNf1hp4s5NQQ2Z+wFcAAxAAAAAOKqgUdWmzlLE8lx8toqNocNE2xmdronqiYVkCNqniQ6AieYSrKhxMT0b6U5PDvrynAAAzkAtgAAAAVkACAAAAAAZeRexLKIZr9xAz/3KM39Ag3tYPPHM+LMWP+042DR7TkFcwAgAAAAAA8SOQVl+rClpfdMKluNxIF5/4CAFMANtGmgSMF1b8wmBWwAIAAAAAA+U8kRTlU7mHEDYsQAITwK9evW8KUVzt+o7/X+H3GOwAVwADEAAAAAKoaH3IZNNWbxSseLnIjNs7aH63jSc2Z2igJ9iDbokBBWwBXLoIkiBZDOyPsYtdnO6QADMTAAtgAAAAVkACAAAAAApSZFnhxKcKm8eJ3fDiKw5bbL+pCGJnkLYqVznm+ba44FcwAgAAAAACtvFb0SqinyubFQ7AE65djO6RmJcQD6HP6mSJ5+07w+BWwAIAAAAAC6PqRi7DdeTxlu8MwDJze/klq97g4guBxANryB9LjViAVwADEAAAAAlPhSXNlz7b3Wlq2ctZSzoeGEE31maQ7A7QVLRoyORbcX/o2yFkiMbAuYGsHYDgDqggADMTEAtgAAAAVkACAAAAAAnf/gzk5ojKBw593K6XFy59q42fQ2T6Ct8ikmfQ5eSccFcwAgAAAAAEz232H0bZq3rASMCZ1u8TJhefVoT0ml3ZGhHSU6rC4CBWwAIAAAAADJYGhBuFWexLngkOX2udm1Y9E2jBRKEtolYRb9OCBz1QVwADEAAAAAliblWdOjMk6oXl7snwS1nZqBfb/+exB6WdPeVeAfr+4GDK/N05uGBBQDd8SQSJ78awADMTIAtgAAAAVkACAAAAAAXlG5rsH7yxfo6TfODWdsHyY0bgFP9fVkk2aDgiZ7X3QFcwAgAAAAAGOAa2FCGtrPff5UYqMnEyYStMvfmV41+Y6a98trN4u4BWwAIAAAAABPaMD0oAfz1Ju4Ce0LsI6fOs2On6k7Hvsi7TGR2V27NAVwADEAAAAANQzBLml5K/E/LJkx+UKjiMVHWL9SvtXgWc/JGkqW2b+s72QjrFxb8eJdwh4/vN2NxQADMTMAtgAAAAVkACAAAAAAaXNHiUhVSnMX80fSNIr5FUMNLHODFrgchlpoVEXlqxQFcwAgAAAAAGYWpR1Hlol8Bo4T4aQahIZYBR3NFu3gTFh+j/udsdvfBWwAIAAAAAAyFM/grf9P+RmQi9GKjVz/5syQBPO2TKazPaNVb+liCwVwADEAAAAALINXVkHJak2lFpJ1mcpY8gBVv5UPuKxgh3g8PVx0ZUJr0vhL9/yFf3WQmlC8yvDPWAADMTQAtgAAAAVkACAAAAAAJq7/n8YJ9xnOHtqajuBcz2A4fjhy4Q3iQ59K4mJ7OVcFcwAgAAAAAHBGZrWDHH9HBy1RHDJB9wJFRzr4Ms2eJrqhCGLPySxhBWwAIAAAAACXL1OOB3sDo96tLJjyY5uzuKSSiYX/CYnwkYVGoDAF9wVwADEAAAAAxmd0fLYu53FJwN3ocadTvWsoHjJFf0FrELs/3ObjAX7pAxDaZ36j4L3mp6jliHVX+QADMTUAtgAAAAVkACAAAAAAes665K87Dz+1+ceUddKET4abafqArW9DG5eH3J8BlbcFcwAgAAAAAOO8b0NP2sgYaytSGAAe4cdbjhWKShNatwUJwC+/trO4BWwAIAAAAADNYULFWd7hlHIdPXpFzzH+ZHdItbkKs1xqNd8jDimS4gVwADEAAAAA92w2DZyi+u7XMAlo0kfGtucYZDmPg69tEthC6MKmTqYBJYqozVsoXUqRzhTirlEMigADMTYAtgAAAAVkACAAAAAAGXS8C6K3OrIYy+7jd32Qxsiv9XSFc5I0R3ys4ms/fAkFcwAgAAAAAIdQnV+9PJkai9BEqJ2b57WlA3vxZPuvRCHuRjGt0mnlBWwAIAAAAACez4qSGpxvW/6jDSTPcbVJbgYbe9n+pz7jI072n/WfaQVwADEAAAAAVLw3EperEpf8RAuC+118FGvE9KWg9DXg1Rk+eryKmCv28BtvQRvXs0IITpJ+IkWMVQADMTcAtgAAAAVkACAAAAAALm7PAlvWeCTIyVK2nVznfiSTSpPn6h1mASmxpwAgPI8FcwAgAAAAAMy4kkRr9XK3zoT/il3ZrccSBKMdRP9EXEqSR+FWHrhCBWwAIAAAAACjQq+RJsE/47YcUf2zUdkJ8sZem9EW6qGakLY0fQxb5QVwADEAAAAACGQfS2teKjyfxuuSeajmryNDx13knvte3DqFWmBbqZIi4fzhLHFPFB83mBIMFh+xYAADMTgAtgAAAAVkACAAAAAABLFY6YZcR5VB6fhKwp87wKu3EJJVzznvTEXIL51P/LkFcwAgAAAAAGg8gUbKZRM3ga/rF178Ej2Gma3OjXHE6AIetpyaMfDVBWwAIAAAAABno6rPPW5/BeqvK5+LPn5xt/o9aZzr7iZns/dojZiQHwVwADEAAAAADDKyhbATm81xMT3YCUhLeUmhhK0hBN0JoB5qXzPxormu4t1Yam4TlWPfJ4E8JGTOUgADMTkAtgAAAAVkACAAAAAAcUvsciiEsnqdtWwfso7mEyf6l8Duy32bMfNdAG7PPxQFcwAgAAAAANnxg5+hqiRQbznIVrnYUbMCOlHhr6fXfZVhCLs78HcjBWwAIAAAAACGEnvIxBNc0eqQDxt0yipXNwR3IZbJxqt7u+25QYcNLgVwADEAAAAAdiJmbXDln8RqyABuWbQUgVy/L9+EWPFjW9WTGPLx9E/XaQ7eikzcuCFf7on9J+F+TwADMjAAtgAAAAVkACAAAAAA+gcdJwpMRpPDJAe8RTzqR9byq4pSbPw/YOfi1vXfGQQFcwAgAAAAAFyYwFI/5A1DsrWZrMwarGxO8TBYYeAPt6ilUevD1n/VBWwAIAAAAABlMZsqMI5mKRhbMd1BmDrShlp1V/Ess82VY8t2X2zKjQVwADEAAAAANOWiq0tylNklgYV0239KHr0pLxsHtQI8pE5roxwtaEZ51LY0JHk8Vnz9LrsXlfGxOwADMjEAtgAAAAVkACAAAAAAEyHSjPgJJWHxmQ8T9kzQN/fTrPHIb/Grne/8o0soGM0FcwAgAAAAAMrJ3+hMQ8ZTGeCLG7B5hWD8DknaIPFJqngXDRoNoaWGBWwAIAAAAACKAfbEyEwav6SjmURSEwNvQvgXy0Q9bjyTdYMn+lGQywVwADEAAAAA5xteQkB5VtJkFsdEuD8+U5Ci3bYbEUcc0aAt/2V+nxrR2cIiY8k1VS4HEBVv+9PewwADMjIAtgAAAAVkACAAAAAARcySm8yUPNZeafteoHAX0+TSkyjVal/5Pyk7m4MtfYUFcwAgAAAAAE0O1xw/LCAXTrq+2YDKQWy4hB+yFCDhjiLTnwmAr0z/BWwAIAAAAADTN1RjxpwQo5DchkVPcnGkwNbClXqQEujl8sVOjhFB1QVwADEAAAAAb54Z1HgW9LsWyhA2nAJpiB/kCEz7MhXGXb2Hdg9dR+4juPCOqvsY8lmRsLs7PbP48gADMjMAtgAAAAVkACAAAAAAEyUya9M9junIdvrKEKNcz8cXJ/joBp6COsIyELFytFgFcwAgAAAAAMs2iEN/SfZhj9janXGS2JYljYTJYnDfJFk9sgBRenmJBWwAIAAAAACuJKqqjf2xnDwAmviBp7ceQdYAu4g4Bmdzzjptv0KNLAVwADEAAAAAFdL37T2SjsopAbfBCnx0huXtF7TlO+NOjsD6qCHpQjm82+9+hckGGYjWah8kSPd6DAADMjQAtgAAAAVkACAAAAAAhvYxi/JANcF3PpeCBQN5WFTbBSKo7jVI5KE/VX1LQ6sFcwAgAAAAADelKafNWGPE7YkxHb6WJy/LB3PdH/gfIQLZb0z7UpzxBWwAIAAAAAB/AMnSOCj+fU1okCbPvh03i4DyQhn6kg0EO9C1GPyjwgVwADEAAAAAabOklTKw7i6xeXzYoQwhNuIVXzXfAXBJV+C7czE4aQMv/4vpjbhszVgjomt7Z8SqkQADMjUAtgAAAAVkACAAAAAAC3VxrFwM2s3w2FxdX2r7u0jWfwDzXDdzBXcsg73Bky8FcwAgAAAAAPCwS4WmLaXS9+1PVL5eWaaQ7fpF8vap7GdcUpM1GrORBWwAIAAAAAC1gI2XXG/5D4PlNSqmGIgy6OcJ+bLvSqNxJlAJ4JGEYAVwADEAAAAAd1V2e7TfAKs2rmdlM5mts1GHEjMca7OeDh2n5xM93z3455zifTByJbGQEq31L+l8dgADMjYAtgAAAAVkACAAAAAAFFvnLYbuMhmFbdJo57/mfxfuL4IB3EY0RX664pPQ62EFcwAgAAAAAHZMsNQ84vTYsX8lcBTZ2j3Ubmt7vDA3RCIhHe3X6/HGBWwAIAAAAAAI+r9HXOzLPaRJ71Rk7K78XPDeR+DVxW/v4xoUJvWH1QVwADEAAAAAhF93EEO8shCG6TLAuX5ZFtd8F1Ec28N9wODrJpD1GKTf6jlkFfdxnRUwxbil3BpsJwADMjcAtgAAAAVkACAAAAAAio/KKj9eAXNOsQKlVUbzkYtloWyKFT1x/XMV3Apjc4oFcwAgAAAAAJJ8bbyK0s8eC4xr10r3RPzrWCpvYJQGIcgkthfVHpHIBWwAIAAAAAAFbx2IgyiGt6vCNxzOevZ19dv5wjbVNHm1zH3ISeh/gQVwADEAAAAAPdkc45Su0E80gbtUpUB8zwwjv93vTJUaM/g8Dlua5G7AuJLWh9kR8zyFEKQJHVf4OwADMjgAtgAAAAVkACAAAAAAqgVbYjPHH43lDnrqCZ8t4QZhorb3pSZl54axc+gj01YFcwAgAAAAAMxH41EFwqQMtd8wYddndFgjFJ1DK6RAqQOCmbRnVbaGBWwAIAAAAAAin0/3ZMo5HikTsA1ca9fDEmGNOCzX8l2lhx6c4psz2QVwADEAAAAADTGsZMF6NDbE3u792TQtLlryMBiaRU2ai+m0U7vqRdlFQVuFBsFCkxql3IF50zjXxgADMjkAtgAAAAVkACAAAAAAfJy2MxtWjWQjju5WWmPZjvWgZhFuAZI6RxlxUy9KGjQFcwAgAAAAALiN1uRaAJfmSMUoNjqH7Z2ZlZx5U9BgEvdTBzr7riXaBWwAIAAAAAAfYwW0TZkcj+fM/xbqxeefkTuxL/vVN1QcOkRnV4Xi7gVwADEAAAAAQ7TKNn7FE+9Df5ZCMngeCxgiSwGGV+VPav/5VhqxaTLS2XSnA1yJ0875arU43miPmQADMzAAtgAAAAVkACAAAAAAHbm4GQmJ6rnLsXNqoVGjtlJnW15FWDo67Go+lBTSuscFcwAgAAAAAAlFkykDl4lAC6LLKEUHDBazUjOTW5gSXp9MTJzzH3s/BWwAIAAAAAAYbH8rGdHlwBFrrVV0Ns/OXYisFGVkCXPxM7H7fte8pAVwADEAAAAAruZJC2/buFJbwInc6f21Ll32RKyNDej4RRyOiC63oZhfqx8dwY+MF8COLtB2kOxCgwADMzEAtgAAAAVkACAAAAAA3TQQfngK+iTCDF/VOQPVQ1QapOABUEy9xh1yik39BAkFcwAgAAAAADXhswcgFD09oOfsGhNE4ZSXwNAp4ZNjGi8wp7qUgQSMBWwAIAAAAACFm//8ycxAiImo43SFyna01jip/ihJDe9Ae0hsmA3/zwVwADEAAAAAu0cPhIVEh8eE3QjQCJeUm4g3Yd62eNqRB2vU4ZlDbAlg0TqtXom/Bmt3RBZFJBWnbQADMzIAtgAAAAVkACAAAAAA2omd4GwqQZvDRx8+79eOznfR3oAkN6HeolzFxGL6eJAFcwAgAAAAAKbirsCe4EVDYDftfrfuGO1XWzn1p/dhyXMMymJUrrsqBWwAIAAAAADaP6Wt/9cxWbuTkXgZMiDMMD9y/79WE2TGNDMvqzs1WQVwADEAAAAAJT82q07AURr26T0nk+DyuaGa8MhgbqIeH+ebjugczRPcjsuIhZI0BQB0bGfFJii23gADMzMAtgAAAAVkACAAAAAAHMSO7pJAxBXrOZC14RQpKx/ephfDcsyOw7p/gSF9RJsFcwAgAAAAAAZMshh8N+H69W2+nnbxZc0Dh8iHNQ6nkR1yHVwqdbC7BWwAIAAAAAAgbyAhVqY8K+fuo7m4/3wlbR8tu1ozwZHlr9Id1YVKLAVwADEAAAAABQMNOJIzPqMORv73VRBd2fA/uVoam28mhPnRMJaQgM61g2a40sgUyyw1vUm5Ux6aPgADMzQAtgAAAAVkACAAAAAAZeC1NLNLC5+6ybplk5LfZ88L8nZo16bTAdLDdd77aLEFcwAgAAAAAKsC2qTJP9a7aoSuVSH+5Ru2rBlMxA3/7eYu+/9gvEL0BWwAIAAAAABi5ED5SOSwddk+cLEF695tAJCMrcg+EjE/tswbQRNikgVwADEAAAAAFFSObroMlZCJAA+yMiJCC5uNahaEQ8NaIRpFkInnrKyZFABZae/VIiWhCT4JoUKp2gADMzUAtgAAAAVkACAAAAAAa7yoFzx7ivJdbdWswvVY5gWIvMSx0QUFfa3HLWBYf7UFcwAgAAAAAHFD6Leo9wNToX8j4K+eim4Na2AqJMQw5t36xsxpk6w9BWwAIAAAAABynrJTipWxP7q+3s9POJa0KqEKZS7oogV3UYzuO6zkfwVwADEAAAAAegGa22z3mJ//CYPOf4mt0f7UCG5ogbn3Wx6uMh2tlc9n/UhdijbROLbFTeEbj70lGgADMzYAtgAAAAVkACAAAAAA0iTVUiE4uBCw9y7bseV4cU4ahCHb6ZEglbAv14i0WMcFcwAgAAAAAJ5Tgpr/U+2voqTcmpF3eqE+uWE/s3eLsqNdudvwO70MBWwAIAAAAADzcEnL6IT6LccSLYIJw5Jq5EOLl3XnvqC6e6eRp85DGQVwADEAAAAAe8P1v181QLWJd/jqPoMDzTA80HMDsph7e4EgcYQ3fYbKo2f/rxjt8fBOCUMnv+yiwAADMzcAtgAAAAVkACAAAAAACbA/xUXrIuo9W5vt8Z1KlBeofBBmWHjZyUmzNY8LaOoFcwAgAAAAAFCFp0lHPhrMxItLjRPv4ioLkhBFNpGg7S1X7cbPM1DEBWwAIAAAAACMkUyLfiuiOezEkgtVjTOxMQ/C7s1hgzbCtUVWAQJ4QwVwADEAAAAAMfh0yVrwidxjCLd6AbeRhC3b9n4kyU09xps5cLTYZKQyT1S4tBVvKkYAQkzld5hkIAADMzgAtgAAAAVkACAAAAAA8xLtqV5mqEuxJf0Z62ZELWnuYl2Xjn5wTZQQlAvQSV4FcwAgAAAAAEE8WO03WzNXt5cI/nH/+Hy+TAk/rFvnYZp2tgX3DpYXBWwAIAAAAAAAY2+NbQRSmJsEwzT6A0SGzWB7JSbbLGS2FZ3ueVhnYQVwADEAAAAA33sPOsCJ/3iXVTwzDzHAzEEhGLcaVzlsVjk6zrs48SZq1/V4udEI82jToguOxfOs3wADMzkAtgAAAAVkACAAAAAAathkD6mmM3Ucjx4ANURY3OdLMRfj/lMwRRSZuRcWAt0FcwAgAAAAABNpbSVYGggb0tRD5WpTCNPfq4tRAZA/nsyzIy0RsVM8BWwAIAAAAADHrlIuX7H/ryM40DU6UD5CnDPmHYfyb2VSeh/pzXtmgQVwADEAAAAAgk/6PalD++JLi3fBRXd8r9He3h9wfKE9SybMAIGGy7HZe/28NHXTR5KcPZ/sNmscfgADNDAAtgAAAAVkACAAAAAAVljiTiqnN5/AmxaH0uaZhSz/COxyS1vU6urQct31Cb0FcwAgAAAAANCJp9g86En0OOLiVgyiJ3LnNUPDiqNsLhifJhWsuNvkBWwAIAAAAAA9QB9LSbFrrVWQN/L+dbh40SngimQoMgwB4RtG6gpLKAVwADEAAAAAWwtz0prBVDY1Fxjc09XnoAdWDzPiy39uXpONBd0T1gLR8mfE9Xk23snZf0UgG1y/CAADNDEAtgAAAAVkACAAAAAAao++Bs+xPLO17LeTGXhMW7IzBsrTn9daAhNsDpHb7NIFcwAgAAAAAD5YL7hA0vMPDyNZXiHMzsQA7nA8U7yljwqQCWRjnNRKBWwAIAAAAAArH3x7KkTVJpGuHKgQ+Z6g9VxFjK9WfnfLnCUFP55k5wVwADEAAAAAHAVqSf+JHHF1yKZa9rMTEd9iL5zsvbkO5asr0ma0+HZo1fqxx8XuCDsUnc8GUHXMagADNDIAtgAAAAVkACAAAAAAheOgHdLaUJAdLP0+N61HJr7QOarHMRJxnpQURXPnpnEFcwAgAAAAAKyqdxuehCAfVW0qbLJd719f2hAZNkBxKpwewzzujo+LBWwAIAAAAADp7QQHhkH6nL3E50cnLcUgfTXbDekoP216e9CEX5kZeAVwADEAAAAAUNp7ufkjWPq6RQ3N4VhEmG9XgHx1WLOIgfg7+w25OQ/JBGj6V6voLLnZt1BglHrNcgADNDMAtgAAAAVkACAAAAAASZyQhJAHSH3mZott36C5LGbta/+fZDh3yWAJqqeIUwwFcwAgAAAAAOI+UnX3C+5amHNqSNnxTnZW90v7WdhIHgAYq7p/DtSyBWwAIAAAAAD+P1v5VlAPvheCVELcZidSZovBo54Knk7Fa3sl80CWcAVwADEAAAAAXXMoJunNXrkUp6tOBucYPp6QYw6CGtGFw6h243mFPkgwzmEVAwIr+2g3y4ZbaY6JLgADNDQAtgAAAAVkACAAAAAA2X9aQvgs7lQy0gP5aDjzpiBpq5rH7KuxhEQO1dPWeA4FcwAgAAAAAKrp/pdrg3xiGs6C8A/2NJTIp32T4wPCyN5MhtlbyLgRBWwAIAAAAACkkkO70rbyzqOO7XMCqcsBASPGXIwc4+e0mq9rc+BD6wVwADEAAAAArumNNLrXuVKJjvwFrZDBAE+0YpCLzCvGgDlUPqUVgpbIr6fyb8k6D60anGMANoX6/AADNDUAtgAAAAVkACAAAAAA7ICu/+TayxQMgStNgQDdLcYMnfAOvJ7DyahEl3X0u1AFcwAgAAAAAKsQI1isLnqymW+Ioke2Ms2eAszJGN5w0HMdVuUyo6DEBWwAIAAAAACeCREvt1856DUnn5fuE6yo5KDwaZq6jFXQCM9N6K3bEAVwADEAAAAApCG7V0V9fvrRE7U4UdHZ77AyTB2s0J9KFE1SlkvG/yJqDBPuaIev5Tm1ZkxDKks0vQADNDYAtgAAAAVkACAAAAAAnKfKO4lQf9I694oRIS5KD/C/5bMqAc0ID/cOjYo9a0EFcwAgAAAAAOrymtXlN8oI5923BbVdjadLBUE+S3U3pRFgfmqmpYP5BWwAIAAAAACgPmLbjBJEWKrc/13dCj0i6GMxsF8WZpJVGf7PWCPGzQVwADEAAAAAoewqaqQ92rXTYUjEdP+V0AGzQXrRSxmrdA6VUe6dhswdFJCtpqapn2a4ukQBj3ObzQADNDcAtgAAAAVkACAAAAAAt6Ma+U5PqtFiECWJttZVBmDoak4z/BqoAc/EQBBi1bIFcwAgAAAAAF7Q3MLa1rBfWMSPG0zs5RefPtfmkgfbTCMD05AN06gABWwAIAAAAACjvS7DyGKARmDiSijxc7eimGO5qqZ6xniMC8W6Kn2VZQVwADEAAAAAj+YZxlHCGET+NXTkdrntaUlCM0RQaokTPwdiYaH39tullnZMf3/0QWb8ArQqDNzrTgADNDgAtgAAAAVkACAAAAAAHnekwMTF7Cf6d4LL0+fmwF8mvUSz7lY/CMaR8Ue1Fm8FcwAgAAAAANgJNjr4Flx8yRn971dJJhkK2H2xPFcrwxMJnieA7qcVBWwAIAAAAAAotvhpiM8dtovs6+0h/EVo1RX7SZdCp3fL6dotSVJIagVwADEAAAAA8naJV6CMlGHNz2ZcyhXlXK3zCcMJ9V3wBvRqoK+RyKZXz+UYbgiz2hTqwjLNNpjX0gADNDkAtgAAAAVkACAAAAAAV8Eo2SERldnP84wskU4B2s6+O46URcd9TBajDzRnOZcFcwAgAAAAAJGUJ0cToB2u9meDXhgj8wzkyt+g4BQT8alylh/PquzCBWwAIAAAAACvoyI1QcrjCt++SaDkLblbCa0mSNSwvkn78PLO8PbUUgVwADEAAAAAgPu1ktLkczLEDYWX9D1AgzGRfwt4kDqZzOPcrf9VEJiWvZ2+gIHriOo+DfdoMmkUQAADNTAAtgAAAAVkACAAAAAAFgYfkMVjkOKH2I9LfxLXocgVSwGJuZKkacnVTMwZKToFcwAgAAAAAFo/c+ZxtaA/+dG28HmSikf4ETIH1E3n+ehSSL3vXGMNBWwAIAAAAACj85M7KSid7Qzw4/hZaL8i0aaKXJ2St9VYSB6cSq8QVgVwADEAAAAAjnw+FwTtj8QnxQ940WwJXtenrlqBpevnFzldjNjdbPdCEervjpAXRyJ0D2T7aa0XqAADNTEAtgAAAAVkACAAAAAAE7ePWPxVFcvH2wwNtucFsS/KMQvBgoFAFDisoFRYcyEFcwAgAAAAAI4r7SX548gjMFuhQqms82N2WymvtnN6EkjluZx7zL32BWwAIAAAAAB8mw/RVgj3+df7HC+eELuiUFuuio/Js2yK26nE5End+wVwADEAAAAADvNXgZnkf++epaw810NU9+q7SFFIXyVxN6QOFuNBAKMqO2EQ+IZ0DqROFw96GSzs5gADNTIAtgAAAAVkACAAAAAAwCxOYz1NKoGZHp4qHrVq9v+3QmyoZkO+44a/uVIFI3sFcwAgAAAAAI7aO77woBZy8gMWRTNH4UhW+bHmDz6czEq+beqz03aXBWwAIAAAAACPA/FlSQqWXM+oQSFQgROCKdTBlGz1Y8AVwxR4hijdFQVwADEAAAAARLanlMPIQ65AUOg0Flf1+iBanhK/D+CZDXkSxBadjhxmtfCqUeXvuvjh60uPMlCEDgADNTMAtgAAAAVkACAAAAAACxEOgKjKVviLizw03DV2+19BOVauCQnIHeXCs6o1M88FcwAgAAAAAMig5mSsc9gN9AbWFsz16vYjXdt7Oz5wo3vNz3rWtcbQBWwAIAAAAABvnd5BKURjyg8HoiZZslVP8zo+eagaKZWNCa56BDL2aQVwADEAAAAAqLHVkLtbqDtoDYNW3O0ru7u6WLaoo5nF3hYl8jmoPmw8DdvWqjQKmEHSdD2OSrNqYQADNTQAtgAAAAVkACAAAAAABXylfkQmlOnzeLgsDjUmtY11pf2HeCw2J5NYL2+Dl6AFcwAgAAAAALgFwT1PjEpZejQ3B8wY8/90LLaXLS2ZP2HDmoUssrM6BWwAIAAAAAAPgx1b5dGMdkLEhksBMTlKI2pEprqV9qdIVRXdc+DDKgVwADEAAAAAN3/V/sZHPKuJejxy41JVBxge3JraOWd2ZsQPdVSAHiQffzwHOIsXrU7qAkHOIYeUiwADNTUAtgAAAAVkACAAAAAAUubxf1hn5VaET4TKigL+R/S60FWDwnT2DJTj1s9qf5YFcwAgAAAAADNw9vOFGnYszYbw8cmj5VKNnh4NrbEllAIYxC9mdXAEBWwAIAAAAABQycJroBpAmZZPI4V/iQoROxCJUm6JTugWDKFyB/GwVgVwADEAAAAA6rGKqO05mjOBOVxC176SOTnQU6Re53+c/OuVdzMmq6Z9iRERCWb/vVEby4U45PuOrQADNTYAtgAAAAVkACAAAAAA6MdU56D7BHB+l2lQpneMtXb/z4bx+D5KQbR3HCJNGR8FcwAgAAAAAA+HjgGa9Oh4ZKq/J20Q2sef38DlSnXTDPfNqK0oppAdBWwAIAAAAAD2wsiEWh4THy/4xxnVeQ9EUAjD8/o02EESbjndKo7ZYAVwADEAAAAAXV3SZ0PUQhlpn5cfLAv0/fQRGMTqMfjXY/qRLj3/rInGh/lcuIalj11VvlJPbYz6kQADNTcAtgAAAAVkACAAAAAA+1wDUDcfegJYHTjWm1JmxHmAyBfkgmAUezHmWFfWwFwFcwAgAAAAANYytE+WEoNFZeUcJccZLQn/SoRaQZrfBl1SUjwdj0WCBWwAIAAAAACVUZUMSxfpM9QtnBYDTwWkCUvcM1iBzw5vpMb/28czdAVwADEAAAAAnaaTfVbNZCIIlxPQS535XVerneEcS6NXNbynKV/f7lk1hS3guPukbZDzX/xaspf7QwADNTgAtgAAAAVkACAAAAAAB+u1yi002XD8Q1ZtlxvpgXDhNNr9LLCkGE/t+ef/jmEFcwAgAAAAAFUDL1YRmiVgP+7Pbt+ruQEqFAMgcbHeI3clp5bKmYzQBWwAIAAAAAAEQ2ZRgjTVNx7mRIDIMBkntLSjH1P6DbRPWFDVtoSkQgVwADEAAAAA7cXKvQzEZmQxnYXW9JQqfyh9hcsvdEKhSCAX+ssOnryFN8rdH3eTT62UUh+wOzlt3gADNTkAtgAAAAVkACAAAAAABeBGBe+CL7g4PnUG3rVbXsQvqbC87Gtv2+BsaLO9AHcFcwAgAAAAAAaCEsSpIF6he9zZ1xj6DwMWmzN3n3bhcz7nMNxXo9w3BWwAIAAAAABWVEke4ISQQA70TOEBGTlNoedeqW/43q0L9FyGQn/pOAVwADEAAAAA9FJ3dLjd8ep3ySMYbwK9xUJ5anrXsSMdVP1Qj1/Wu4M4NqZhaBe/9NtM4ZWq0Nm1CAADNjAAtgAAAAVkACAAAAAAKK4EjDnlV6FIOB7saSFoFiw7g6ANotp3LWWIiEtp4hEFcwAgAAAAAP8SqHqBeusEURk0+Ssm6inTRXAJNM3dMZia+GvZhgtkBWwAIAAAAABruD7+1A6yl1kANLCmSPoIJJiEUmg//6YZs1gvXoBS1wVwADEAAAAA4drghEA3aj8yMKzuqr3M8NpWVcl+H/N/t2uU0NNFipH6/1uC8yt6oGpNG9hdTOLBlAADNjEAtgAAAAVkACAAAAAAd5PCIwMgds3oVKyIONqo3mH97+fz0w/isXa17xp2h7UFcwAgAAAAACw+sxOiy1mu3Mdj+OnvlXL79zI+EETxKE63O3k1FX9qBWwAIAAAAADVvFPdKaCbaBX0LvFd3Ka5Dmu5VY5Gjbi0crz5eiqtywVwADEAAAAAuE2BtMJrQMDj0QYbHMxND/+Rgx+ILN5BWwxxcjDvILdgG4+Emg8eOW6iBEFiUs1gFwADNjIAtgAAAAVkACAAAAAADr9aMiV0jmQlz1iTr0wQejc4nC8PxO+3re04grqnmgwFcwAgAAAAAP/KpIuSpoJSsF1VLkjUBUf75mbuCbw6nB/mHwQSByWzBWwAIAAAAAD5rJNPm6rE+kh1jtLgX+RXTrMguMUpDDignC5gANFYxgVwADEAAAAACtB+JUEHcZQ+68HUyZT2JX3qev+FiDXFMIi61Y3YBZf4jn1bEPkXghEMEhpz62I3PwADNjMAtgAAAAVkACAAAAAATclzZLE+bERz8JuMcHVLlKFFQYLAMJLdkSMDTHw7o+kFcwAgAAAAAOucHXR5AlYBPgslr1FJMXGR/SeRI4FDfH4iHz3j6JDgBWwAIAAAAAD6pN1KTXva6w+BQrmP6gKB+ech/bViCESgihVyJkdszAVwADEAAAAAAD3ALu4u+jZjZAjePCHMWJXLNlvdFmaoHo/3zqmmyQyGID0dHUB7a3djnhOFXTmToAADNjQAtgAAAAVkACAAAAAA1sq+fwmCiA2fQPspzdvqgI7f632VDSz5vWAm9KjxsyYFcwAgAAAAAE3Hba5X2rrq10iZ7qvsc1kC/kew2W8LyciFateP8tF8BWwAIAAAAAAfDe2xVegMVNZQLT47dQypFaIHFL7Q+hdtjlXvbn5FbAVwADEAAAAAA0ifFMMBHkaPKFCEPfuTi/IWLlg95SKUtx0FNQWTsMyYOqI/ep0aMRAXzT0DG3itlQADNjUAtgAAAAVkACAAAAAAuXOeQcu9noiVG9ryzs//MkZ2Q2wEhySSEKekEYqm5DMFcwAgAAAAACdZquJYJkAJLATfEiEJr0g8QNdCCF/p7y3vccNr1UwpBWwAIAAAAACW2Hctu4Gps+T8esFrk17BVD9RkjS17aXCjUIVvQl+sAVwADEAAAAAyvJLVubY8MsDakIB9cCYI193mFCYLKH6zvziQIXOGBu8jNy6eF8D9NC49XRL7c2VeQADNjYAtgAAAAVkACAAAAAA+l7FNPa48sKFFBQYYOi95N6FXxfGO49MbKkWZyrwBTQFcwAgAAAAAOWixg7y7WJ/VHrVdeaTcwOJE603kKZ4+dddJCo1ClpzBWwAIAAAAAC5SrcNfTcOLySUN0krstzDiHIRIWaMk/FiCJDiQ87C3AVwADEAAAAAd59ufOlU4gRupOJOkUbtqqah7pgI/kevS4xjEtBDQkg/zIvi6casJq3sBhsyswn4hAADNjcAtgAAAAVkACAAAAAAOiu9vsMg+OkZNfxbxKRldNNFGF7NcU/4xsOifvp+PaIFcwAgAAAAAFCSKH8787XSwC9akucfCzIvrm0lJ0JMBVjV6HFqUdMEBWwAIAAAAADfwmHmdChC9doygTAhLolV1IBijvlroJA2+DcOvNHblgVwADEAAAAA1zF2oX6THDuIL/zDPC5cYhgyDHQjYsnIFydUGR5oVBKoouJuRkLXXkXgu2biBu1RBwADNjgAtgAAAAVkACAAAAAAZx7VF3MPOPRziPLz9nLks6+gwjeTX2vUgWN/F77s8qcFcwAgAAAAAAaj0KSFBshMnrRX3NgPjlGfNJIEAMLfg6bCOAUn4QdbBWwAIAAAAACsmLhfW/4Ve+tSAqQfJC/pOmKQO4utELy0chXaBk00ZwVwADEAAAAAh1/HKSRZwgjTdqGeZ9PzaAFomh35O7uxBPPudD1GvORdfgrYZlp45/uUfhZu+MLktAADNjkAtgAAAAVkACAAAAAAxS4EUkBX1YHPVn51JFdhd5DU+epjTp0mwxvOXBAZqjUFcwAgAAAAADpeJg2WZo4nhyL/+9/tafq3zZQFLn86lyZilx+DYrsZBWwAIAAAAAC4Z7RZMnNzf3QxrBSTC8AipSwuxGlraMckmHJ2ivyyCQVwADEAAAAAN9hx/avuq67NS1BCWNRCwlUjcop6coljuO69dgGlmYVql2K0ZYWcCsLz+RCQjZjf0QADNzAAtgAAAAVkACAAAAAAj1dM8oEWlytUW6/7YXcdn7mgpfLWfrlESzEIvSZbN0IFcwAgAAAAAOlOcPAHzIsFFvUVBtr9vzqgx+tDpsFNv/Febtz8+JHBBWwAIAAAAABuKSsMP8bCf32yf54CO3fBE/uUEjTv6K0t732WcssqYgVwADEAAAAA/q5+nnuHImX7i5B6sX6WUQW2B/OeemHKvBqyU75rxVXzX3NrPD7VIKhZZpnF+ZGP2gADNzEAtgAAAAVkACAAAAAA34EUbtZOeUXxzu3L2NHu8gGfrd1+jS3h8RVn5Au+OKEFcwAgAAAAAO3hj8t9fSyVeA9IqoZRLUyHhVz8HB8aVkvcEO5fAntJBWwAIAAAAAAFpeJcC4vkuMAK33mF6PfXiH5lhjZR5yVGrPFoQ/So5wVwADEAAAAAqvOoid/VfqWyiBnKSf0Zsv16OmXZYUKfe93O9O/XSa2siM+oCI/bwciRIG0iGAPq5QADNzIAtgAAAAVkACAAAAAAldC9qJ9WZBLM2C3XbABfXv3dgJVOGF5sGrszzDT6r+oFcwAgAAAAANrAD/OdCo+ojnnyw+cuPn/5cGdY5LPg00/SJhimm0zUBWwAIAAAAAC1iWBW2hoA1/6KHExiEdPNBsyKzrh79w+m2R4ZvkQy4gVwADEAAAAAwHyJYakJ1naylXod5z0nYzhD4/0A5RtExnpkpK1GjoaAhLCzdixPXtxE1wHMj1uXwwADNzMAtgAAAAVkACAAAAAAAXG2IbGuaB6c/imf03BlWeSaaeGv3Ot8xEOGtEn8XKAFcwAgAAAAAAlHvcsDsXeBDJhUot5o5nMFRVBn1iVqgXKqMGeFM43VBWwAIAAAAAA1qDEga2v1tYPUPT/F3AQPMxmiA/o4dW63a80cA2jGQwVwADEAAAAAFd4J685qatsvykI0c0EBNJjhFMEGJhV+YYPY3I3tW4WNtbK7xYlEBtD3dqeef6688AADNzQAtgAAAAVkACAAAAAAzyPLqdjmyCTg8EezTf7leeEX49wr5IuTICg0qusAG/gFcwAgAAAAANgroB26S3q9BKIv0x10yx0FNeZCBqQ8mdHu7HVeMRnwBWwAIAAAAACBb/VFFntakSZcw305Fb4oRJZ5UohrElK4Ag/VHRawWgVwADEAAAAA+nSl6+5pjh1VFfGUhKzSFgLCWuOzUYYI2HhU49wOKL6/HrnfpxubbRLjbpCe/aFzsAADNzUAtgAAAAVkACAAAAAAXCcCM5Qq71nJb+LTIo4CtEbXLPMtlGqyPDDuvWLdIhIFcwAgAAAAAFD6bs7UiiHV+JCvWlvuZh1yC9oqUe7BdeYAsRASm+XcBWwAIAAAAACgCzl0w14hC0H7zAj4Vp5dJ+3XdqEtTPs+cnWaPO5jbQVwADEAAAAA28nqqRT0lh4ait0UvYt8MTs7yaSAjj46nXMgfGPoXPNKY7CFpYvi4oDQbH9nZuIQJwADNzYAtgAAAAVkACAAAAAAxk9Y2osGntAHuIjN8Gbe7sIhu8eYPyMQP9QtsaTQ3PQFcwAgAAAAAFc/y7fmTRgTIxG1cYwRJQUpdzQ6Odn4B26Hlys9Mj4tBWwAIAAAAAAx9GEyZk9lE9g7IgibF3LPjahGECCX5XQTVgZ0BK2h8wVwADEAAAAAtaLUOUgImDp5QWEzyVsKGWUlnPV7TyqEa9DY7Cj/E8cuw9Zlyw/xpfRUnwhLTA25aQADNzcAtgAAAAVkACAAAAAAEs3uyc6tO2wTQugWhJnP14BffOA3QqQUMOUvk2UAnqAFcwAgAAAAAHCwgWFp2WUeK6nH9bp9tYpe+H+sORHotCW6IwKyKEYZBWwAIAAAAABdi4VgiOA+1vcYItFeZbq1XR2zcMcf7kuknAU1tQem4QVwADEAAAAAhtOQQ/N57940BQJCoekwTdx/cD8ahAuPAqOQY+sRMgn+JyE179bmgpZ3m1TTQUOzWgADNzgAtgAAAAVkACAAAAAAmBUSm9tv6vheJLue8Y6lnmhJ8UTvMplR1WGRNFS6lr0FcwAgAAAAADuHUbrPF8rsvWuo9GAwFGb5E1/yu344qoUCxoxl+b4yBWwAIAAAAAAUIG++9dE4E+iRk9bJMcYjfViyvUEBIcDeFOGIBDvxwwVwADEAAAAADvl5S7ClDi3JEcZpvo24CW+mzz9+u2I85FmhxOyJpGJlWX6KgAA6UpiSwoMprAstRQADNzkAtgAAAAVkACAAAAAAfxGWh4M7T+gAi6BKcqxnGwF7Sk9ivpc9aajyaGsSCSAFcwAgAAAAAKoSvUWi2aZZ9wxzJgpNrfOUcP5uqPTcBQ0kN0hnjCrRBWwAIAAAAAAC0NeP3Gs0uiO+g8n5lPB5P5P9Hs35MGcfywm9H+5Y6AVwADEAAAAAHcWuC915d/gvaJrVhT5i473SSykAOwLSEOnHGEjJHqqTqx1zgl70N8QxuUW4fotKIAADODAAtgAAAAVkACAAAAAATlvhfXQb9ppGDKORyhHd86bU19SoE2rZNn3Q0AxQVPwFcwAgAAAAAI819CkjEqOc1tSB2en0dZ1vK4/yAYoSb3+fcJCqUqf9BWwAIAAAAABtGrFR76Sqk4GLtDJkpcxQ6AWdh7cALeV8rRzlxJHaSAVwADEAAAAA/SsoD5nsgy19AQwV6ajFqohd+xn8O/bEObjenAda2nmQe/LEd5eJqCQP5j8kZZPqzgADODEAtgAAAAVkACAAAAAAvEXuOCo7cIz64P+5PFP/+jDve0X1jwtsoPAHWq9Yir4FcwAgAAAAAFttuQzKono1P0RCXq8ZDrky+1bO/36B5qVZXorsCXPxBWwAIAAAAAAs5bIwn9ETTZ/9kq43rl3/FT/hrpkvY45imw8FJSLIHgVwADEAAAAAKeqgPGKeQvN37Qk3BaJGXKEuIlELDsb33iy3kEdqthFCvHtbyN6VzSsVDUG2Pf9KMQADODIAtgAAAAVkACAAAAAAAFKMTMtg01mZCGUApP5b7Frq5XRSy3Ub/akAtGdW+IIFcwAgAAAAAPVJlEhw/9KWA3jO9UOckkiwJW3RFd1PFdJzqPnjHiFUBWwAIAAAAACV0IUVha1YxH6jh5wg6mRdwPOyguRoLyNB+cIbNPJ6zAVwADEAAAAAz0hAGxbP5w/CWR/T3K6Zdrkbw+R+jEWKlqBKpBokW60Kqt7CVF9muyodBNtPc37SaQADODMAtgAAAAVkACAAAAAAr2G+nMQJegf/TzU+OUdGc3L6oigVrY+Mt+3fW4r+PlMFcwAgAAAAAO4Mq7kg7QRsfsXWVaQFikpMLqcplxHK1xzwqwflD6bjBWwAIAAAAABaKqCGmMi4KO2SU7kvJT4vW9C90OKLWIEFjzuCS/toBQVwADEAAAAA/PvwCOf8g+ihTJRYrvZZAWJD6LyhK24Jg1SJ802gOc2zrl5a8stfRElE6pmII5vstwADODQAtgAAAAVkACAAAAAAomx5sA7qdP1tVkf/CEx/Gx0btuNEJDO5r5hYGS1uY7IFcwAgAAAAAB+5SGXGvE7pdtJaKjpZwJp7tS48+PC30sFx+1mfDlUDBWwAIAAAAABeMT1mIiUK4iU+NCQ5bFGqjrezlVVFnFkvuW46roMmuQVwADEAAAAABtm20dhwZP/QsY7V3Vqhtndd9D78g8Fbvl7i8wUEG956LlPCNJPuJmyaSMxeqDDHfAADODUAtgAAAAVkACAAAAAAth8+izW3o5w3kpKzx7+bm8SqHhUaKPxghWKTHjznsrYFcwAgAAAAAFffzKrPfe8f9ZvmG39uLM2wAgu1UcRBBzz7eSYhXBFJBWwAIAAAAAAQ497cxozpDJVki4/6B1ubs5/Ygva7y++Uouu4xpmojAVwADEAAAAAebUyqPjX4JUqeFNkIFT9zWcrQ7O29QrZwoPzdwyNvWSugCwX2/Im71/JcHsNyBDmagADODYAtgAAAAVkACAAAAAAHnENcGmBOBpLw+grAYI+VK86D8LTDuQPSyosHQdOIOsFcwAgAAAAACdh/dwxuz71twmO5scOjNgWvJEjPJfXgviWTNW7AXIfBWwAIAAAAABmD1mTLHQwEetv1mmtYEKqu/TyMfeuvqn6LnGAguR3AwVwADEAAAAAgXP0iQCZOXt+lznhB3cOyKzYtyTWm9T3Z9vMq4C5uAwmFgQ09xG1LN3zRhOie2mauQADODcAtgAAAAVkACAAAAAAduYpX2r0xThs7TeSVRyOt5HkFyjVNyLg2Vdufp4n3VcFcwAgAAAAAKEz0QrxKIjQ6RQxz4v30ZhXzoJWx9HeisrMOgYfHmTKBWwAIAAAAAA16hHvBICbQAQ3n5QU/9DaWj2tbH7PGP7KapBE6DohqwVwADEAAAAAlZHGJJ5iW3t/9SnM0pJGiqP1KfMoG1khWRftut7QXTqcPPL9qEE0Hed4+BkYEOF9EQADODgAtgAAAAVkACAAAAAA5uyBeZVB3I0K/Pg1H0HkkT782YCzUdYfx6Cdpt0xqTkFcwAgAAAAANUbrL1DKYKnUYZOZG5lXXeX0QOQ/6S22ZEHOHwdCiSfBWwAIAAAAAAAZ9L8sqMldlz75KJX01wa1MdegDhofjJqfAiA0gsriwVwADEAAAAAzkwz0CCnaRX/S2x7CbjZXCud7bFS3bfhEkVt7uvAt2SnFDM0G5znjNDvWRXiCEk6tAADODkAtgAAAAVkACAAAAAAP+TR367xbcr3moPbC1LrMhVqn1xqptmjQPzLEbWxsZgFcwAgAAAAAC45if+I99NNrtfM6leKo+t00KhI8s3Nyop+hnEVT5/CBWwAIAAAAADT8URrdKR8/jDzfiUQfwMfTW3nxUwmOcps4Qmgz5xplQVwADEAAAAAqR2OD9KNXS0xB3qvDG4+FawpDkxBEJZEVamI8yEB8Ze/8vLIhcr3byu7E52YH2ppTwADOTAAtgAAAAVkACAAAAAAzYA4Zj2dVagmGG/h6xf+rSyEr7WyUl0fwIktZDROez0FcwAgAAAAAHNrEtv4JATXayzmbVooB7i83wE5U2RYSWO/JGt1YpF+BWwAIAAAAADDITxhkknItgQJwLnAvutlWOPYxhayipvhOkj/3Hq7TwVwADEAAAAA9qnxOyDDuRa9BJnrzVoIn+RN9zKViv5TdplUoAkBjRWFImRTG6CNIhZ5Tgz1rN1KJAADOTEAtgAAAAVkACAAAAAAw44xIRftC0TnPi1MuWA/o+T9ARZEF6tRfpmvV3Cu46cFcwAgAAAAALsPk9dRkFbMmg/Ds0/zLiet5t4hp5kNK68Dj9idsVmUBWwAIAAAAABYn0fDPDMbcehfT41tJNtPDDN5u4lhUQ/if3p4PhH0zgVwADEAAAAAAkt3xr/CiwAO2MDgQUzP2kjltOfREZlGUYyfGWXhVV089npzZj6rXcmK5JYQAbEJcQADOTIAtgAAAAVkACAAAAAALcO4JWzhy9IRWS6hpveSB4yA/xE6SOv7p9JjxqJ5ewQFcwAgAAAAAFrjQkU3uUHB8fNhQYTmXr0RkaucIWLTacT8rnpJDAPQBWwAIAAAAAC8RP17eK6mqchLgk5Pzuy9S/zhHFVzn9NOlmBCmp56+QVwADEAAAAAone7ayX+8KATToiHFMflIlcd9DH9X7DRFdQ8AZeQoaHsoqjiQ2oTh1aBa37Gz7vQ4QADOTMAtgAAAAVkACAAAAAAbbEkaX2MGwX89hfmGnDly/xg5YexUWPzrRJCmAJiMXUFcwAgAAAAAJu5b6rDQ/5lVfKuHhufrFP1plUXgvbNeBEdSo1rlktiBWwAIAAAAABrSu+uo4QUc5axWiOZMaYis+OoTML2eHNRtEy7SOKk+wVwADEAAAAAoBhrVe7ffxRz7kwEC7Pm5MPaBWTJEstQVlDULF612J1GtahuKYQeVuP6kbEqCDhezQADOTQAtgAAAAVkACAAAAAAN0O5zd//aYzPOYwq8QYZHzRdJ2uhRG5i8esuXaxuA/8FcwAgAAAAAP7Ji2Zi3OWOZgsWZJ7MLgs0u73KjBC8UVA3Sgycy4HmBWwAIAAAAAB4GNNbYKxvsE8UwvO0FGLjlxDTxHliReMPJlShJmvuRQVwADEAAAAA8wAuMUZAXGmi1IKs4QrDd80Dx/pGUzwnsMX3CLSLW381P9UZiWjIiDMgWwHujWChpwADOTUAtgAAAAVkACAAAAAAYdgWmhYmif6rSHZ//Im5C5DWULork0L/5y+bnZAFijwFcwAgAAAAAIaptX6CjszKkNpYeTSN89tcDylnuHzS/MrmXy88l4H5BWwAIAAAAADQ0crOTQRVyLU1BQS7uUj0tAquleg/I6pEc5H5ntL1NgVwADEAAAAACjoXct2uQw/3hh2yOGWiZPewg4ecUnmxNbd/D92HgMxB6xKcS7hEe8fAL8hlkITl+AADOTYAtgAAAAVkACAAAAAAegNnLc4MxL8/fwNnMThEZeDfQc6eVUdpHo49NPEfTbwFcwAgAAAAAJx89W7hKAIhHuwASs8FXljKyX+qdUdbT5QYQmG93/G4BWwAIAAAAAByCJT2VOszNUkx2/wnQwhFsFRHLYnxMQAVOHGKkEM9ogVwADEAAAAAda8OOijtYDLlkU9ouL+C87DRGAtGZVSEyFldn3ys4yaUHx6MgyJMmrIDzxbJXR0XuwADOTcAtgAAAAVkACAAAAAAiO20jowXBHW+lWMtVq8GlkkWDvn71JnCQ76raUfGjJEFcwAgAAAAAASTo9X8s4XbOxfPkmTGjJq3Ukq9/ErKoNM0NmgaOOmPBWwAIAAAAAAG08cKyZPtDWjLTTSl9ASNKo4F7MwWAHYPkkHODkHtCgVwADEAAAAAJkBY45KbELBSIw92spUQtpuBZcXnRx1s7wk1N3WkmiVLb7MhLLpiNBJuA/QHMFCaBAADOTgAtgAAAAVkACAAAAAA9mFP1cFLScbLN6lqg7OOlCdag/VVyq6fvPNl+euCve0FcwAgAAAAABb1Wjz8UxSQFz/vArL/IbMp1P+HHRwKDLbCcqReaOgNBWwAIAAAAAB7VICdOmgzKM5QPH8jY3XgGo8dCS8y8e+KGaOb9N3CqgVwADEAAAAAdG2/Hb9K2rXJBqXoYypm7F+jEE8af3Rb4fsow+cyjy+4v6Dsq3sAllJmmuY5NnuVXQADOTkAtgAAAAVkACAAAAAA7HrNt9jv4irM77uZnWn/dXehbJiBXdkB3cPdIiJXFZ0FcwAgAAAAABULBU+X4tgY508tskrWxdA0pAR+K7Ih6l4kOejDa1TdBWwAIAAAAACjsPvDFfg0t+NslniVOKr5BQ2CPr+T2/pDL951ZQHFNwVwADEAAAAAFLnT5pM9IVZgtzp199ceLx3qoCuVskrRa91njbp+3S5nIfgNmSSCBLF3lXF6eyEsrwADMTAwALYAAAAFZAAgAAAAAMce0iNChW2gdqAFeOMKTI/JSF7v1fTIswi8mh+x0pXsBXMAIAAAAAAXuyF/uoiRQjXhS8hWTWFx67wtM0q1NDIz4zoU8DF5KgVsACAAAAAAzisTNUvFpc28dV9yS5tpUQ6XCR1WBBoo+NuwLo5lxF8FcAAxAAAAACUtkLTxPZKWKFxbEgXEORsyp3gEc9NHy2BsFq7l4H0TGFc3UAZol02p1Qihw4UxLzUAAzEwMQC2AAAABWQAIAAAAAAPh+NIeRXJHD+I09A2hztm3eAnsPxrLGsJMloXlDjfQwVzACAAAAAAQzC0DTUZSw8BiI+3R8Obx659ZtyDr7pPlhJz7kRhJGgFbAAgAAAAAHn27uOIvPP/1ihOkU84j96M+SdQ7S/wkYHva/mIdZqxBXAAMQAAAAANhRB/wFondkpv+C0BhYfvK2bZtHzBBVvXAAlEXWIRiBVcG2OrE3wJeFzlBR/QEM96AAMxMDIAtgAAAAVkACAAAAAAx6Ev6ilwmpT7RU7QdVqXDeoF35q+lwORK2V7uXIZ5m8FcwAgAAAAAEajmscfDcsExWknLP9OjEnyNvhvCNylLJkpabxTQvXcBWwAIAAAAAAHYd1VyPWj6LLZ6QQV/l4xnAcPqSPx89nVdyR/VbrZ8gVwADEAAAAAB+GxYA8OBu5992JayqSbSvuWEQs7bgRB5WuLNw6s2XaFUjkkBpYqpz16tYk/pYBo7wADMTAzALYAAAAFZAAgAAAAACV43IbGVp9qKF7SxuCDZ9Hs1VkqWbOM1AODRlaDkEDaBXMAIAAAAADZBkOB5PSGYIoOPYPrUxSlUHgpz9uaNgTChthGUGnxGgVsACAAAAAARQWv9ACJIlV1ZCr1OTrwo8dgqJdq8zXJZzJGqEDKq64FcAAxAAAAABxU4xz9OYykC56I9j3tr8KdwU3MAfO0ajkbLwPxcz+ZT9cGr65abrkCjHaDrVAbqs4AAzEwNAC2AAAABWQAIAAAAABQfXa3FNemSi3kj8sLJdjCCjmzFWax1pdX8QCtZKp/lwVzACAAAAAAryd2LjCOfmHERDsXjsfFq/0P7V9dA2yVg0Ykr+6NgYAFbAAgAAAAAPhhpEp7wr3gE/EyHQs0lAXWOVTZX24nrmQTxa9/cQXfBXAAMQAAAACGEYjfZm/OPlqwxdnaE2eS14FVTQe1Tn2oaIWRBVunzif5TsWzUwQvLNrxsF3UBHRvAAMxMDUAtgAAAAVkACAAAAAAZ+CKRmHj3F5D7HaRPwXhRhNePRULn9d62AMiSZ1JPrIFcwAgAAAAANeLGlCLb6Sn0iW6g5q/Kn4L71WF/SLvwz3brj0hnXHQBWwAIAAAAAANuN/lD3YyKnl6IOyv9pEc55by8NIlBgVXQHOnP1vdGwVwADEAAAAA1MbVVc9J3bC7uC++RwLnWKg96YrAYU0kG0g9Drho9Pp88oNcSmfMjSr9ufKQXT4DJQADMTA2ALYAAAAFZAAgAAAAAHqnXSNUFkWPeKZ5bFEtySDjCw59mXtIt8KZqxZFVl4GBXMAIAAAAAAHx3kv84ye4DzkFhfFLxDSnTlB16S6ZVh1JHYfN7kLdAVsACAAAAAAD+vki1ifi/nvuo9O1OreFcvuqd5AJV3px0zEXRqNVVIFcAAxAAAAAJ3I0eiJbRk40/Q/KS1hOtW3BEPr3yEPgxo4AJeK9zdEGT59oojQy+BSkJzFEE+LCqoAAzEwNwC2AAAABWQAIAAAAAAY4zJta5fX2Tqr7gzZ3QARXVVlK19CPJCfwkrMU/lZXgVzACAAAAAA5oB/PZ21vgYKvptg1uZo2eXIqP2DWDEAtzartIQXcWsFbAAgAAAAAEJczcCVzaezemle264SSgYC/fhQBp6XMpnTeW8yyHRsBXAAMQAAAAD0IgH+DiGy8H50sjNgOsdcQQKC8Mh+hu4bkH/8DnYcx+iIqBCtOFnRK7RwpYngYmErAAMxMDgAtgAAAAVkACAAAAAA7ANuRx2jJIucZGmDTv1btFiJHDcUlljLIzanROdo4fEFcwAgAAAAAFQFLSKNyEYSsvtzHqbYMbNMGM+wJoiNjT2xYR9rooA0BWwAIAAAAABMtd5VrVPS4BJKz5SFEc87n03Z6zpEcss03+g5F3AVUQVwADEAAAAAkeuZI+XYnuzjhp0aau36/FVefNrk/+h/6bydqnFyFNE5oa2nBazz2iYCWNJMTGc0DwADMTA5ALYAAAAFZAAgAAAAAAPibWFsPWAvXr5AgxpkeEZpVMkMUEz7N2Oc5jlHJJBWBXMAIAAAAABy+Hv6hqUXk+IarH3rk1dj7kILAQoombrxskMWEB80HQVsACAAAAAAtTTXDVngFHbYcfQw0w3WBXxplJlUV4vGlcEgeDDOMq8FcAAxAAAAAAy7M3x9gjze5Mp679wRE2brTNx9RQ7MFmZKrlJROBtAZbjGffZJrfVyl8oXc5nzzgAAAzExMAC2AAAABWQAIAAAAAAVGmrXV/jcZR7agxNXTZPBNrHGOWMfN0jOVUWfGw2sPAVzACAAAAAAA2/miUiZwHucaHSHTdB8FRaE9uxcH19OfY4tFcGqyDIFbAAgAAAAAAhxtIx2PmwwBJDo3621iSonkw1v74PdvJw7trjWvOerBXAAMQAAAADHTAJI2hASMc8DpqcxJGEZh7foFLy+w7AAG5igNZU8Vm8tQz3SNHput9Wq5VpHkXppAAMxMTEAtgAAAAVkACAAAAAAxBiqySsPkuapaDZDvfQfFLBOzjHC5h7Sg4MyQ5ROoxAFcwAgAAAAAFcxaN3aDhFuUlpD6zeuhRLTEaGs3OZ/wU5LeGvZP5GFBWwAIAAAAABpkeQlD/KHHWTmKcxyW758CKbe7xBLGtwF33nZfPGufQVwADEAAAAAYvc68jEzkEsnVixX+TrUDmHil55CNdoULn13/Mfdh4sYpTCocftbGsipImD0QVCwegADMTEyALYAAAAFZAAgAAAAACxNfjf436+QGolaPz7XS28P8hUFRtY6YJAfqk3Pf1KCBXMAIAAAAADM2rJLmPshEfWbm3K4To8EOjwUUn2hlfps2Rjiay0PvgVsACAAAAAAJaeKOzSY9zjqnnTinIap2xIPjd0zt1jd9sbtp721MnUFcAAxAAAAAO/CDlhXsCFPuPSdl6I18GZEjoJfdzixw8mFxrnlYwWgBRHHXxmMCq7GNY+H4tvmchIAAzExMwC2AAAABWQAIAAAAACIDrnH9vERI5PChxSa0qrwW28jHpy3buQpqWfumfJRogVzACAAAAAAq7yp1LUuGriunoC7e8+OSx0POi22x296OZPu95R9DvMFbAAgAAAAAMgs1n9pJZO/f4qlN1Rj1rIlK+rJuy27qYnTv6sZr2LaBXAAMQAAAABassHul5tKdvRaqXQi9uMmLJhJtITMejl7xgXyhBMAJo0tbj/n4WgpA7rZz51SDMicAAMxMTQAtgAAAAVkACAAAAAA4ZCAM2RJqe1ICafnC1/7puyTJPRhximzjegsToLbbDsFcwAgAAAAAHlbGPI07xbgR5uMSshE4zN6GayzNMd/z7FuqFzWY6VHBWwAIAAAAABeReoJfq2O5D7CRBmp52NW6HdJjbojwuMUX9AfnlGymAVwADEAAAAARCq8g6oxOp94nLoGujCyr5V20n+hZDtkcUj6J/Xdddyr3kTEph/HLgAaOoqt6vHK/wADMTE1ALYAAAAFZAAgAAAAAFc2dP2QlM+bSFMF6RkUYrvwL+fRUhucIJfmcunjzWkPBXMAIAAAAACPvTIEvmnxSzBRitwiDYOjPO2DW5g67t4sBv3e/LHTmwVsACAAAAAAoKcayqwwpfBH07phKyw63240ZuSjVeSEI0WexLy/OhwFcAAxAAAAAEo5pUxBWLB1Rz8T/DfYwoXLoI4YXh7lDnDPgZtQjT+W/mKwaUo85O9k5zsSd8lRCacAAzExNgC2AAAABWQAIAAAAAAsBnjB5uPJp2n9BK8C8tbXM+QcxTCJM/JEQbIH76OdXAVzACAAAAAA1c1hdNQfdfF5sYYxItQF/eHg99xJ/KJeYnBwDhtZWf8FbAAgAAAAAJ8mFbhGXrrYtlMor0Fq73IrG9/1LRihwJ77PjwHVuF/BXAAMQAAAABNDH5hxZTdHX4B8G6Q3saNBiuWKMb8bzv/uE+c2Gg1Yv3g1Li1oOtX4UPBxYhOTfbmAAMxMTcAtgAAAAVkACAAAAAAJxXeoVDaOAGwopuwJOPxqkgyJMGvh6rly12jJ8DSbVAFcwAgAAAAAJP8Idu4PGsysU5UwksYt0Dg8velsH9RgQQU8r3gMMFJBWwAIAAAAAD4KDZLmeYvTbtax1cj+sus/ENxmxvkASe01rwVfM8wtwVwADEAAAAAoCHCVNNTqAQ/MA0kJ/p1do+iOUeGHBPvZBo9DdCFmQSvJ2GTdjhixLVxHP9joHjVTgADMTE4ALYAAAAFZAAgAAAAAHcrdO5ANRwtjS4Gvs/7sYlPhu/OVSyA8DQT0uNaMZ70BXMAIAAAAACq3RuoMnUKbjIMjI900Me2V5P231pk5ZEMu72PivuDDQVsACAAAAAA0LJSLlIVCcoL4ZV3HhYOYGbSbwAvWHswlvaQAShZjTYFcAAxAAAAAHk8IMGgJrspOIKTH5Ew/0L1ItNdlyLiRT67r4IF2g7YmCND5AOJqBxMlIkLFueZ0rMAAzExOQC2AAAABWQAIAAAAAAKWvkcygSCpKZEtKhwSfTiUdf7VCkBCllkxcrV6CrNGQVzACAAAAAAhKVqvO/JGwS2KyqoCp4Pm8Qfe5ITgMB/04CRC7+u18YFbAAgAAAAACkB3Mz0ZFYytbyqwZneriey76eq+uatwXevMlOZ33lSBXAAMQAAAAAbvxM3sQUI4lgJ3aZL/O+9nX1pFDG+LGUWJ/EtQ71VTMJY1YKCYjVAgWTjgs8Jzf7AAAMxMjAAtgAAAAVkACAAAAAA5jB0UTFR0WzjZmhzWywPQIhacjBXbUY7ZrWkyaUhEcYFcwAgAAAAAFU/PCqOLr1ywT03gVJL/cSLLPVy0bYFWY1hz+Tw5ruSBWwAIAAAAACqvDzfjUGvr2RS4QCNqs3A5TLB9sR8+UfpUQzahsqjjgVwADEAAAAA3Us3IPBp9bOET4GQ5g0UBGYuKO7ZwX15NYbdtQKqWhSfOpCiHlQKZzld98dw0aeNJAADMTIxALYAAAAFZAAgAAAAAOCngelbX7QKsseFwdvQU/WDrGxo+c+ApIBUrHapVcyLBXMAIAAAAADDlGsaSEXNZJqlmyFwlxgRolJsNCa3nnXLq6Kg++un+gVsACAAAAAA4sVA48x45lTl9JEUMXgzWpxqW4qFQAtAE4SR0tgN39cFcAAxAAAAAPhdQ0ryj4Ito9A5uzzVCtCSUehwkIHrPZiEenQ31bWaZgrLGYgrTkBC8oJvZPT6T8cAAzEyMgC2AAAABWQAIAAAAAAXjwmaT3kRxJKg5RKGEwAKRM+K2xQECTqfQIOLG+rrZwVzACAAAAAAue51dQfCgeuWXwc4veJ5AzfX+MpEvWlTJkjYhhV0CmwFbAAgAAAAAE8J3g3jDvVo/9cEsNgpYd7/8zEPJSrhNIaOmK0t2ljHBXAAMQAAAADYt+vXPy6dCkelXiStqka6Yv8Gdfika5dOv8ug35n6fKEH35lGOZ28wNvT3uEzf2YsAAASc3AAAQAAAAAAAAAQdGYABgAAABNtbgD/////Y46NN8CHrb4J7f/fE214AP////9jjo03wIetvgnt/18A",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "decimal",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128-v2/mongocryptd-reply.json000066400000000000000000000034341521103432300303530ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A6EAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAQgAAABN2AACwTivebwAAAAAAAAAAJDATbWluAP////9jjo03wIetvgnt/98TbWF4AP////9jjo03wIetvgnt/18AEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "decimal",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128/000077500000000000000000000000001521103432300234315ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/decimal128/RNG_DATA.h000066400000000000000000000372131521103432300250270ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xfc\xbf\xbe\xdc\xfe\xc2\xaf\x03\xe0\x35\x6c\xed\xb2\x78\x3d\x74"                                                 \
    "\x29\x97\xb6\x5d\xf2\x56\x70\x07\x2c\x73\x80\xce\x1e\x7b\x43\x36"                                                 \
    "\x7b\xb5\x9a\xaf\xa9\xad\xa6\x44\xfb\x66\x60\x69\x0c\x9e\x4e\x17"                                                 \
    "\x93\x3d\xdd\x2a\xdf\x76\x54\xc6\xf6\x1a\xf3\xfa\xd4\xd3\x14\xf7"                                                 \
    "\xb9\x33\xbb\xdb\x8a\x1e\x07\xc8\x5c\x0b\xa5\x4a\xe2\xfb\x27\x8d"                                                 \
    "\xaa\xf2\x36\x03\xd5\xe2\x5a\xbc\x11\x76\xb7\xb6\x86\x6b\x35\x4c"                                                 \
    "\xa9\x77\x16\xbd\x05\x15\x19\x31\x38\x8a\xba\xea\xdc\xd2\x2c\x9a"                                                 \
    "\x8b\x0c\xea\x66\xd5\x3d\x21\x01\x03\xee\x74\xd2\xf4\x8d\x8d\xf7"                                                 \
    "\x13\xf9\x35\xe4\x43\x42\xba\x2d\x8c\x00\x3a\x64\x65\xf7\x3b\x07"                                                 \
    "\x03\xda\x33\x2e\x8e\x87\x04\x97\xfd\xc7\x1f\x00\x92\x7c\x1c\x40"                                                 \
    "\xe2\xaa\x81\x47\x56\x9b\x39\x4b\x13\xc9\x71\xf2\xda\x2a\x36\x87"                                                 \
    "\x2a\x86\x87\xdc\x86\x4d\x35\x66\xf1\x4a\xc7\x8b\x9c\x88\xcd\xb3"                                                 \
    "\x94\xf8\x52\x5c\xd9\x73\xed\xbd\xd6\x96\xad\x9c\xb5\x94\xb3\xa1"                                                 \
    "\x96\x26\xe5\x59\xd3\xa3\x32\x4e\xa8\x5e\x5e\xec\x9f\x04\xb5\x9d"                                                 \
    "\x35\x0c\xc1\x2e\x69\x79\x2b\xf1\x3f\x2c\x99\x31\xf9\x42\xa3\x88"                                                 \
    "\x2c\x83\x57\x56\x41\xc9\x6a\x4d\xa5\x16\x92\x75\x99\xca\x58\xf2"                                                 \
    "\xc6\x67\x74\x7c\xb6\x2e\xe7\x71\x49\xc0\xdd\xe8\x71\xa7\x53\xbd"                                                 \
    "\xf7\x6c\x36\x0d\x9c\xa2\xfa\xee\xd7\x30\x09\x68\xd2\x47\xc6\xb6"                                                 \
    "\x54\xbc\x37\x12\x97\xab\x12\x97\xfc\x44\x0b\x82\xfb\x5d\x7c\x14"                                                 \
    "\x08\x64\x1f\x4b\x6b\x5e\x2a\x3c\x9f\xc6\xeb\x92\x79\xa8\xe6\xaf"                                                 \
    "\x0c\x32\xb2\x85\xb0\x13\x9b\xcd\x71\x31\x3d\xd8\x09\x48\x4b\x79"                                                 \
    "\x76\x22\x66\x6d\x70\xe5\x9f\xc4\x6a\xc8\x00\x6e\x59\xb4\x14\x81"                                                 \
    "\x34\xe5\xa2\xab\x4b\x72\x94\xd9\x25\x81\x85\x74\xdb\x7f\x4a\x1e"                                                 \
    "\xe7\x1b\x5e\x42\x40\x79\x56\xd2\x64\x16\xc7\x44\xb8\x3f\x3e\x53"                                                 \
    "\x6f\x9e\x19\xd4\x78\x16\xf4\xbb\x16\xca\x10\x36\x9c\x02\x69\x88"                                                 \
    "\x15\xd2\xf7\xed\x3d\x92\x8e\xca\x29\x01\xb7\xc1\x0a\x7c\x74\x86"                                                 \
    "\x69\xb3\xa4\x95\x32\xb0\xee\x2e\xb1\x79\x7c\xd8\xa1\x0c\x21\x36"                                                 \
    "\x77\x55\x76\x7b\xb4\xdf\x00\xab\x36\xae\x67\x65\x33\x99\xad\xb3"                                                 \
    "\x84\x5f\x77\x10\x43\xbc\xb2\x10\x86\xe9\x32\xc0\xb9\x7e\x59\x16"                                                 \
    "\x3d\xd9\x1c\xe3\x94\xae\xd0\x4f\x34\x81\xbb\x54\xa5\x40\x7c\xcf"                                                 \
    "\x0d\x31\xac\x64\xc1\x7a\x34\x36\xc4\xde\xee\xfd\xd9\x34\x2d\x2e"                                                 \
    "\x43\xb4\xca\x36\x7e\xc5\x13\xef\x43\x7f\x96\x42\x32\x78\x1e\x0b"                                                 \
    "\xae\xe6\x49\x0b\x6f\xdb\xb8\x52\x5b\xc0\x89\xdc\xe9\xfd\xb5\x2e"                                                 \
    "\xbb\x47\x0f\x84\x85\x44\x87\xc7\x84\xdd\x08\xd0\x08\x97\x94\x9b"                                                 \
    "\x25\x3f\x36\xab\x4e\xc0\x51\x1a\xf6\xe9\x3d\x27\x93\xe0\xf2\xb9"                                                 \
    "\x05\x03\x0d\x38\x92\x33\x3e\xa3\x0e\x46\xfe\xf7\x55\x10\x5d\xd9"                                                 \
    "\x14\x54\x8e\x6e\xba\x0c\x95\x90\x89\x00\x0f\xb2\x32\x22\x42\x0b"                                                 \
    "\x7a\x01\x9a\xdb\x6c\xf7\x98\x9f\xff\x09\x83\xce\x7f\x89\xad\xd1"                                                 \
    "\x7b\xc3\xf5\xbf\x5f\x35\x40\xb5\x89\x77\xf8\xea\x3e\x83\x03\xcd"                                                 \
    "\x31\xf8\x74\xc9\x5a\xf0\x89\xdc\x63\x08\xb7\x7a\x01\xb7\x91\x84"                                                 \
    "\xdf\x7b\x0f\x3a\xc0\x89\xff\x78\x97\x55\x3c\x33\x0f\x31\xc0\xcc"                                                 \
    "\x82\x4f\xfa\x3d\xa9\x43\xfb\xe2\x4b\x8b\x77\xc1\x45\x77\x7c\xaf"                                                 \
    "\x5b\x0b\x73\xd2\x9a\xc1\x54\x36\x35\x17\x18\xdc\xd3\xd5\xe7\xa0"                                                 \
    "\x1c\x05\x6a\x49\xff\x89\x1c\x71\x75\xc8\xa6\x5a\xf6\xb3\x13\x11"                                                 \
    "\x50\xda\x7b\xb9\xf9\x23\x58\xfa\xba\x45\x0d\xcd\xe1\x58\x44\x98"                                                 \
    "\x5d\x73\x28\x26\xe9\xcd\x5e\xb9\x14\xa7\xab\x4e\x06\xe7\x18\x3e"                                                 \
    "\xae\xe9\x8d\x34\xba\xd7\xb9\x52\x89\x8e\xfc\x05\xad\x90\xc1\x00"                                                 \
    "\xa4\x21\xbb\x57\x45\x7d\x7e\xfa\xd1\x13\xb5\x38\x51\xd1\xd9\xef"                                                 \
    "\xa1\xec\x2a\x6a\xa4\x3d\xda\xb5\xd3\x61\x48\xc4\x74\xff\x95\xd0"                                                 \
    "\x8f\xe6\x19\xc6\x51\xc2\x18\x44\xfe\x35\x74\xe4\x76\xb9\xed\x69"                                                 \
    "\xf2\x76\x89\x57\xa0\x8c\x94\x61\xcd\xcf\x66\x5c\xca\x15\xe5\x5c"                                                 \
    "\x80\xfb\xb5\x92\xd2\xe4\x73\x32\xc4\x0d\x85\x97\xf4\x3d\x40\x83"                                                 \
    "\x8e\x7c\x3e\x17\x04\xed\x8f\xc4\x27\xc5\x0f\x78\xd1\x6c\x09\x5e"                                                 \
    "\x0e\xf3\x57\x81\x99\xe4\x7f\xef\x9e\xa5\xac\x3c\xd7\x43\x54\xf7"                                                 \
    "\x44\xb6\xa7\x94\xc3\xc8\x43\xae\x40\x50\xe8\x34\x16\x57\xf5\xfa"                                                 \
    "\xa8\xb1\xd5\x90\xbb\x5b\xa8\x3b\x68\x0d\x83\x56\xdc\xed\x2b\xbb"                                                 \
    "\x37\x7f\xd5\xfe\xc6\x47\x3c\xab\x89\x7a\x3c\x72\xe3\x52\x55\x07"                                                 \
    "\xea\xb1\x8a\xa8\xed\x39\x9a\x33\x81\x39\x5c\x42\xd7\xbe\x92\x39"                                                 \
    "\x5d\x5d\xd2\x67\x43\xd4\x42\x19\x69\x9f\x97\x1f\x2c\x0b\xf4\xfd"                                                 \
    "\x9d\xa6\x93\x7d\x56\xcd\x64\x22\x08\x97\x13\xd0\x4b\x9d\xf9\x5d"                                                 \
    "\xed\xc5\xca\xbd\x0c\xc4\x66\x64\x31\x9d\x85\xd6\xf4\x94\x2a\x7f"                                                 \
    "\xf4\x52\x77\x74\xb8\xdd\xf1\xea\x77\xc9\x23\x18\x6f\x02\xbd\xc5"                                                 \
    "\xe1\xda\xe0\x84\x40\x37\x6a\x3f\x32\x30\xac\xee\xaa\xbd\xcc\xf0"                                                 \
    "\xb8\x4d\x81\xb4\xc2\x6b\x40\xc0\xe3\xd1\x06\x1b\x1c\xcc\x4d\x0f"                                                 \
    "\x0a\xd0\x7e\x25\x41\x07\x71\x94\x3e\xeb\xc1\xd4\xc9\x94\xf6\x25"                                                 \
    "\x00\x3d\xc0\x2e\xee\x2e\xfa\x36\x63\x64\x08\xde\x3c\x21\xcc\x58"                                                 \
    "\x03\x48\x9f\x14\xc3\x01\x1e\x46\x8f\x28\x50\x84\x3d\xfb\x93\x8b"                                                 \
    "\xca\xf2\x4b\x56\xe6\xd8\xf0\xcb\x03\x6a\x42\x01\xf5\xc0\x98\x23"                                                 \
    "\x77\x9f\x6e\x7c\xe9\x54\xe2\x04\x6e\xa4\xe2\x4e\x91\x46\xed\xaa"                                                 \
    "\xd7\x31\x76\xa1\x7e\x93\x1c\x3b\x88\x2f\xfc\xc3\x3c\x2e\x5c\x62"                                                 \
    "\x87\x5f\xc7\x29\x24\x59\xc2\x08\xd3\x76\xa1\x9e\x67\xd3\xf3\x68"                                                 \
    "\x37\xd8\x71\xfd\xab\xee\xab\xae\xcd\x4b\x50\x42\x58\xd4\x42\xc2"                                                 \
    "\xfe\xae\x7e\x9e\x7b\x87\x22\x65\xfb\x8b\x90\x7a\xb1\x7e\x96\x51"                                                 \
    "\xaa\xf3\xa8\x89\xdf\xd5\x7e\xa5\xb2\x88\x19\xca\x49\xfd\x19\xb2"                                                 \
    "\xc0\x7c\x89\x61\xa9\x09\xd6\x76\xb2\x95\x7a\x1d\xe7\x3d\x27\x63"                                                 \
    "\x15\xde\x09\xeb\xce\x6a\x6a\xdb\x2f\xca\x42\x34\x73\x41\x01\x34"                                                 \
    "\xfa\x74\xa5\xeb\xee\x69\x8e\x1d\x55\x15\xf1\x94\x84\xac\xd2\x16"                                                 \
    "\xdb\xc9\xea\xa9\x14\xf4\x96\x1e\x1a\x8a\xdd\x14\xbd\x8b\x7c\x31"                                                 \
    "\xb5\xa2\xd4\x39\x48\x08\x98\x3a\x79\x41\x61\x33\xc9\x5b\x0a\x19"                                                 \
    "\x86\xd3\x90\x43\xf3\x79\xef\xde\x34\x05\x02\x42\xa1\xe9\x30\x4d"                                                 \
    "\x0e\xf9\x79\x4b\xb0\xa5\x0e\x2d\xc9\x11\xc6\x69\xbe\x8d\xb8\x09"                                                 \
    "\x1d\xc5\xae\x0b\xdd\x79\x77\xf8\x2f\x68\x9a\xd5\x85\x3e\x62\xe3"                                                 \
    "\xfd\x2b\x28\x0f\x99\xec\x83\x2d\x7d\x01\x0c\x15\xe9\xa8\xc5\xaa"                                                 \
    "\x29\xea\xa0\x3c\x62\x9e\x42\xf3\x77\xed\x09\x37\x05\xa2\x46\x5c"                                                 \
    "\xcf\x48\x40\x1b\x16\xcf\xe7\x0f\xc2\x59\x1f\xd3\xdc\xae\x99\x76"                                                 \
    "\xfc\xfb\xf0\x08\xe7\xfc\x83\xe8\xa1\x4c\x94\x58\xae\xf6\x59\x01"                                                 \
    "\x06\xd9\xb6\xd1\xd8\x70\x64\xff\xd0\xb1\x8e\xd5\xdd\x5a\xa1\xb6"                                                 \
    "\x79\xb5\x32\xa8\xf8\xd7\xe0\x95\x2a\x78\x53\x64\x20\x54\xfd\xcd"                                                 \
    "\x81\x73\xf4\x89\x00\x99\x39\x7b\x7e\x97\x39\xe1\x07\x77\x0e\xc8"                                                 \
    "\x95\x91\xc6\x24\x9e\x62\x5b\x7b\x7f\xf5\x29\xcc\xd2\x92\x46\x8a"                                                 \
    "\xce\x4c\x33\xd0\x20\xa7\x69\x15\xff\x4b\x6c\x7b\x09\xb8\xd9\x5c"                                                 \
    "\xa9\x1d\x8e\x0f\xd2\x8d\x5d\x2d\x31\x07\x7a\xaf\x0c\x6e\x3e\x15"                                                 \
    "\xf6\xa9\xf1\x3b\x20\xc3\xb9\x16\xbd\x04\x99\xeb\xcd\x5a\x08\x9f"                                                 \
    "\x02\x4b\x77\xc6\xbf\xc2\x8b\x00\x0e\xd8\xc0\xe0\x41\x4c\xcf\xda"                                                 \
    "\xa2\x77\xbb\x6b\x25\xfe\xf0\xa0\x13\x4e\x88\x87\x14\xc7\xe5\x22"                                                 \
    "\xa0\x18\x6b\x55\xee\xdf\x7f\x14\x73\xee\x4c\x04\x0b\xb3\xe6\xe4"                                                 \
    "\xf3\x00\x2e\x31\x46\x40\x5c\x69\xa2\xd4\x82\xac\xe1\x0a\xc3\x77"                                                 \
    "\x0a\x3a\x17\x72\xdd\xae\x43\x0f\xf7\x86\x1d\xb2\x38\x65\xa2\x64"                                                 \
    "\x75\xaf\x0e\x3a\x28\xed\x60\x32\xe5\x91\x4f\x68\xb8\xbf\x82\xf3"                                                 \
    "\x26\x40\x58\xe3\x92\x9b\x10\xb0\x52\x23\x0f\x76\xb2\x95\x10\xb6"                                                 \
    "\x74\x6d\xbf\x1d\xbf\x4a\xda\xb5\xc9\x06\xa5\xe8\x63\x2a\x66\xec"                                                 \
    "\x14\xb9\xd3\xe6\x93\x3d\x21\x56\x60\xb7\x3a\x75\xf7\xd7\x1e\x2f"                                                 \
    "\x25\x2d\x90\xb4\xf1\x3d\x92\x96\x28\x5c\x5b\x12\x05\xc4\x39\x1b"                                                 \
    "\x0d\x85\x10\x7f\xc0\x5a\x27\x76\x4a\x6f\xf8\x2d\x01\x85\x87\xef"                                                 \
    "\x07\xe1\xb1\x60\x0f\x0e\x06\xee\x7d\xf7\x62\x5a\xca\xa4\x9b\x4a"                                                 \
    "\x1c\x54\xe3\x1c\xfd\x39\x8c\xa4\x0b\x9e\x88\xf6\x3d\xed\xaf\xc2"                                                 \
    "\x86\x11\x88\xdf\x66\x6f\xce\x3e\x5a\xb0\xc5\xd9\xda\x13\x67\x92"                                                 \
    "\xd4\xc6\xd5\x55\xcf\x49\xdd\xb0\xbb\xb8\x2f\xbe\x47\x02\xe7\x58"                                                 \
    "\x9d\xc8\xd1\xe8\x89\x6d\x19\x38\xd3\xf4\x3f\x29\x2d\x61\x3a\xd5"                                                 \
    "\xf4\x22\x01\xfe\x0e\x21\xb2\xf0\x7e\x74\xb2\x33\x60\x3a\xc7\x5c"                                                 \
    "\x91\xeb\x99\x23\xe5\xd8\x9e\xec\xe3\x86\x9d\x1a\x6a\xed\xfa\xfc"                                                 \
    "\x0c\xbb\x33\x7c\x7d\x82\x3c\xde\xe4\xca\x7a\xef\xdc\x11\x13\x66"                                                 \
    "\xc7\x4c\x02\x48\xda\x10\x12\x31\xcf\x03\xa6\xa7\x31\x24\x61\x19"                                                 \
    "\x62\xf7\x3a\xf2\x31\x33\x90\x4b\x27\x56\x2c\x57\xf9\x3a\xd4\x0e"                                                 \
    "\xef\xc2\x0e\x58\x57\xb0\x21\x4f\xb8\xf4\x9d\x97\xa2\x35\xf0\x66"                                                 \
    "\x5a\xb2\xc1\xee\x97\x9b\x4a\x76\xf4\x5a\xa9\x74\x22\xf6\xe3\x26"                                                 \
    "\x44\x2a\xbc\x83\xaa\x31\x3a\x9f\x78\x9c\xba\x06\xba\x30\xb2\xaf"                                                 \
    "\x4a\x39\xa5\x4c\x41\x58\xb0\x75\x47\x3f\x13\xfc\x37\xd8\xc2\x85"                                                 \
    "\x4d\x0c\x7e\x61\xc5\x94\xdd\x1d\x7e\x01\xf0\x6e\x90\xde\xc6\x8d"                                                 \
    "\xa0\x21\xc2\x54\xd3\x53\xa8\x04\x3f\x30\x0d\x24\x27\xfa\x75\x76"                                                 \
    "\x79\x3c\x20\xc1\xa0\x26\xbb\x29\x38\x82\x93\x1f\x91\x30\xff\x42"                                                 \
    "\x1b\xbf\x13\x37\xb1\x05\x08\xe2\x58\x09\xdd\xa6\x4b\xfc\xef\xbd"                                                 \
    "\xdd\x4b\x37\x20\xf0\x69\xf5\xb3\x84\x4f\x81\x90\xe6\x0d\x14\x04"                                                 \
    "\xf8\x5d\x43\x4a\xf2\x8f\x82\x2d\xa3\xd0\x39\xbb\x3c\xd5\x0a\xd0"                                                 \
    "\xd8\xb7\xeb\xd7\x3f\x2e\x9d\x0a\x47\xa5\x5e\x24\xad\xaa\x46\xba"                                                 \
    "\x53\x72\xd2\xa4\xda\xfd\x56\x19\xce\x84\x13\xf2\xe9\xc3\xf1\xe6"                                                 \
    "\x14\x7c\xbd\x17\x93\x30\x59\x86\x5c\x86\x29\x70\x27\xe5\xe1\x13"                                                 \
    "\x86\xa6\xd2\x1e\xa5\xb9\xee\xcc\xe8\x2a\x3c\xf2\xb4\x7c\xf4\xdf"                                                 \
    "\xee\xb8\x9f\xbf\x8d\x8c\x21\xeb\x9b\x57\x23\x2f\xc7\xce\xea\x0e"                                                 \
    "\x55\x35\x94\xae\xf8\x43\xe0\x54\xec\xb3\x88\x02\xd5\xaa\x8d\x37"                                                 \
    "\xad\x15\x52\x96\xc9\xaa\x25\xd6\x36\x55\x46\x37\x9a\xcc\xa8\xbd"
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision-v2/000077500000000000000000000000001521103432300253705ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision-v2/cmd.json000066400000000000000000000002761521103432300270330ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberDouble": "123.456"
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision-v2/encrypted-field-map.json000066400000000000000000000013251521103432300321150ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "double",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               },
               "min": 0.0,
               "max": 200.0,
               "precision": 2
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision-v2/encrypted-payload.json000066400000000000000000000111541521103432300317110ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "C9YIAAAFZAAgAAAAAKGC7e7Khb0eNdd0PGMhz88wjaRo8hhtloqXiO3RlPPgBXMAIAAAAACO5kHsnT2RtRzNVr/dZqqJBOql9vFQQt8tx7XFgkbdGQVwADEAAAAA2haLMsXrFz1qArgnBCTTuGS4HYPxt4n9AhD0tPqb+SA7LEva9ufIwi9D6B653QCrvgV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAEAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBJQjYD5+8l/QKJ2xtpFmPwTaO3cbT10hGSsG3avRpR1nAhKYs+DGSKoj9SonSc4JSvVmUqkPauQ/9ncmPCI7UHFBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAA2ybUFdeMB+ujbbvEqABQti0mVh2gidMf/lbSMxCnqIISawAAAAAAAAAAAARnAD8HAAADMAC2AAAABWQAIAAAAACCTEu6TTXfvs9twJDgH6+xdukb+sStdrChBLo/nWA30gVzACAAAAAAtQ3t4esWC7DbkQOgsTYVr3hge5CtGicFUks/MM4vL7QFbAAgAAAAACbBD5/yFo+aOMivjfGplV/Sl57BC2RtidXOlmtgHwROBXAAMQAAAADWNigqgKPd+KEVCsxy5Y8aW66/VRLwkIAbiVzOgDQ2lo7p0NrWzapCUknDe/1QJ7VZAAMxALYAAAAFZAAgAAAAANk91642dYB0wQK5m6VTkXhkq4lZzIzAzj9NqK7EljaMBXMAIAAAAACkGWqEutN6uJYX2tsss5twvEf64+vOm8LXhJDR7XMxowVsACAAAAAAMGDrqys8iJ3fCT2Cj+zXIuXtsf4OAXWJl5HoPUMlbNoFcAAxAAAAAOqBXyyyvE4JqqzgTx1dkcHICc2NsV67dEIW0ulpyw8HXZja2YOJbNpkKUd98HooMCUAAzIAtgAAAAVkACAAAAAA3uYLtxjo1etEc5MJ0fF6FaR8CqVtO6o2Tzlc/EqHUeMFcwAgAAAAACBU2rXaSwz9yZu1PeD+BpuqjizHDyT8G4sQBZUnKFxeBWwAIAAAAAC0+fcRgZMiid1CmuZgqc91xJ+mU8dIyy2/+4HJPNbEoQVwADEAAAAAXYSBYMoPApxmpqGA54u1yj+8b8A58vviYOujO60pwuZXGcfN5xHBo8pHZAIwYfG3fAADMwC2AAAABWQAIAAAAAAZ6kS+6LBFKXZKhFEXeYWqDS7nPCgPs0jNKODRaq2M4wVzACAAAAAAy4S9uTqZJm+oah6eizOVBPYXBhyBRlleh7q4ZofwuHUFbAAgAAAAAOgl/lqbUkJCdW1b5ArQiWSO+stq7xAENAsD/SBJd1svBXAAMQAAAADdHP2a1io+A/chkp2sRwF8KZjfeWu3Tb83NfH4IFLfhAq9cjRZ8LqnGnQ9E93UCZudAAM0ALYAAAAFZAAgAAAAAESrZ23i6fpJHqg+L8sc2b92AWz7eknvuK28gRPkNDChBXMAIAAAAAD0sxeWkLvt7yeUp3t2dqDJhSQ/Vi08HNlgs4tvpbd+DAVsACAAAAAAPP+wWZPSJIaqTs7kytoS66wO5+0Xb7pkjLZMMOfLt+0FcAAxAAAAADxe1Jb9EwYR/lphz0I72QX2MR5uVmKIZyPjvuYcKnahXSHBu2aFwYKR3TLsJ+wBsl8AAzUAtgAAAAVkACAAAAAAQXIPscm4wHxEay2glEaoZhYS8hVAaixigvTGTt/4A0wFcwAgAAAAADyPkP0b6lgXEu0wsx8EZnaAhpT+2zcm/O01eWxSv0iXBWwAIAAAAABSg8CZPZ+6d04xOsu5j4KPMlF1c3QI2Rc55GGboazyNwVwADEAAAAA6jeZEl/ojIYz75xtpcJRKqO2hzSz1YF2uRi/BZ5wESj2jzRT1NQOrOOAYedlIYKzdwADNgC2AAAABWQAIAAAAADQBLJ1L/dZvwIaqalrEZpXLs09art9ITcAWt94TYNK0QVzACAAAAAAIrBi8HKX7gEjNmN/Uq9vC90EWnPJVq0/KhgYFu7WH6oFbAAgAAAAAC04/aY75Yt5N0tBkJyVSZ4AYGxMClGMErG4wf2f2tbWBXAAMQAAAADaVoNTYgF2lhg7MJeq08gZiIeLWQ/5PDO2+S2xlp/Rf2DqvvLzOLXBR6Hc491STPaWAAM3ALYAAAAFZAAgAAAAACfOptT81z28/ogO3ldBDvRJfrp1UOKGrXHbu3E7SC5DBXMAIAAAAACHO0rm2+uq0kbILJ5Wsh2nng12wb+ajvTsxe/zOFI3uwVsACAAAAAAKHN9iXrNVBXVQS4dk9IxaaGFbjbEj2NuS31krRgXxmgFcAAxAAAAAPj/lDFjnTNIVhXVBvMDG3Sfk6Zy/4gX/DPyKNxkWrzk9ShUwcAdRTvNpA17F0TPEVgAAzgAtgAAAAVkACAAAAAAsg2W884aVdqaiDCIFZR0GYDU7Bpa5UDTxGTPbVa5/X4FcwAgAAAAAHuO7J9X6WylXxVrmepRQSDWU0aDPUBG0wWDnT6sR6h+BWwAIAAAAAAjAnZcNon7oV1P5+QdiwiJRUnsxYTXLtfBrlmlcXJodwVwADEAAAAA+2FJY248ycTnEZ9YiC+te8NlRbhUbmoTJmYAyDwQ+WwDAuR3zyJNHidwojV7UaExZQADOQC2AAAABWQAIAAAAAC7cb9sJzT/6ZtXJbgBI+4PQF7nu0y84fdbRku4X6gtrQVzACAAAAAAU/9+6ZI3g1LQmyE82/FQInKd7YU2XLWK39m48FE8jfMFbAAgAAAAAOTD1HhJ8RrwRF1w3su434I4PV3hSyX7cuZqNLnfswEHBXAAMQAAAADB6voEKAgf0uyivHp9x7H8ukBL5AScHoRJXt8hKdWJtD6VhCq3fiek0kYhphc+Lge4AAASc3AAAQAAAAAAAAAQcG4AAgAAABB0ZgAGAAAAAW1uAAAAAAAAAAAAAW14AAAAAAAAAGlAAA==",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "double",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            },
                            "min": {
                                "$numberDouble": "0.0"
                            },
                            "max": {
                                "$numberDouble": "200.0"
                            },
                            "precision": {
                                "$numberInt": "2"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision-v2/mongocryptd-reply.json000066400000000000000000000036321521103432300317650ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A5gAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAOQAAAAF2AHe+nxov3V5AAW1pbgAAAAAAAAAAAAFtYXgAAAAAAAAAaUAQcHJlY2lzaW9uAAIAAAAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "double",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                },
                                "min": 0.0,
                                "max": 200.0,
                                "precision": 2
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision/000077500000000000000000000000001521103432300250435ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/double-precision/RNG_DATA.h000066400000000000000000000044421521103432300264370ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xda\x16\x8b\x32\xc5\xeb\x17\x3d\x6a\x02\xb8\x27\x04\x24\xd3\xb8"                                                 \
    "\x50\x8d\x80\xf9\xfb\xc9\x7f\x40\xa2\x76\xc6\xda\x45\x98\xfc\x13"                                                 \
    "\xd6\x36\x28\x2a\x80\xa3\xdd\xf8\xa1\x15\x0a\xcc\x72\xe5\x8f\x1a"                                                 \
    "\xea\x81\x5f\x2c\xb2\xbc\x4e\x09\xaa\xac\xe0\x4f\x1d\x5d\x91\xc1"                                                 \
    "\x5d\x84\x81\x60\xca\x0f\x02\x9c\x66\xa6\xa1\x80\xe7\x8b\xb5\xca"                                                 \
    "\xdd\x1c\xfd\x9a\xd6\x2a\x3e\x03\xf7\x21\x92\x9d\xac\x47\x01\x7c"                                                 \
    "\x3c\x5e\xd4\x96\xfd\x13\x06\x11\xfe\x5a\x61\xcf\x42\x3b\xd9\x05"                                                 \
    "\xea\x37\x99\x12\x5f\xe8\x8c\x86\x33\xef\x9c\x6d\xa5\xc2\x51\x2a"                                                 \
    "\xda\x56\x83\x53\x62\x01\x76\x96\x18\x3b\x30\x97\xaa\xd3\xc8\x19"                                                 \
    "\xf8\xff\x94\x31\x63\x9d\x33\x48\x56\x15\xd5\x06\xf3\x03\x1b\x74"                                                 \
    "\xfb\x61\x49\x63\x6e\x3c\xc9\xc4\xe7\x11\x9f\x58\x88\x2f\xad\x7b"                                                 \
    "\xc1\xea\xfa\x04\x28\x08\x1f\xd2\xec\xa2\xbc\x7a\x7d\xc7\xb1\xfc"                                                 \
    "\xe5\x21\xb4\xe7\x9c\x27\xc7\x77\x62\x53\x09\x2a\xc3\x9d\x51\x5c"                                                 \
    "\xac\x7d\xa9\xca\x52\x73\xff\x75\x12\xe3\xf9\x94\x18\xef\xdb\x3b"                                                 \
    "\xec\x2f\xa9\xcd\xf1\x8b\x80\x61\xaa\xbd\x96\x72\xc6\x3b\xbd\x69"                                                 \
    "\x1a\x75\x61\x8f\xa4\xe1\xda\x32\xa5\xad\x6c\x8f\x16\x05\x3d\xd8"                                                 \
    "\x00\x95\xc6\x6c\xfe\x30\x75\xc5\x53\x76\xf7\x28\xbf\xcc\x7d\x30"                                                 \
    "\xec\x86\xfd\x48\xa5\x15\xb5\x02\x3b\xef\xd9\x32\x01\xa8\x5c\x2a"
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-v2/000077500000000000000000000000001521103432300233775ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/double-v2/cmd.json000066400000000000000000000002761521103432300250420ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberDouble": "123.456"
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-v2/encrypted-field-map.json000066400000000000000000000011761521103432300301300ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "double",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-v2/encrypted-payload.json000066400000000000000000000401441521103432300277210ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "C2gsAAAFZAAgAAAAAKGC7e7Khb0eNdd0PGMhz88wjaRo8hhtloqXiO3RlPPgBXMAIAAAAACO5kHsnT2RtRzNVr/dZqqJBOql9vFQQt8tx7XFgkbdGQVwADEAAAAAwaEDToDFIvFrDKk5L9poiJXPlD4QXpN68+y77tThpVJ9PKXXTDkD0vC/+VWybYVmcwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAEAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBLABSYj+6eFHviVTCP+EyZyx/RLh8aQtjqbjI5FChknWKzX3kerRqR1JQfjhR4YhAEGU/BONPyHjT9cZ/4Oc85NBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAA2ybUFdeMB+ujbbvEqABQti0mVh2gidMf/lbSMxCnqIISawAAAAAAAAAAAARnANkqAAADMAC2AAAABWQAIAAAAACft0Ct7p0d2AiXZFc1xs3H2sG/z5OYtPQYrgXt27Q8tQVzACAAAAAAJR/O8D1VkB1R7Uow+HrTfkOTc+5koZOCzImvSl7FZlUFbAAgAAAAAEgMw1JNsiPUWtdfY1TaBRwe5j8Lv9KKK2nNwrr4nD2LBXAAMQAAAABnoxnhtywk7zagFBbIV/Dudd6i1EGWkSuW4dKRPSwlKpWOoQ1/pvFGfPDRUBc7VzEsAAMxALYAAAAFZAAgAAAAAOh1vj4LUxJiIlkf1EpaOgAi6JJSc5q11Zvz+ZLYsZ+3BXMAIAAAAABxwqZ/jiglaW24HIUAWC4WLtOCHQ6XmMrFPsVvYWmXyQVsACAAAAAA2+FaAbl8JZogfNCI0FFbmZZPy/KLF1u16FGrPspSbEIFcAAxAAAAAMnuHKcljgfhRyVyAzJweppN2CSzJV6A4gXhiI4Bf+AWqae8q2qcUeDBhM0ZIHt10pEAAzIAtgAAAAVkACAAAAAAJFUjWhc4GZjtdGYZH1GdxK8pv+MaRb0Yk9Tu4B6RRwoFcwAgAAAAAEvWzEZ/t73uP1iM0HofkXVIpmjyR4yDZxbGGuuCPZi9BWwAIAAAAABKzb4SCGGUyL2JzcXeIkmlqiy3NPEFnwUHVlib/A63vgVwADEAAAAAHJ2cPwy94xP/J7rdqowIpKyTQdt6AKWFsjPo3KPqeZKreUpEQOwJpBnoG/A8KdyjEAADMwC2AAAABWQAIAAAAAD8wiPmMZE6vE3laX7gKlWvjd8NXhGbnikR9BDWJqxcMAVzACAAAAAAQX50GO+DqpBcvqMEl9foEPGTf93v+v4roRDImm24j58FbAAgAAAAAH8D9aNK/8LdK6NechstQcXIqz9wyXGdrlXUPR083u5MBXAAMQAAAABrL61dzl/Kr3XD2f/kXDa5ndJZJ0phZwG9DpIaLyNy5GMhPkZ2+8djIBTDKUdCznnkAAM0ALYAAAAFZAAgAAAAAHWnpOFpeAiW2BEt06bzPQ8wAh7KpkVkA8gQXohRAgM3BXMAIAAAAAChKZF0FP7IKjtLMZvQmnLuOk45XsBMb3Czvn3AAKmedQVsACAAAAAAQsiZcRrjk4AmBUiAWzDcGTwaQ2vXfgtFPHcLFD1of2UFcAAxAAAAAALZyW7uQ0koTnaU6Vq5RrW9+98kYJvwfv/mRCmAVujMB26iPiueJJJksrmLqSVVAg0AAzUAtgAAAAVkACAAAAAAMUMaOvVryMgIRIyXxsnm2izPXUxjcxgVbPL2n5YVx6IFcwAgAAAAADCF9Cn4wuKAwno2sRd3Wc1StVQ/S2J1lgxmKua1wLgABWwAIAAAAACLW9g1z9QR0Pb4JciaOVh/IwwXXKmMya3zfN2LNpbk9QVwADEAAAAAfOK3Zx/8AsbdyYImANHWZ/ntg3tk1S10Ls2looLgY7S9hyPza6Q8ncdIgEYCIFmSQAADNgC2AAAABWQAIAAAAADvLphVYEowRTGCsbZKyjZexkPaY6thwrQuBiN95wqftwVzACAAAAAAN20plHtuSHtBkF9DoQrOCX/aJop5XDy4EGmAxnqvUmUFbAAgAAAAAJIg8ym3GP7hjj/xP6eKbx+/0rKFSkC4jNeguAnxkqFJBXAAMQAAAADhiVQxJJyxP9DZ3uRj2Z7VjBjzfwRU4gXDNO7Xs5GYq0r+4kBD8tIij581/QVMRQiSAAM3ALYAAAAFZAAgAAAAAATqicDXukUTs23e14xzNRAHBlO2n8/GGKV/WNpbsJHfBXMAIAAAAABpQea7gq6rparOWr7J/0L5dNkkXvLZ2NTlxRsHYwhE1gVsACAAAAAAq55jDvPqq+WJKJePk6qQ3fXeWgO7PB70tQKdrkfNI6AFcAAxAAAAAFq3ICYwq0i9rCNflUuWF0/9xDeIb4LLQnJrUJ4mOFUnWvljOjZKVv3+AnjFwgwq+T8AAzgAtgAAAAVkACAAAAAAybRXgbQ2H2gtK19nqqwApxmQZNMh6L9GqUOv7s9UL3cFcwAgAAAAAMiun7gwdxtWZrdpzOaTVATHXbXjVZ3LKM4cFLjZchP0BWwAIAAAAAD87dp+dGY8ClK/LId55BVy8v9GPObMo/I8jjlK8Ip3tAVwADEAAAAA1ZdN3CTXRthzbZU/ClHbYGvd2r/hdYT4xIXTUvmFKS4wsi3M+4QOTa5xxg1CZwCQngADOQC2AAAABWQAIAAAAADOoSGpX5zlJ7Wc4B7Tw5YSSBk3EUhaoe0SWJ4D90gXlAVzACAAAAAAC70WdZjKYqtwStRU30BVDkRcOdmX5L9pKUykB3+ZfWgFbAAgAAAAAEXqasg8ahA7XD4tiZRPC9t5LqftW/t7IHQiRBS/KrXaBXAAMQAAAADnYPxoW3iOAhmFRJC35WCkWV4uGwELG2Y5S9BfGSyTIAOfuRncVd2J9+6/o/3Ciu5GAAMxMAC2AAAABWQAIAAAAAAiyZuSuu2E6K+UnXQKtpQkk2aM5v0Yq2J2hjlNnygtXgVzACAAAAAAX4t2jaZ8EnzdpiARpP0nj2Co+4C0Hop2VQnVKuUMYV0FbAAgAAAAAJlRDonwmpyhZCENjik3pJ3GpfOcJNR4IKsjUWYXGdbjBXAAMQAAAACKkwYTyx09ZroBJksMTc9pfDrUlMqpKq8zsMV/eGdpNFHe8que4mfLpHpxHGcybI/GAAMxMQC2AAAABWQAIAAAAAA9FyOTumgu3RkgzhshigJD83vYX8XC/0EJPAatleP/DwVzACAAAAAAK1tLjYq3CzckODIZgIGzSA9U43oLg0bI25scdQmL5/UFbAAgAAAAALi1+1R6lo40+ZLh0Q7TNw02NMIHKFl/zjZqWOklTj57BXAAMQAAAAB63rkuQNE7c3xqnj4fpf8gZnGjV1CFYcg+JUYxhCurox5MNRgoNmyRyFM9vQvsMhFMAAMxMgC2AAAABWQAIAAAAABfWzEmQOxZX/+aX3U94bHKPB1He+RxgqaMuZETRiV/+wVzACAAAAAAXaKjcwXSaSFRGJF3Qjp5lH6jIWnVKHIVyYxUtlECc9QFbAAgAAAAAGPuTgl4wtGBadkSU8YTJiDoijCN9hOdIf6aW+G1YLiPBXAAMQAAAABOlNAdfJ4ivqKxtaL/9Ufrg4S1mviWJPbOw5x0fDej+fItAv/e739buEYOJS0+uOO2AAMxMwC2AAAABWQAIAAAAABnrG5k7VV/caeBiIs2jRL1lkKl4JpF8e/YidREqxzFfQVzACAAAAAAIYIJ/PjhAns+Hu4ZjGA5O8R8W5AyAMny1z3j9Oh8NREFbAAgAAAAAGy57hRbjZ4dcsWaAzUG/9BJFzJNfwURBIAmY35TQO6UBXAAMQAAAABZ58ZKZw8OfO+ab0lj5WQvXD8/Q1ba+t/nSan5808O0ZRqiAJgd85V6B3ooHfpIpiPAAMxNAC2AAAABWQAIAAAAAD6f3YBcAsS7KUUgwEYKMqpbzlPJwHiXj0t5O3i9pmg1wVzACAAAAAAw4PVDxk3yfR6MhiCaEs7eWKMtug5LHTIGmzAvPiTlFIFbAAgAAAAAGOz99qNc2arAxKiIxjGtNNkHAUF1Q1xlUfS9ErzTuWbBXAAMQAAAADMc2PdNigDTJt+0jCDzguxOHCWQGFQoWSX8ATCsKAc0XhypR0bcmJ0dt7AqyARybgaAAMxNQC2AAAABWQAIAAAAABnn/RUnwnNfcWlWnxXPKY2E/FdzZ5ZRCgv8LwxLxpQqQVzACAAAAAAn/URi1Foja7My0XWNQxy7HOF00sCpLCbzMnDMvIWuoUFbAAgAAAAANKebpRbQfwsgbqWBkKiXK9mIBQD75FLprPyocBxLEotBXAAMQAAAAAgMIO/DCFEPcNzQklXR8z8ND1NT/orlD2mDy6xbFXIEPQj5E/OQ5jty2G6JDPDGPD+AAMxNgC2AAAABWQAIAAAAADAcBN3nRPSw2/77njGaQyDC44OAMnxI4rwEj2VXfCnbQVzACAAAAAAgzG8aGEgq1fQPS+X5bDSiHx3XwHQu6w5EY4RHT93QeIFbAAgAAAAABwnmSmvN6DzEVrArPXNq1GPrnK16HLsM0KkGs3FzHvTBXAAMQAAAACXqRqL0sIt6b8U4poFwyfp90ufmP+NckUJmxyZIlkoAySUft+IOghASRRgR7IdcsfEAAMxNwC2AAAABWQAIAAAAAAQt9sKaYXCm5DyxDctaexa+07dtvJfQ3fAZJ0Ywc32jQVzACAAAAAAibWGcvS3QrKeZu7OvNYZNZmGYFC3EhgbYJZAMjZqWNAFbAAgAAAAADl8eL8YijDL25DyKDl2DG02O2Kf/BmdPgkKgjNfcMjgBXAAMQAAAACkHVVP6niSgduenn0UEtNIFB1Y4w0NReXEcKARz7qtSm/avrKlkZEQ6A34IwXAbIIbAAMxOAC2AAAABWQAIAAAAAC1fwzGIsoXmnEFN4a+kvgh4d1k5ZVDWQHLJ4U5Bp6y9QVzACAAAAAAXai5aSBYcpHjj5sDel92AeyOLsDiWhloiS9sU9jWUYcFbAAgAAAAAEMT+F7TmIJ75KvQ0+3565l7E2TvwNvbJChFzciK+Mm4BXAAMQAAAACT7N3fTsN4NSB63qA67tWKDT2a2qoKvCn5lOlfUnnRnenMA4fgTXOv2t5+JU3jPE2IAAMxOQC2AAAABWQAIAAAAACCRWe0lz7hmCCa7aVQVUmxeF4KoS2aalC4g5J2gIbUewVzACAAAAAAXaDOXcbiujpwQWq7WPK5Wyh4F2VXkdPE3kmaoNpTyNsFbAAgAAAAAHgVSRLDYD/BndsCcGyDBTPxf+oAnTJwVEmqDVlrRThgBXAAMQAAAADvCzl9j/1DUOJCDXbYuUIKCQqh5czNn9tVmAHUXaPbskBlxrJjA/sq1WuhDxKpw90sAAMyMAC2AAAABWQAIAAAAAA4SLZKWmCVFvfRh9SkPkh1+c/uy5xrpiSoMejpEFsSswVzACAAAAAALqHr8e2EjJAyBsCBTOOzFMqT2DOgH1oJf4UWSzY3dwcFbAAgAAAAAOSUyv8griWIe7simqf50GUG9Ka9zDBAuA3nKdg62/73BXAAMQAAAAA9/r1ReJeIuC/5Mk0PcFjwNX3SbF56zVDWAfS0E3jKBUVTbeMEFBJSkSPuaueakbCtAAMyMQC2AAAABWQAIAAAAAArdfWpMgm6SypDo7WYBg5mdI/yz9KWb8oM212iA6/2NwVzACAAAAAAImWFAmBhae2DKznZPUJxjTWQpbkjN0cCkgOn704EiVIFbAAgAAAAAJynZHeNKqUmOGQAJ+r7woCbKxnZFrH57XZRRqWcPf0NBXAAMQAAAAD536vAWCiej3w0RUzBxRgI+UqS+fSEBZQSypvwmp5B0GS5XnGINqETCGto7WJNxEDmAAMyMgC2AAAABWQAIAAAAACay4jcSSevnHaXvFgGPUnEZv5/sfbtVpNyzDTQ+iy9EQVzACAAAAAAsv3aeQutsQ+2mAO8wPZ3zTfEjqRb9YllSj7yChaPFqUFbAAgAAAAAKDcUDPFuzuuurroi13xasZGEF4fOU3ZJ/mHFTzJV0TLBXAAMQAAAADJZvH+6vtQaryFGRB5ZHb2Of0X6kCIlZTNiuoCC+gNv4IZJ4M5NdP6xeIAq5VFFSMlAAMyMwC2AAAABWQAIAAAAADwtZp3zYY+/8ZSoS7H+s+yZg0fePlamjbZzIAS201dLgVzACAAAAAA/I5abFnF4d7vKatjoLRhl82iGc1CfVUYt0fjs9NREhcFbAAgAAAAAKUP8QV7RGSQCRKIlEVESin8eQ4MTsZYmTJwTH97onVnBXAAMQAAAAD6W6TnO8w5NX0s2F+P8qGTOWahm092J49bGP7GPaVk4847PcmEHnC7l8+hXEY8IueLAAMyNAC2AAAABWQAIAAAAABNJ6p/FzgC4+PfhNgB1ZSelRfr7LjANm4uVYN/YBmhGQVzACAAAAAAdrkwAMdkUX0nxlN4G8EcNzBEGR+YxxEb16awNem4qPMFbAAgAAAAAC+nkWgov3YxS07Try6PhW0ad6lStrmHha9I5GALbAM9BXAAMQAAAAAVNIfGAaegAJSMAjElXOmX85yOCnVcxLxMpFAW5UpQsYF2lO/YhoOijblw2fqL7pL8AAMyNQC2AAAABWQAIAAAAAAxLRkzBAr9sqlZFVc7fcqiOBZM/SJ2Pk712W0KtiFXbwVzACAAAAAA86F/FCYD2pywJLXDnJP6OzC/tyu7Tyxq8g8UN+uHOEMFbAAgAAAAACaIBISU03FRNOEFmutoabsoK839UNVHwjl+wvAGc5oXBXAAMQAAAACePwxy5g9/IeC5FyIzumkMmdZXL0yh6rpb8YKh+nKR1rQ9LP5LJlXVnrcqrmr4juYrAAMyNgC2AAAABWQAIAAAAAC7XSWz7nm+NFQuBA7x6xWyobk8kPH7pI69GRt1V4A0bQVzACAAAAAABdMveWPZ8WZT1yGwd2z/Yx+U+DwxmNTsC0deNQlWmRIFbAAgAAAAACd0npQbW5WzmXHZUfQvfYWYdM8bEL6NdcEX4ztDp0MkBXAAMQAAAACfYbiNay+Gpy+/e+yCjlg0RFBywnQ9Yxy1Q8PVi5RucCgI+FG+J2nwCrKw+y2XTW0WAAMyNwC2AAAABWQAIAAAAABHKN4WLYNudd3K9UwH33ipum8dQUKzty4W3jBggyRtYgVzACAAAAAAulVEC1MEHe8gdx1pFU7TZxDFBrEFZSVqqYX3ZeGMBxAFbAAgAAAAAIyX+YxBnZHOS23qQHa+48x7ZsEkLsMVAXd/EKPAbq3YBXAAMQAAAACyheKv8tSlerpCF7XI/PRJqVULOyZ1VBuVO8Y9LgSo19lCjSp+/otZV0Vx+U9QTKLDAAMyOAC2AAAABWQAIAAAAAAEFGGlo8uxeNdkGJh1M8j/B9ZU3qzLZGysAHlaUgjPYAVzACAAAAAAZ2u4JlCTvaajzXBOJ6x1R4Ur+apurhRJOXs9IWftm9sFbAAgAAAAANQgbUOLIo77svEdrkW+zUBmRE6T49sgK0mL/Vay67dkBXAAMQAAAABl8MTVveY9upE2WoQIeX7IVXB1ReLNPKWcZaDn6eUFf9owGXynUCQ+4M4SouFuaiUtAAMyOQC2AAAABWQAIAAAAAAJb1EYgtU5RJwp1FEVQC3jwucATB6sY5HA8HcxgfZSxgVzACAAAAAAWD2jWpiMzS/WmAbNA5tu/esBFnR8YOigzKxk3oYR47MFbAAgAAAAAKigscMJIfYqAMsJr/VaHCLku+nqyfKf1WM+aOsQ+8KVBXAAMQAAAADgdKI0GqbaGoRot6JmQNgfZzYThH2PDYiVoURyQxRcWTsz8Yt7UrjJqCLCMJE50EgsAAMzMAC2AAAABWQAIAAAAAAHM4JqrcItNeN2gAwMMFzZ/A5rAxD9AoOw0DoLT2XjpwVzACAAAAAAPtuyTly5f5x5ErZeQTMjciPx3rWNKnHSY+dA9rTErRoFbAAgAAAAAA+JgsYR4QxXA9wXrr6WXdw4cmfJy6U4prjnSiR2SooaBXAAMQAAAABIgUeq4UcFrITeFVvfLmvdv2olXDfthz+nOzMcX5m9cmoruAsIwMZqRKgUTRTRWVS/AAMzMQC2AAAABWQAIAAAAACoa1IeOxOIX1E4TKmdl4bAAm7WVktFBwnjZbg1uT3E0AVzACAAAAAAtneQomUUrnsa3e1FyQWdmh7Ly4LuZ4aNJuK2bw/9ResFbAAgAAAAAJSjzQ3vTh2fPlQGrBIYd2bwhxZIlgxspDkMqjPqAT0KBXAAMQAAAABpr57K7ZmvfQSzjcz0pXpvUFnCBUaJgFz4uT5jOqvEm0QJ7QRDtASYmjsZ/UYuDBTIAAMzMgC2AAAABWQAIAAAAACLNqfx4NDEdyaOrvP5RYSd7SK1o72CsDZ1ju2SER23cwVzACAAAAAAIe8IgfZxuZmC6l2GvVX2EkNj8vhzvXXBSpL2zNPyJ0wFbAAgAAAAAIbS+tRZTOnY4YDCSF7q7qht4dWEJPsqY1iYKQTluO4QBXAAMQAAAAB7N3JTwuC9CGOzoxKEJVUcYqNBmBAq7An4IOxMKukr3Qud028dvTxVPTqRW7adCWBEAAMzMwC2AAAABWQAIAAAAACnkG+1tDv0mxgKF11PNnnbY3ES4pb1kQDMzWFm84uXWgVzACAAAAAAme2UfmOga5gokVOU8jQWmmtYq6BtOl/uqcTajlmo+cYFbAAgAAAAANp7bQZY5sz0vQgBBs6HuR0A41r1hfeSL1jMkaSNSgfiBXAAMQAAAAA3e3dxbBknJwOXiaqTdfkGzeDSQlrXnA9EknSdgyPHn4K+9VcC/Hp4W/oAkMfWGcz5AAMzNAC2AAAABWQAIAAAAADUrB0QN+wxXhcqupnkiuRY5x9peq7WryozkzyfU1WuXwVzACAAAAAAHXs0X3y/BjRnUH5br8IAk4VjhnVOIx2wbmoBJ9XimmMFbAAgAAAAAA7ePVmfiOYxi+h98yAgVSQeRCY86L3IPJhkrxYgCK4uBXAAMQAAAABpGiJ9NWY653KRI/s5AK6pgc/nZ/xpPuhPyGJ9ES7C177Cbe6RraTVbBEQhRuMbwo7AAMzNQC2AAAABWQAIAAAAAC89ib2XLPQvTvSV96dvMQ/+P6cNg9JI3bz5GybvMoMWQVzACAAAAAAzppVYEzsloxQ+Oy9nnZkme0B3i9ZAfOlFGiItWn0U2EFbAAgAAAAAFlHfkMkTbo1kPADqGCh/zBepuxgoJdsXDo/FZ+eKQh/BXAAMQAAAAAI564qt8JN1nIV7nDRr09N5lC6jw9xFgmJkXsTJkfRPBcest+59FfDQW5XDftLtKqdAAMzNgC2AAAABWQAIAAAAAD3/4OJLA/hGgSmLMZC0Vzm/kPU4aGg9yuOB4PACgzN+QVzACAAAAAA1keGfZcScISM0pi22qYdqNfG88QbYMDAXgMpKmwUBpsFbAAgAAAAAPl4p2ihsHIwPCCaSeB5E3c+PpCxklxCEBWMr6ZwbTpWBXAAMQAAAAAi5sh510GyKFqiPg29M6x9ezDheUA2qDO01/XdZbmT2bgv48C+cPrpEllJ8EHXB8YaAAMzNwC2AAAABWQAIAAAAABEXVHib/9HjrechHci9wNa+t8oVU1j2Q77Vf67Yhip6gVzACAAAAAAn2ze0q/ca7kJ+lUKtcrGXTw+Za1kF3JqakBUhS+OosIFbAAgAAAAAPXBSHi3rTwho5S5ZvzrknF0Wen/+odPK2PBUaiqAnuhBXAAMQAAAABQy5a1wknFfdM2fNpqxFT6Qn63y/3O5lLTDmwR8R4LUVroxP1GwZHyR3ccZfzIGZA6AAMzOAC2AAAABWQAIAAAAACZxbsChjUZTMgnzhMklgl8S5bViCJ4/+atHE6ILjcZYgVzACAAAAAAnf7xU6+NfbooANg/OmfpHnGBd07Kcvgs21h8D+DTK7IFbAAgAAAAAOh6mXUPXnElV5ro5YdCx5UbXuE2tnbZUq2GDgpdMNBlBXAAMQAAAAA/fKTe/mAsicR55DHdt6F2005LWFKCQnHjBhH456SXJ+TwBx+EbsqU48PdfqpwKHtpAAMzOQC2AAAABWQAIAAAAADA56eD2uZTqXGeYiM4mDXiV1hXQ4Qd0zlzqIamlGTc+AVzACAAAAAAALZLV4nwTQ37aIux2oeEvXvAHfVmhHZZQbsUF99sFk0FbAAgAAAAAJTbTFvW366kTmCPfSyqxv8UBPzJ3UEhTkMm/+9QF2nOBXAAMQAAAAAxQ+G7uFZ4Hi2s5/80j8Y2lmJHu5pKNjipUR2TKe6sv8eLSGk8XWYIHsLREFqshCSsAAM0MAC2AAAABWQAIAAAAACVnmPXAlf10mh6KCvIrlywZvBQJ3GKI556G7CIGRd+KgVzACAAAAAAFMrUpp0M7NAwOkDkTL3kw1BMh13Jxpvx7BU+RQtGCGYFbAAgAAAAAH3K0V5Fo/cxbyCeApSEegQ6cj+RiXa9G0lY9yBXFcv3BXAAMQAAAAAsb0NlL3/R49GJ9ekr1FjXJEpPPzo1so0I8kKoJJtxpg4p3GDLOpaLO/SB/yaAoyT5AAM0MQC2AAAABWQAIAAAAACs2S/v3rQYOyzmVyc0nGJIfb8h0aTVIajn5tgPGSuSVQVzACAAAAAApQnSvZxdJe77IoPX6kvab5cK4xidJNJ4KUBhfqwvG20FbAAgAAAAAGKAaUUYuGzJR8Y+6bdA0OmuOpPpzeFHj3eDNACo8MkTBXAAMQAAAACvG7T77GHw849TnKoxaRd9Gua6bPA37/EaCdbHyMo7tgAJES6bU0sh/U4GTGwehw3wAAM0MgC2AAAABWQAIAAAAAD56IQlX7UhCleBWHQSp/JA/edcxoTg0sWdHbbks4JdBQVzACAAAAAAyINDxywXgCkX62DnU029TTcYWT4LQ7Y4GOaRHAcpNzEFbAAgAAAAAAZh0EGehyQNsAwx1UyEbTdRjf/y4HDL3Q1w+18BdeIfBXAAMQAAAADKbhu6DWBDTTVLI4hKUSl+fA9ilRGVW+/hxJUcCxGKvCimQZi9WE0bu0ePbJhyg6xBAAM0MwC2AAAABWQAIAAAAADUMJR09ziZujjdEPDd5fhfOLtF7NfxPefXW2ZnuOmJKAVzACAAAAAAx5nM1KXGDCnivhUNwShNC2GYMN/Rho/86Uth+nfXNiQFbAAgAAAAADW5ys1ONRxT+ydSwLzSK4VACbVyJ9OWzMeboMYKbqePBXAAMQAAAAD9tuDUy5SL02UiB5s56kb/5K+hr7KluAZ42+ss5mwmDU9Ygd+gxKF8WjIJpOmBzRMBAAM0NAC2AAAABWQAIAAAAACTP+FRBr3zDN8IZQpqDHCznfhxxs3bbE6uAJlgRHHdWgVzACAAAAAAvdXLVzSYwnpHd5pe9JBZfLQMMS1sWI84UZj0LOwZi0MFbAAgAAAAAFEx9mHbnFu21KNK8RQlk6fxcGnhJ2KCwv7WzZmAXQS8BXAAMQAAAABf8k7uLoqN413HpPzU7LPBwrMt1J5iPoR/al2hdd2HdLcoboYgj5tCmONcTcui7OpdAAM0NQC2AAAABWQAIAAAAAAZbHIFEJI2EyD0AXCIKYrnibmJCXze9U35t8cAtkz1LwVzACAAAAAAxY/MrgqVf9W0zy4keJI4zJFKUMZsvy4cxqw6qvY2uGoFbAAgAAAAAHWzwCyWgGSVgtjqLEyY8XczhDiIUgV/FSmvPaIFGR9NBXAAMQAAAADnvhmXenrGLgvNwJSHzV/GU/5M9BxSf5H2DUPjrH+WYwpGCQmyzSTc+kCRx1NPqyf4AAM0NgC2AAAABWQAIAAAAAAjCy3VgA4qfecXFADgURacy8klmZppF70e7oACeEt77AVzACAAAAAAKnlblZyfpcXIUm1DPSKCKIvj3LdyFMUZCJuIvqyfyJkFbAAgAAAAANdM1Ah+QJAJyknxcWFGuDvRqKrGKdKTyk4jmy6CrDQSBXAAMQAAAAA3CAHzOJC1d5fdxEKptebbKfYtvi8dGxIYHwwloC6PbZGp7N6QB/+VODHvBvzESsfMAAM0NwC2AAAABWQAIAAAAACVCIez4H03xjjTxdoeeaMKbB2q3RPf6miYzs+rrD4o7AVzACAAAAAAUVj32/GhBRJJzmMDeD0VRIS1NTEb2G8fhDRYNlsSxEYFbAAgAAAAAOclkW0ojUZtjkzl5XA8J1wGUPt8eyXjlVw3rZ9vTu45BXAAMQAAAAB9S/5PXcHhXd49allwZbbrD1CXsaw/MFdlYBKQ53AfFatVS+deQqDL1r6KIR7Zax+7AAM0OAC2AAAABWQAIAAAAAC5EntiCpTldThXw+asqYcNJ03/0BeTiy3BehDGQ2ApCwVzACAAAAAAN1T43hRWT/XAWPp+AcAZvjyxjJxqgCbTh3SAMsyDs2gFbAAgAAAAAG1RpV/I+wsTR7N3W2EDP2Nfw5Ta+qya4JG0S4YcBMT6BXAAMQAAAADODN+XiO5yCGTZCUQjZjvEswwqz8MJ0t5eGwpZSJVKzzL3w2sTFmHZCHTyiFSpnpr0AAM0OQC2AAAABWQAIAAAAAATwDL8VPtPI6ab0bY/1ZRYBl8/y44exZmM5HeXWwXx1AVzACAAAAAAzsh0XK9fu4x4RpB4CmnlbU8HK+yNSO1GVz8yl8gLNpkFbAAgAAAAAGxiK01DFN24K43NFO6reYDokblI2VYmpXu43vDxUXrfBXAAMQAAAAAsmPr92GoEudH8v8vhwXc/T0/PVG+WFtoSpzS8ra1TXt04bIElLXkf4aTqvTb5CM5RAAM1MAC2AAAABWQAIAAAAAB9HHjcQJFeFiOSTlpTiWhlc0zfaA0X96HggZQxKB96bAVzACAAAAAAZdQM2aV6xENTgIIICOKj6aLhtr4eacmieEiAxNY/1KkFbAAgAAAAAMVycQOKlIrHMws3+wl+XheYxR+n5uShDHqj2pUrsqJQBXAAMQAAAABChMITFP9WTPNAh7UTDQY4zb/PU5H8UQ1qMoCkiVqzeaIIsJSxm47ELG0wx4V8v5T7AAM1MQC2AAAABWQAIAAAAAATHJ8YtKLwDbYF1Eym5sDbGJjGvLYhdL7dMv+OMBKyowVzACAAAAAAjRimUOIzij8BP4q8krZdIHJxRQnYMd4KXWNSU4SfCQgFbAAgAAAAAF3ZDSegL/ctC9HyHCRVoB3QDkB/jXy+q7E/jdyZtumtBXAAMQAAAAA3xRW2/SOAbjJZoYMtc7vVeea5KeSB2RvzSt60UbmF4izpR2jVh6wGLNMkeQJgYEwdAAM1MgC2AAAABWQAIAAAAACRaorXgwSBnVzA5LiD3JbrAQxIsFw05LXdE075hDmjNQVzACAAAAAAKf6e6IIsKU9F9RfkursMoWfVI0l75yzL2Rm+Oauy+98FbAAgAAAAAJofqRQMpBpc3uEj5pRbK3SBPnx4xx/7yTs7HyZ+4vDaBXAAMQAAAABO/S03CTjNPaXv4Q/lNqmNc6UgraCfYkbASFaBp1O/cjB4qNao80GNZhkOrEu886D4AAM1MwC2AAAABWQAIAAAAABq/dBlUW2OCiEHJH/DqgqTpk69lMyDSNfevRkQWzKP5wVzACAAAAAAZurfos9KMZuZYvT3KwvUKxS7mWlxxXqZPyqmVO1bL7AFbAAgAAAAAAle3VrzgJ+uMR67iy3aJd8xNwCtPBA65BoXa9iQlktvBXAAMQAAAAC7J9SBXt9jhWcH7QSEJoJIx/XDJCt72Imb0/KqmW+EtVLjxgkDTDGgxTFN5/pTqhriAAM1NAC2AAAABWQAIAAAAACkDPTbW292/7sKSeLL2NamuWTwHF/1Q96LXO4vt2DYbQVzACAAAAAASGKPvjooX2y52AuNgpPkZUFsM9uU74Dh2JnGnnz3dWAFbAAgAAAAANUoxTn6+RIZi7g8Wm+ocZr5RLFeoDspELXKkxb9za0gBXAAMQAAAACFg+2L6HEplnEAxXVb409NZNhugWwujY9a+Q7kUgcfnUQ6wHb3B9WQaP68CAmZljguAAM1NQC2AAAABWQAIAAAAAASRskHLtBNN3Zg7n3gudRKRWlxr6xFGDn9P7djm2lmbQVzACAAAAAAAyxWCS0yxNp1vRXpKY0g0sEMc3HFXQEcKCYMXz6jQEEFbAAgAAAAAF6QnUHjjxDUmzl4XbVN/0EVUp6YZ2IXedj+2pVUvzDIBXAAMQAAAADvL/qI8prQ47g43NMSeiFlIgpsZMklsNHoHiPWcQA8ubZbwf0QsBcL8xK7zfwRSHngAAM1NgC2AAAABWQAIAAAAAAUTc52Auixs5RBcpWFtkA9YqdaFWKfYC760YknHuHT4gVzACAAAAAA9i1ZUY+EKYUy15ZL9B/6IziWwaQ+T4NZvs60lwUdfQwFbAAgAAAAAB5tAY3koFZaHfmzA8PmYBHE8ji1XVWqfZfpZmGQkMe1BXAAMQAAAAA43Lk2hiOpvKVPUMK2PUDUCLVCU5iumKF1w/tciHnm9pXV0glcvWkCBheXR+RN8azcAAM1NwC2AAAABWQAIAAAAAD4aemeYTsW438FDmaTWiIMJKqJLcdQbOSTHbV5VRjQFQVzACAAAAAAlE7UqP/7i6tpnkUWi3y1Pm3Qlbw+gsMPuJshygiVr48FbAAgAAAAAIPOM+ursUIg99ECHO+iPiYTGqZl9FI7pbDrDn9qp8RfBXAAMQAAAADcWzX5ibBFfz0qYf0O7ABSCa+RbOD+lU6HB4PKgA51rDWD9BrWKiwHvJG/E1iR7z23AAM1OAC2AAAABWQAIAAAAACi3E77uLPIfnKkjitfOIl17RSDIuQs+jASqOOagn1qygVzACAAAAAAI2ksEaNKkdjC0Z8x4Nx+YSQ4sjIKNFQ1QiiN/uz+FikFbAAgAAAAAPv9DTFg6bxT4TcBw4aXd1WWUli6RluyP9f8/ynb2xhdBXAAMQAAAABxpR/lSeAm+dAqHV8/noIHB2NBYdtxO2c9uStGKHGoe2FOe8oILrfFUqmktMUugbEeAAASc3AAAQAAAAAAAAAQdGYABgAAAAFtbgAAAAAAAAAQAAFteAD////////vfwA=",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "double",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double-v2/mongocryptd-reply.json000066400000000000000000000033721521103432300277750ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A4kAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAKgAAAAF2AHe+nxov3V5AAW1pbgAAAAAAAAAQAAFtYXgA////////738AEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "long",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/double/000077500000000000000000000000001521103432300230525ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/double/RNG_DATA.h000066400000000000000000000201131521103432300244370ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xc1\xa1\x03\x4e\x80\xc5\x22\xf1\x6b\x0c\xa9\x39\x2f\xda\x68\x88"                                                 \
    "\xc0\x05\x26\x23\xfb\xa7\x85\x1e\xf8\x95\x4c\x23\xfe\x13\x26\x72"                                                 \
    "\x67\xa3\x19\xe1\xb7\x2c\x24\xef\x36\xa0\x14\x16\xc8\x57\xf0\xee"                                                 \
    "\xc9\xee\x1c\xa7\x25\x8e\x07\xe1\x47\x25\x72\x03\x32\x70\x7a\x9a"                                                 \
    "\x1c\x9d\x9c\x3f\x0c\xbd\xe3\x13\xff\x27\xba\xdd\xaa\x8c\x08\xa4"                                                 \
    "\x6b\x2f\xad\x5d\xce\x5f\xca\xaf\x75\xc3\xd9\xff\xe4\x5c\x36\xb9"                                                 \
    "\x02\xd9\xc9\x6e\xee\x43\x49\x28\x4e\x76\x94\xe9\x5a\xb9\x46\xb5"                                                 \
    "\x7c\xe2\xb7\x67\x1f\xfc\x02\xc6\xdd\xc9\x82\x26\x00\xd1\xd6\x67"                                                 \
    "\xe1\x89\x54\x31\x24\x9c\xb1\x3f\xd0\xd9\xde\xe4\x63\xd9\x9e\xd5"                                                 \
    "\x5a\xb7\x20\x26\x30\xab\x48\xbd\xac\x23\x5f\x95\x4b\x96\x17\x4f"                                                 \
    "\xd5\x97\x4d\xdc\x24\xd7\x46\xd8\x73\x6d\x95\x3f\x0a\x51\xdb\x60"                                                 \
    "\xe7\x60\xfc\x68\x5b\x78\x8e\x02\x19\x85\x44\x90\xb7\xe5\x60\xa4"                                                 \
    "\x8a\x93\x06\x13\xcb\x1d\x3d\x66\xba\x01\x26\x4b\x0c\x4d\xcf\x69"                                                 \
    "\x7a\xde\xb9\x2e\x40\xd1\x3b\x73\x7c\x6a\x9e\x3e\x1f\xa5\xff\x20"                                                 \
    "\x4e\x94\xd0\x1d\x7c\x9e\x22\xbe\xa2\xb1\xb5\xa2\xff\xf5\x47\xeb"                                                 \
    "\x59\xe7\xc6\x4a\x67\x0f\x0e\x7c\xef\x9a\x6f\x49\x63\xe5\x64\x2f"                                                 \
    "\xcc\x73\x63\xdd\x36\x28\x03\x4c\x9b\x7e\xd2\x30\x83\xce\x0b\xb1"                                                 \
    "\x20\x30\x83\xbf\x0c\x21\x44\x3d\xc3\x73\x42\x49\x57\x47\xcc\xfc"                                                 \
    "\x97\xa9\x1a\x8b\xd2\xc2\x2d\xe9\xbf\x14\xe2\x9a\x05\xc3\x27\xe9"                                                 \
    "\xa4\x1d\x55\x4f\xea\x78\x92\x81\xdb\x9e\x9e\x7d\x14\x12\xd3\x48"                                                 \
    "\x93\xec\xdd\xdf\x4e\xc3\x78\x35\x20\x7a\xde\xa0\x3a\xee\xd5\x8a"                                                 \
    "\xef\x0b\x39\x7d\x8f\xfd\x43\x50\xe2\x42\x0d\x76\xd8\xb9\x42\x0a"                                                 \
    "\x3d\xfe\xbd\x51\x78\x97\x88\xb8\x2f\xf9\x32\x4d\x0f\x70\x58\xf0"                                                 \
    "\xf9\xdf\xab\xc0\x58\x28\x9e\x8f\x7c\x34\x45\x4c\xc1\xc5\x18\x08"                                                 \
    "\xc9\x66\xf1\xfe\xea\xfb\x50\x6a\xbc\x85\x19\x10\x79\x64\x76\xf6"                                                 \
    "\xfa\x5b\xa4\xe7\x3b\xcc\x39\x35\x7d\x2c\xd8\x5f\x8f\xf2\xa1\x93"                                                 \
    "\x15\x34\x87\xc6\x01\xa7\xa0\x00\x94\x8c\x02\x31\x25\x5c\xe9\x97"                                                 \
    "\x9e\x3f\x0c\x72\xe6\x0f\x7f\x21\xe0\xb9\x17\x22\x33\xba\x69\x0c"                                                 \
    "\x9f\x61\xb8\x8d\x6b\x2f\x86\xa7\x2f\xbf\x7b\xec\x82\x8e\x58\x34"                                                 \
    "\xb2\x85\xe2\xaf\xf2\xd4\xa5\x7a\xba\x42\x17\xb5\xc8\xfc\xf4\x49"                                                 \
    "\x65\xf0\xc4\xd5\xbd\xe6\x3d\xba\x91\x36\x5a\x84\x08\x79\x7e\xc8"                                                 \
    "\xe0\x74\xa2\x34\x1a\xa6\xda\x1a\x84\x68\xb7\xa2\x66\x40\xd8\x1f"                                                 \
    "\x48\x81\x47\xaa\xe1\x47\x05\xac\x84\xde\x15\x5b\xdf\x2e\x6b\xdd"                                                 \
    "\x69\xaf\x9e\xca\xed\x99\xaf\x7d\x04\xb3\x8d\xcc\xf4\xa5\x7a\x6f"                                                 \
    "\x7b\x37\x72\x53\xc2\xe0\xbd\x08\x63\xb3\xa3\x12\x84\x25\x55\x1c"                                                 \
    "\x37\x7b\x77\x71\x6c\x19\x27\x27\x03\x97\x89\xaa\x93\x75\xf9\x06"                                                 \
    "\x69\x1a\x22\x7d\x35\x66\x3a\xe7\x72\x91\x23\xfb\x39\x00\xae\xa9"                                                 \
    "\x08\xe7\xae\x2a\xb7\xc2\x4d\xd6\x72\x15\xee\x70\xd1\xaf\x4f\x4d"                                                 \
    "\x22\xe6\xc8\x79\xd7\x41\xb2\x28\x5a\xa2\x3e\x0d\xbd\x33\xac\x7d"                                                 \
    "\x50\xcb\x96\xb5\xc2\x49\xc5\x7d\xd3\x36\x7c\xda\x6a\xc4\x54\xfa"                                                 \
    "\x3f\x7c\xa4\xde\xfe\x60\x2c\x89\xc4\x79\xe4\x31\xdd\xb7\xa1\x76"                                                 \
    "\x31\x43\xe1\xbb\xb8\x56\x78\x1e\x2d\xac\xe7\xff\x34\x8f\xc6\x36"                                                 \
    "\x2c\x6f\x43\x65\x2f\x7f\xd1\xe3\xd1\x89\xf5\xe9\x2b\xd4\x58\xd7"                                                 \
    "\xaf\x1b\xb4\xfb\xec\x61\xf0\xf3\x8f\x53\x9c\xaa\x31\x69\x17\x7d"                                                 \
    "\xca\x6e\x1b\xba\x0d\x60\x43\x4d\x35\x4b\x23\x88\x4a\x51\x29\x7e"                                                 \
    "\xfd\xb6\xe0\xd4\xcb\x94\x8b\xd3\x65\x22\x07\x9b\x39\xea\x46\xff"                                                 \
    "\x5f\xf2\x4e\xee\x2e\x8a\x8d\xe3\x5d\xc7\xa4\xfc\xd4\xec\xb3\xc1"                                                 \
    "\xe7\xbe\x19\x97\x7a\x7a\xc6\x2e\x0b\xcd\xc0\x94\x87\xcd\x5f\xc6"                                                 \
    "\x37\x08\x01\xf3\x38\x90\xb5\x77\x97\xdd\xc4\x42\xa9\xb5\xe6\xdb"                                                 \
    "\x7d\x4b\xfe\x4f\x5d\xc1\xe1\x5d\xde\x3d\x6a\x59\x70\x65\xb6\xeb"                                                 \
    "\xce\x0c\xdf\x97\x88\xee\x72\x08\x64\xd9\x09\x44\x23\x66\x3b\xc4"                                                 \
    "\x2c\x98\xfa\xfd\xd8\x6a\x04\xb9\xd1\xfc\xbf\xcb\xe1\xc1\x77\x3f"                                                 \
    "\x42\x84\xc2\x13\x14\xff\x56\x4c\xf3\x40\x87\xb5\x13\x0d\x06\x38"                                                 \
    "\x37\xc5\x15\xb6\xfd\x23\x80\x6e\x32\x59\xa1\x83\x2d\x73\xbb\xd5"                                                 \
    "\x4e\xfd\x2d\x37\x09\x38\xcd\x3d\xa5\xef\xe1\x0f\xe5\x36\xa9\x8d"                                                 \
    "\xbb\x27\xd4\x81\x5e\xdf\x63\x85\x67\x07\xed\x04\x84\x26\x82\x48"                                                 \
    "\x85\x83\xed\x8b\xe8\x71\x29\x96\x71\x00\xc5\x75\x5b\xe3\x4f\x4d"                                                 \
    "\xef\x2f\xfa\x88\xf2\x9a\xd0\xe3\xb8\x38\xdc\xd3\x12\x7a\x21\x65"                                                 \
    "\x38\xdc\xb9\x36\x86\x23\xa9\xbc\xa5\x4f\x50\xc2\xb6\x3d\x40\xd4"                                                 \
    "\xdc\x5b\x35\xf9\x89\xb0\x45\x7f\x3d\x2a\x61\xfd\x0e\xec\x00\x52"                                                 \
    "\x71\xa5\x1f\xe5\x49\xe0\x26\xf9\xd0\x2a\x1d\x5f\x3f\x9e\x82\x07"                                                 \
    "\xfb\xf7\xdb\xa1\x87\x56\xd6\x13\x4b\x2c\x8e\x73\xbc\x56\x98\x1d"                                                 \
    "\xae\xe6\x9d\xab\x15\x83\x0e\x0a\x4c\x3c\xd0\x11\x41\x96\xc0\x56"                                                 \
    "\x94\x7a\x1e\x0b\xc8\xec\x23\xa3\x9d\x6d\x01\x01\x22\xa7\xbd\x96"                                                 \
    "\x51\xb0\x6a\xab\x98\x7c\xcd\xd3\xff\xf0\x67\x63\x16\xf2\x2f\x62"                                                 \
    "\x3c\xbd\x8b\x4a\x49\xb0\xe4\xe3\x12\xd4\x6b\x12\x41\x32\x22\xc0"                                                 \
    "\x51\x0c\xaf\x7e\x01\x46\x2b\xd5\x50\x9c\xd6\xfd\x42\xb5\x11\x4b"
libmongocrypt-1.19.0/test/data/fle2-insert-range/int32-v2/000077500000000000000000000000001521103432300230645ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/int32-v2/cmd.json000066400000000000000000000002711521103432300245220ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberInt": "123456"
            }
        }
    ]
}libmongocrypt-1.19.0/test/data/fle2-insert-range/int32-v2/encrypted-field-map.json000066400000000000000000000011731521103432300276120ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int32-v2/encrypted-payload.json000066400000000000000000000132051521103432300274040ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "CyINAAAFZAAgAAAAAAYUuQ9Y1Ie7prKiZXHw9WYEhXcFQdSn7mmkPHFKGAEqBXMAIAAAAACJiCpdKff5FC0BawKHW+wWtxyoXymVQItTooswHY2SMQVwADEAAAAAnfKX2WSr3o8S2zaag8gMImdCNfErp8BkxXFlb1aNkPcwyDuSLm7gIXzlP992WjKF3wV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBLRt4PvCvZ7N8s3Xakt0SrKupID4FsPQTVZXoX1fBkDnk0irfALQzHOt9kvd3ZurzzZXCJPfQTlZIlq+MGiG3gxBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAAAAAAAAAAAARnAJsLAAADMAC2AAAABWQAIAAAAABT+uzCz7yxp7uN3+z202vxQ5NOV985A80zAqx86317iAVzACAAAAAAotJPQKqQGOGUkMWaWW9GlbqGpHdx8Y9mSX7tBhUz8YMFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAAD0xvkQWzHDaNccWDjxapqJsQQJxIpJjSzH2N1sMl52I8VCGmhbZ7eiv1GA6+MQxOtVAAMxALYAAAAFZAAgAAAAAC6uvkdzWq3DNcfLqndCqjmD1janFLc/rxnwmsOTlcQUBXMAIAAAAACs7A835dqkD4syrnsnA2rujWW5+Q9e/1ELolPgiQVEoAVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAOD6SY9UmJPLh8W9rY+WOcGfBhR2IbREcrPOlvcWitCDpgifW8LNgrL1DtsD+aUrX0sAAzIAtgAAAAVkACAAAAAA4ZMiLagfridRRfmhKbzkDw5T5V0S5kyhqfBvZn5dPlYFcwAgAAAAANo9G7ZRhNvttdgY6+vg49XT1TXWf5UE6DAFlX/AP82RBWwAIAAAAACHR4WfBFBn/Zv0d5gD1QJv1LAe7z5dfOKsdcEJaup2rgVwADEAAAAAh/iVo21MDF6a43aBUOdvobfFrmz0OxcKEl87Qr/9ulWQYnliZ2RZvlhXHLqxkpgtiAADMwC2AAAABWQAIAAAAAChShCDXpnDH8wgL+pVXLX6iPO5/XkcAyVINSwPJHWMEAVzACAAAAAAaNNLGMgUd2cbkY4lzPfLGl6J5Qc2Il4H90QDcnu40WUFbAAgAAAAADnNH06cbj1eufeCtTvMEOif6iP7/UVQg6m0u/B6xZS9BXAAMQAAAADH3pCs96kzIBJHPZlls6ErYAktA2yuwLKyPFi9iXdea9rTY7PxuUQoZyLXHnuwLgjwAAM0ALYAAAAFZAAgAAAAAO+3mZV828ldgLhwSL9H7PSvmJlv7WrvByHAAAGSof4LBXMAIAAAAAD/ELnpWf3l/SNoUNP0S338kKIp5E1V+3Z99E/3FPXh1QVsACAAAAAAEC+jQrwZIUPbpQq8GjVCzoL+1IX6d2YbIkx0jheRskoFcAAxAAAAAH5K57wo1v6F3PVPdumG6cN3hShAXObXElzOyoLNfG0Uu5KyU7ilPAVwJ5NWL9jZBMwAAzUAtgAAAAVkACAAAAAAE+06hBFHl4f7CHQFGGj2ZZuT3WZEMqTj4CzUM2e9PeoFcwAgAAAAAIIlGRvBtZCaXT4OWFMXF+3VC8jzuJFe3mR5RpM/T47OBWwAIAAAAACBRJPSwvsKXZ7fostzk7s73P1v+MuxlsoSzfGCBVqQWQVwADEAAAAAvx9IM6acqD8WYRJIbL+bwmpzAcv2J0i3W8dOyHdHo5GaGbhUbQs+2XJAsKBk/qIFyAADNgC2AAAABWQAIAAAAACvCIk3Z/O0aqysrOwm+wSmvNpZFsMJBXBhSIBBLiT52AVzACAAAAAA9dRAOydZMcNo1yNh9XP7JS1cA4EEBD95wN3t6NZ8pNUFbAAgAAAAAOV9V/DlJ/McpfffRxJGPtgN5IgReZMW5hjPPCC5wfh8BXAAMQAAAADq6xqnx/gbvKUwg6LsZEPah3z3EDqqUUHd2yTo4Uc8boUF1TAl0lfj6Eu3fu6k0qTcAAM3ALYAAAAFZAAgAAAAAGKJ35r7U30MbUBQIvdcgcQW7MG8KbYldN4PCdrQRRcRBXMAIAAAAACwwmMiXwHvD7BEzhBgfDj4L0AajySh/Qhtz42i5ogkbAVsACAAAAAABcm2+tbs0VSGFn9rDvWyvhuIRVDepRdoIvNa6TRv3zkFcAAxAAAAAIarDydCpSFo/NqlzaMC6E+eBx7LiXrB/fG2Z9szcbLmXjUcSJT2iKLj1tFILB/vmisAAzgAtgAAAAVkACAAAAAAYLNccfL4s3DMHDChNpne5is7B1RfWUpctFbO/r2JTAMFcwAgAAAAAPOdU0Owvp1pFujAWWt7nKFVJxcXTMoVw3ZZiheO1DB2BWwAIAAAAAB1bCFHfdyYaPnhvEnOmfNQZIQRVqhxDSwj79IqNXdlmgVwADEAAAAAXEyApIKBa7i/jiq5ISOwAbfHhwxnDXWMoi46jtZK5PL0TFYnvfIW0kItQIYnrygZ6QADOQC2AAAABWQAIAAAAABLprrCZQNYrlCa1tiktkFmgLViqEn2CEz2GYfCozEMxAVzACAAAAAAI9l/JeiixzTr2mna2nwoaxaJ1oHe+if0AJrtviapy3gFbAAgAAAAAIsIWi7l4L3h7alFc2Mnyg+QQ1EIMZ3D15k/sq+BvaYIBXAAMQAAAABmfDn8qbdhBLJhj1/rb9Ly/kz+GbT1WpkxhezKcoB04iBaBYKJrfSeqH+7Pk8SIN5LAAMxMAC2AAAABWQAIAAAAADUq7UIfHJv/mGbsXxGaSnrk1w2OaF1XIQOdL3Ibv7jAQVzACAAAAAAVYSgjDnwcfQ937v4LtelZhXljZRVaPPKpZ72Eg6Qp5wFbAAgAAAAAJnqBNMfgotr0Bs83VunOklSDAVnoO8FAAs2mjULfoP2BXAAMQAAAADLBVQCW9s1i387ZRgAamYrfC6rt+uPHYFBrs6eJ9LWU8G2/EULjJeAga30urUmqCfTAAMxMQC2AAAABWQAIAAAAAAIz6wOrQqBXWAOmerbQckRy4fLI5AC9k5DhBJ3kHJYJgVzACAAAAAA4c2luh9MO6xSNqffZoH4SwI19i3ffdJ9iyAaGitoZxkFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMQAAAAD4jIxUQRn5M+M3aEB8y8gSZVxGM9DWUnOXnxW4nWvwVBy6M6dsIbMKK0ntDsHoUK5wAAMxMgC2AAAABWQAIAAAAABy6E2SiWcuWFUFZF7u1+9q36Bidd7cB0PMwxN4qUm/iQVzACAAAAAAfXXRP8HSe9qTC4Mhpwm/nGty0swMvdl3Ly7ZwgQ5jjIFbAAgAAAAADPuDapB79oBpsUKLXh9oeESX6227xBMIb3JHWmpWCcvBXAAMQAAAABj+GDDYudxPSCKRvpd2JD43W6j6WiKDj87cuSFutQ1viBwlIcebxdNQCrQk2lc4mTZAAMxMwC2AAAABWQAIAAAAADdcutpKLPVhdR6vTKi+/9b/ngd8WF/gvYzoSOKPGwhLgVzACAAAAAAWm36BOmL6gbNykGY7pHDbJZZEHDxt+WMyNRCYL6dKX0FbAAgAAAAAMd299Bokg6hihom7FZAtMDxLS3VJ+aIngv5kdIdD36KBXAAMQAAAADpphQq0q3J5VDQffMiAdBMfNbxzAka6gLTsD/8iu//3n5Co1mSi/PpkEYj7vm7G/xnAAMxNAC2AAAABWQAIAAAAABMvmSKiuV9tNx7pKKhKPEBb4RlXlptLoUC6XzzqwZM/wVzACAAAAAAQNp806yyelkCYf3mmkfm6CZgaegIJok0BRG3T4wihSUFbAAgAAAAAPsI3ldHLkKkInpttNugj3cAyjtn8+aYWSppd6eF4IP6BXAAMQAAAADaAPXQlyrss/msgXPCjTYcuJoAMlULfUlYiGl8glGovkB5QT5Dyp3Z42oSnLxkuYXiAAMxNQC2AAAABWQAIAAAAACWCYL1HRq7/1vOBbRFajjqpNxf3fVtYNrMKH0CWMxlqQVzACAAAAAAgrBrcFAmjTruvGSRfTuTnuF2+uXz/Vtr3g60RqkG4bYFbAAgAAAAAIQEH2axT3qVk3JPY6vLwoE1x/AdRl7Die4AwXOlTSRPBXAAMQAAAAAUsy6h6usThQG3P97tMBSo39YOF/xc2h+xndzc6fbNpbMRUPw0GhpRxIcQgigl6IF8AAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAAAAEG14AIfWEgAA",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int32-v2/mongocryptd-reply.json000066400000000000000000000033511521103432300274570ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A30AAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAHgAAABB2AEDiAQAQbWluAAAAAAAQbWF4AIfWEgAAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int32/000077500000000000000000000000001521103432300225375ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/int32/RNG_DATA.h000066400000000000000000000057701521103432300241400ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x9d\xf2\x97\xd9\x64\xab\xde\x8f\x12\xdb\x36\x9a\x83\xc8\x0c\x22"                                                 \
    "\xd1\xb7\x83\xef\x0a\xf6\x7b\x37\xcb\x37\x5d\xa9\x2d\xd1\x2a\xca"                                                 \
    "\xf4\xc6\xf9\x10\x5b\x31\xc3\x68\xd7\x1c\x58\x38\xf1\x6a\x9a\x89"                                                 \
    "\xe0\xfa\x49\x8f\x54\x98\x93\xcb\x87\xc5\xbd\xad\x8f\x96\x39\xc1"                                                 \
    "\x87\xf8\x95\xa3\x6d\x4c\x0c\x5e\x9a\xe3\x76\x81\x50\xe7\x6f\xa1"                                                 \
    "\xc7\xde\x90\xac\xf7\xa9\x33\x20\x12\x47\x3d\x99\x65\xb3\xa1\x2b"                                                 \
    "\x7e\x4a\xe7\xbc\x28\xd6\xfe\x85\xdc\xf5\x4f\x76\xe9\x86\xe9\xc3"                                                 \
    "\xbf\x1f\x48\x33\xa6\x9c\xa8\x3f\x16\x61\x12\x48\x6c\xbf\x9b\xc2"                                                 \
    "\xea\xeb\x1a\xa7\xc7\xf8\x1b\xbc\xa5\x30\x83\xa2\xec\x64\x43\xda"                                                 \
    "\x86\xab\x0f\x27\x42\xa5\x21\x68\xfc\xda\xa5\xcd\xa3\x02\xe8\x4f"                                                 \
    "\x5c\x4c\x80\xa4\x82\x81\x6b\xb8\xbf\x8e\x2a\xb9\x21\x23\xb0\x01"                                                 \
    "\x66\x7c\x39\xfc\xa9\xb7\x61\x04\xb2\x61\x8f\x5f\xeb\x6f\xd2\xf2"                                                 \
    "\xcb\x05\x54\x02\x5b\xdb\x35\x8b\x7f\x3b\x65\x18\x00\x6a\x66\x2b"                                                 \
    "\xf8\x8c\x8c\x54\x41\x19\xf9\x33\xe3\x37\x68\x40\x7c\xcb\xc8\x12"                                                 \
    "\x63\xf8\x60\xc3\x62\xe7\x71\x3d\x20\x8a\x46\xfa\x5d\xd8\x90\xf8"                                                 \
    "\xe9\xa6\x14\x2a\xd2\xad\xc9\xe5\x50\xd0\x7d\xf3\x22\x01\xd0\x4c"                                                 \
    "\xda\x00\xf5\xd0\x97\x2a\xec\xb3\xf9\xac\x81\x73\xc2\x8d\x36\x1c"                                                 \
    "\x14\xb3\x2e\xa1\xea\xeb\x13\x85\x01\xb7\x3f\xde\xed\x30\x14\xa8"                                                 \
    "\x9c\x4c\x4d\xcd\xe5\x31\xb1\xf1\xb9\x0e\x45\x1f\xc8\x6b\x4c\xb8"                                                 \
    "\x79\x25\x44\x91\xf3\x5b\xdc\x63\xac\x91\x93\x2c\xbc\x74\xa7\x5d"                                                 \
    "\xcb\x46\x47\x79\xd5\x69\x1f\x66\x52\xf8\x39\x49\x33\xa7\xa6\x16"                                                 \
    "\xde\x19\x7b\x47\x52\x07\x18\xe7\xc3\xe2\x3d\x88\x4f\xcf\x77\x0d"                                                 \
    "\x56\xd5\x9e\x0a\x1f\xa1\xac\x7e\x83\x99\x65\xaf\x9b\x76\x31\xa4"                                                 \
    "\x19\xa3\xc0\x26\x2f\x98\x61\x5c\x8c\x83\x54\x2c\xbd\x47\x31\xa3"
libmongocrypt-1.19.0/test/data/fle2-insert-range/int32/cmd.json000066400000000000000000000002711521103432300241750ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberInt": "123456"
            }
        }
    ]
}libmongocrypt-1.19.0/test/data/fle2-insert-range/int32/encrypted-field-map.json000066400000000000000000000012021521103432300272560ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "rangePreview",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int64-v2/000077500000000000000000000000001521103432300230715ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/int64-v2/cmd.json000066400000000000000000000003051521103432300245250ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberLong": "12345678901234567"
            }
        }
    ]
}libmongocrypt-1.19.0/test/data/fle2-insert-range/int64-v2/encrypted-field-map.json000066400000000000000000000011741521103432300276200ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "long",
            "queries": {
               "queryType": "range",
               "contention": {
                  "$numberInt": "0"
               },
               "sparsity": {
                  "$numberInt": "1"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int64-v2/encrypted-payload.json000066400000000000000000000365721521103432300274250ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "CzoqAAAFZAAgAAAAADPNHyGdziCQ9zaQaZjZ+ZoP1K+fjxZLiXuA9j54KiiZBXMAIAAAAAChNxQ2a6y9FVSqz5Y15owrjv4dN4Jzleg6WSnFKQq3GgVwADEAAAAAUEpwfMJ+8dhl0lw9CBfDz/jgGL0SCmaYUQV9l3+fNR7ua/Pt1yVSqPR6M0+T5sq28QV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABIAAAAFdgBQAAAAAKvN76sSNJh2EjQSNFZ4kBJCSYtj3g+vR9lDfLPCaBS4h8TfchS6H0piADZqDchoBQfmm5EEBLF5Erg3xn+0uHtw4/0ud/Z43hFVbNQADHdrBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAfDdIELlhAdHzjCWoKAZIRI3A9dZrhO1Vbn3+IuhoCDUSawAAAAAAAAAAAARnAKsoAAADMAC2AAAABWQAIAAAAAD3SaL8hpF71u2BBV5LWGtNK2vzL9R5fHsvCZvwkQ64QQVzACAAAAAAIfOOmtLQW/1bhcN7t3wcGsU8XDNU+Ij7cR5sc2oCj8wFbAAgAAAAANAduMH6J3Aw0lQgoXgF38oxbSe57qHNLvt0h2bPY+/TBXAAMQAAAADR+Gf62OI4PnQ6eu2+6lqK6UOp2Kp9wNqSjqPKVEdc4fwkHmA7S6VBN1Q7wXuhbwT/AAMxALYAAAAFZAAgAAAAABkUzDlTUF7/0HLvWhtDfutHq3uST8Hk8O/Mfb8LgFfNBXMAIAAAAAD11twfbtpOWFUqQFXS6P7TaXobAhu46STaaYWePYOJGAVsACAAAAAAYtpOd8Bd7k/b0zH8fsxlfmB/SwY0mcOqYsn4JIIuDQwFcAAxAAAAAMWwkZY1taCQMSmuL6COfIA40gPx9XVlinWKLKeFCnOqhDSO0ls/cwjSffrX/2ecH+IAAzIAtgAAAAVkACAAAAAAbfXWI0mudr9ZVWBKFn22/utVf5u6g726PURHBbaJQGQFcwAgAAAAAIlshJPXAu11tfCNCUi/LUbvrpx3juQCTxU0UPa7Y5raBWwAIAAAAAC2y2AT3AUAIH+THXhbHeSIOiHwC3SYGtcfu/+AR+Pw9AVwADEAAAAADadTfL9GJd3AQlHHB8WgTYZ7UF9aaYOOJua+o+dwUe8a+axBaBNr4M2M9oCvXLA1BgADMwC2AAAABWQAIAAAAABBW34n1n6EECT249xWNI+T8HPAvBignIG1d/HkKAtGTgVzACAAAAAAhHf3CCteucHBkS7uIxoDokqUBNubQRhIE0MiJNEZz0UFbAAgAAAAAPjBRHfF32UqW1yqqjwti90xBbMJGhuRX+mkoH/Kfl1SBXAAMQAAAADBfVwDRsO5pQNZohuLC0aTnMrl/7Ki/fDBHD45FTp2E4oIyG1KyTm0AIH9tAmf4Sa2AAM0ALYAAAAFZAAgAAAAAD/YSnioA3WSmGcl6a7BFklyhaydK38gJeO7r3oezU8RBXMAIAAAAACAHpBXiRyLvOJuKsrTRjaFTj/1/CsI2UrztFx+Aj6QQgVsACAAAAAAj6+Zh8895xm2igvFDFvt1XbD/y9TYmFiXqVG67Ok05sFcAAxAAAAACfwXvLzPonPKLglOqUjjKLCyBFQ6ZIJlYFlKG2AdlIgNivzSVHdQTPpW6xeJ9v370MAAzUAtgAAAAVkACAAAAAA506QVBUcYHN8Ry29En/VHKlDhJJrKEXUdGnpiVam9V8FcwAgAAAAAJpjlucimqVoYMtKlUVLsFUTfH/KDX9/ueAWF1zIj+TlBWwAIAAAAAD2UyDs4dKgX3fV5U/gFexoeWadDH0KNKERx9bkNel+tAVwADEAAAAAadZstK9QMm6CM0f3H9WGE4J30k/rBQOkucHBT3eaiazCuMi45HLyfKCGtSs4H/+i7gADNgC2AAAABWQAIAAAAAA4IOW9suPOB1crla5dX5xlitqm/tu9BPMnWb9FUd3lWwVzACAAAAAA1TG9YtQrRzgOFfuDDAryNlMuA/zq0H17KIA984DCC5kFbAAgAAAAAC68ESMoTYfRoWiGGzpMw2zMpA5dVpAVxph8gyDNRiv9BXAAMQAAAABIT++2dioHp5yCJvYdKZ3+qtYQKvXQshsb4iOrdRBkbH0uw94hwJqPwFljdR4qOMNJAAM3ALYAAAAFZAAgAAAAAF0rtZYhjGDzgtN1TxstF3wzgA5WzC39i+BH2nXKguFHBXMAIAAAAAD/0QbuJSfSKwdUeHaM55wNNXR0BQpsPAqjvz99srJw7gVsACAAAAAAF/KztyDstiH1gjP7gAJviUE2EguHsNqkpdDA0jaoGrMFcAAxAAAAAN4hiGEvDkIZaUbmvu8UHgUvkqT0gxw9G/nS7nLPPKCZo1EI33/NFLn29cYBqg2MBhcAAzgAtgAAAAVkACAAAAAAc6WKbzIeIyY5VYXmXNCTA2mTrML9+y4zdHTIsecbmHUFcwAgAAAAAMzRCZFioaeYOLg0kAPZfuwtQ+yRgRlaZL2I3xQ3X8IcBWwAIAAAAADCCOO3tSDWzLO0NSz48FQqmyTB6v7tVuZ6Imm8y8/5BgVwADEAAAAAD7X+4as/f402OG7qt+1MMwEedphZ6QcWPEcduGVf5DNmrC4GjibyPg0nANhemMdjygADOQC2AAAABWQAIAAAAABRPZkQ/pvwU1KQST2Wqz+v/GW97qAp33vnMuC35jqM3QVzACAAAAAAkQ/g3zN5Sk/1xvYcOanZs3kq/pWH8pZJl6OrAG1UuZEFbAAgAAAAAF8y65b220kCLFgQofPwp64ZJ85Cb6blqMTkaTUcQqR4BXAAMQAAAAA1gRV3mZfHUfU0uxYLpLowr0sUdH1Jim9Mdsk++vyS1DhIMcgErFGc+g2b+fn1cOWwAAMxMAC2AAAABWQAIAAAAADS/LB3Y94Sz17U2r/Y7o/NLdXs60zyw2nuSrN9NX/APAVzACAAAAAAcNclH0z54Hjz3mJJkJKg9sJhVIF2cHXMoxiO3Ks5dl0FbAAgAAAAACGvT8yLU7qij8YzUo3Uqvej3vuP+9FsDoMAKolHXoitBXAAMQAAAADpQHGyVa3Wu22KHywnpbuwT4bIvXSQjQlf0i0df16pWrHyXjnhzcTl+6DY5rmzab8aAAMxMQC2AAAABWQAIAAAAAD3dSWQjVy34qeVTH+slXDTwAox//PFObUTNSiyNTgRawVzACAAAAAAe67010kUkq7T3n2N3mWHVb7wPffyOVxVxysdArsC9VQFbAAgAAAAABSZBPKfkADVhDbjUksAdp3BhzjvEQZyueaOlpFrVI7rBXAAMQAAAAD7akKXtUuaYx2EqCQVYvGyb++WVpEEg0M1mXlmkAOGcCWGXwolNZiDKAWHIVo2/MHrAAMxMgC2AAAABWQAIAAAAAC/DYONhYvRUxP267u46GUHAsOLko0fwmFx/td8HekURwVzACAAAAAAUAHEfeUEPWVNrx8jc1L8IvdZvHUwHSg4h/ZdWZNmpu4FbAAgAAAAAIuDSNb/xYIcXs2pP8H/G08eY7G3G4lCCBqwNzIiSPwFBXAAMQAAAAAcnyumUwRFXtWaxwwogxdCVque8h3j4ZojTYQV0dJG7YLBTKJFSlqCyBS+J2+VuQ3PAAMxMwC2AAAABWQAIAAAAADF3sMDdV6Z7vFHlZeZggA9TxJ1xdsHthUPkncjHnHErQVzACAAAAAA8tdxx8uRHUwCgiwr7sdNhdt0wc/jhfFrPEe6Tnl1HWQFbAAgAAAAANfL/pQHO5nOVI+sl32RW7L4SHXcWRibEZb9wCAHMIUyBXAAMQAAAABKlIK8jBnn8qS637b7KpCXseSzSvLFus5CB5BDF6BiFDd+ROSl7zMZvIc6o0MWkQiBAAMxNAC2AAAABWQAIAAAAABF17l5IecJ0a0n9JO2XhgT6JxoFEfXS7pDjCoUjwSjkQVzACAAAAAA+sNP7DT02ySfQoaz8EHj34QBSzJ4owMS0dBxz3MOhZ4FbAAgAAAAAFs9fFjwBxeUtGyRbVz2n08HXD0jdYpJ5gPf9IR6A+ptBXAAMQAAAAC4uqR5qUgrZc9PqRTAUWaIRf61Eu+HafSm948l+ZruEmUDWkcZDsckJEjIR8uJIuoqAAMxNQC2AAAABWQAIAAAAAAeCphm6M4hZw2nsenY1ZGBIeHI9Qx2AubXH5NffTagiwVzACAAAAAAcIHID+0Xnid2BN+MPiDDlbdc/rZ1Wh14K9QV+xKsX3cFbAAgAAAAACMibzsJW0vbuC7ApJV23o9wGc7ZRP+xJXYKDJXrQmQLBXAAMQAAAAA8hgjt84/6v2CB9N6JwznOvm+NBi003IOVvl1ccHYcb7Wpbf+kGXMhzWMgv+xRC2bkAAMxNgC2AAAABWQAIAAAAAAVIU9EvFcSS7lFVOLlTBInEhZ4z00fux98Q7YyBPs/EwVzACAAAAAAkp7YO+o1GuDhGcnIWDK7mkokFiypZ6/sQIfc5tl1SpMFbAAgAAAAAIs9cCJIMw0yzl23mPGS6ywDDsBq76PMdPIpUamb195EBXAAMQAAAADYBYoYn0biqVtsrG9hX/IoFKUUfixlZ7yiay8smRIGKjcBra0QC73dXizTmJXfhRT9AAMxNwC2AAAABWQAIAAAAACupeGRsOanm1acLuQH/TibsFWgqWMyi5my5WZ+9KhcbQVzACAAAAAAxpJ2gKBhU9riDiDM2yPJXJ5uVgCGWHd6nbx5tHBBgz0FbAAgAAAAABnyStURfEyF8ANA0pzXLOSaG/33b0aBeQztbKkgDdFgBXAAMQAAAADezKcnSup/1MQVpTOU6AhC9GlYp0lFmHWndWt3PQdNOLEx3LC8i4cxiL1QWF/mHPFMAAMxOAC2AAAABWQAIAAAAAAnMpfC47m4ehOeTS8f3GX0qEQY7nHRLirj0x25NqKc2wVzACAAAAAASQaNjv+rNaK+DtzPOoK88aorf/kVBttcc6TDNr4J9aIFbAAgAAAAANkU60lOzb2gzlMftlfSmp1HyF0XlUglje7s4RCR23ZrBXAAMQAAAACbI6H2m9AUBeH3Y2ohdtQYkX7U8NMAAJqxGtJurTcYerKPNntzOe2jpvGdbYJ5r/ycAAMxOQC2AAAABWQAIAAAAABvxCOiMl3QGSLeQzdxDtwEECdq/Ty0+shmYmU0bCmXngVzACAAAAAAhTbgjFjOrxDfaN3/GqMDT9o0F2U/NsGjUFOXjMMUYB0FbAAgAAAAAIiaCikQspct9aaQDtTQOktDmdHPgqEKM7ntvjwahWsgBXAAMQAAAAAr46G+F9a4JmH27OEibtuUfMKMWYdKtalcTrUilzhIyBFff5x4FgntAwgeRfABJLUKAAMyMAC2AAAABWQAIAAAAADB2o5lOduZmXGIU9NnSxv8kZmYOoWiLiK5AOmG6Vli/QVzACAAAAAAC5sLdsnTrPTiZWsuZhoftAySyNP84o3BJtm2WHNEdZgFbAAgAAAAAOQMfes0yxBuO2U6xjpWkF3bMJkgVijJ5zXS6F2ZPzlRBXAAMQAAAAD9XGgTF4rRhmgcjq/qG8f2aK8s5gkYfRfuz1QUnjMaM3Jx8AEgBdpbO4Wd55LqjaDjAAMyMQC2AAAABWQAIAAAAAAGM2rYxBCLejoSqktwu/CsJgnMAj6vORTjAq6dmHsnAAVzACAAAAAAZloDHlVkDm9x8UdVX3KyVso0h3wGKCfev7mLddc+edQFbAAgAAAAAIrVZEtyn8emGQRP1swF9bs4okw50Glf6qWnc07pj7PKBXAAMQAAAAAKon0l9U5QszjmtyDMzF9LIdy8k3+GN4vekAfyN5Yo7FjnfqqCuF1FZYUTqTS1bdlgAAMyMgC2AAAABWQAIAAAAACsYWCgHmo2GCQFUuQunw2MDToqdxeNBz8pb8G82ICWvwVzACAAAAAApOlVK60DrnrATLOn9u9+aYrwOJw3WlzJkzqB4tMN0y8FbAAgAAAAAGCDWMqm6TyhIGUFTCYZlIdEZ5KjZdQQY7/knnu6u6lJBXAAMQAAAAA35k47cHV/5zInwyUJolS41+aiZ4od4pA2bCKKjjt8Y1zG3GdlErc3bN7b0/gWjfqPAAMyMwC2AAAABWQAIAAAAAD+a4LqFa4Wra2bPcJpvQNkmLoBnO68jOpzX4eQ0nNLQAVzACAAAAAAX55A6dL7t14uF0adzXTCCL1cmMUe9lhzTVROqH2wDj8FbAAgAAAAAMaSbDsyJeoGYyd3gX/GIM1kN4Dm69TBRGRGGLnlgkjbBXAAMQAAAAA7eX2T0phtA/s14O2xKHWTTij+/2MW6K2ihocTSawHrU74qMLP0/hTZUvVE9sNaYVGAAMyNAC2AAAABWQAIAAAAADdTAgzPBdNeFSrFtrPdEJPSYOQNsZqJ8uvC4816hbt5QVzACAAAAAAiC/tDoO6sXvWQr58SLzgW2OwIXyJMlXBILB7k8wP4bgFbAAgAAAAADjCNASDlWYX4kofeNvLsfLeJiEzMqA4j49EVX8FUi6OBXAAMQAAAABJo2kmsQbM2StKqGqBP67wybXEoUOX+sjYO6Qyx6lAdWFJu9D3oYkYIYKbg49qCTrRAAMyNQC2AAAABWQAIAAAAACPsbPbygF6RUeteE4Nb509Oc5XqlaBftXLQ6IXCSwGfQVzACAAAAAAgBVCiVpqb3jRRRgVaJ3t9I/LXn26jn8XnWoWwve1jn4FbAAgAAAAAIWn9E1H0e/yjT7her8XhmXz/oepg3zcnqlyoJI0dJIKBXAAMQAAAACJGgd0cygAeYKkE1XMPasoWSVXOeg4oHiZBL3sEo04/AWsNGfaKGLCq9m+k+sDqLl9AAMyNgC2AAAABWQAIAAAAAC2BJxbZ1v/LblA/FAuE2i+ZMu3UVhUioqQdio++WbOJgVzACAAAAAArPaDrVhymBE2FfdE5bsn2dfRT1puK3+sLPR5xBmBMQoFbAAgAAAAANponm33ulKpfHQZcauGQmLZ4NMM00rQO6pmEQGs8VcDBXAAMQAAAADFttz4ovlZZtaV4MVFJWCd2qAoLranlCdfsOLlXtOMQByozx2/jCw5fduz5Y8+V+7jAAMyNwC2AAAABWQAIAAAAABH/R7nf2JITkYOfaN89QpAaNkWpFNAbxRMAdsn3ju4dQVzACAAAAAAZvT3r7aLToVP3gaF9BHEmtuiSEpge6Ni+TGNAoUL0pkFbAAgAAAAAHhpRSz/MAJHzSGOD4C/CXIocghrBW07aPmLqhLGj+vBBXAAMQAAAADQc7LAQS3Yybvb0PN4mor1OB4Yu3hF5OoHwjGHrUrENJjId7zexgr7Ptgc3iuaZNcQAAMyOAC2AAAABWQAIAAAAACdjbsCAO090xWC/yoKdgonfZJExgFeBuS6FQgGWbyxKwVzACAAAAAALCT008t2tx4hnO3+qQILKUKYH5tHNUSdg8tcf9sdZqgFbAAgAAAAAE/AIZbpCu4uINoJ9q6cVVke/ebvsWcKbJE1qqp7FJk8BXAAMQAAAAAge5FPyGBhXl0bqhZvoUKCFra/uaipVjIMB17KSpBcc0ojbLBhcVYlBVq6WOEOVYIYAAMyOQC2AAAABWQAIAAAAAAtI9u0v7PBNqfR4dRQ1HQbYFk364n55Dc/un2sYO/Q8AVzACAAAAAA+C5AskYppHFVdv4oQZP4OsXKpPacs0xsH2zi/vaB6RoFbAAgAAAAAM0GJ99jViZ1hvuPzB44OC7muCOvQ6m2I+Jn8+6gz8ryBXAAMQAAAABcP9AbQffdjqzP0CdQzrxuFmmySaF4PpYLOwJTgGnxaAlct/XeGpUOXevQ2Sk4BvgAAAMzMAC2AAAABWQAIAAAAADLJgSZs1x9DmrJyWOBjY2Z2XvuPiw4VqZDkGCvfdcJCgVzACAAAAAAv8UtyQN9AyTUqBjdetL3pHRjAdci6PLH+90hiOyK4QQFbAAgAAAAAGlQL/pYh3CnO7xlqA1zhg3N8cI2mfDx4NLf1XusdHNkBXAAMQAAAADNK5/tgsf8+qo3BqqNGdwgOQvQ2Wxx5q2Bzs5n7rg0kFUaCQ95VRqakkbVZ+GgPvI1AAMzMQC2AAAABWQAIAAAAAAlLzop5/9I1lNfpf35htzD8DYjwHm83hwbTTA9NP0F9gVzACAAAAAA9EwU3QGLL5zXu5XMsK9pboohtWi66M3pPLSl04MtuYQFbAAgAAAAADIQ3MyNeyyxMLwnaYIrkS0hlKbA/hAU0JFDMpbBi6biBXAAMQAAAABx9HVkPSj8fqmJ6UEBlBfxMW0Kkv1H0G+o++QqDns/H4eql2z69G6g/jEVCSYifM3UAAMzMgC2AAAABWQAIAAAAACajPvYYpm19yN9tE3q4kLBTKefBRHpjko2KKJGXC+v+AVzACAAAAAAMfDoh9hQb4aZOnepQkGqQeB7CGrt2Tt9KX2PyadY7lcFbAAgAAAAAE1vL87RbrRANLCW7fNrKQYUwVQ9kQYlUwf7LjsZpSxcBXAAMQAAAABgXUBeLpWcE4WGeHNXm/qthckzO3CAWzcl53wfsKLX94x/GKSWTtjLf6ZVYCrcr9q2AAMzMwC2AAAABWQAIAAAAACRpJuh3Jnwljy7it3sjZvyoMgtq1+TkH0Aeh8WbPieiAVzACAAAAAAXlgGZv5fpM7q7W5gqLv/N2a8Cx5IOtyoLVHAo+wlHBQFbAAgAAAAAH71N+GhdVuwiHd7nzq85vcQXebS1yE6Kfw3zUEhd5zXBXAAMQAAAADY1EgDY6DDJ6cdIkY87bunhmvvZwUdKimylcgtsHbbfCujhm/R9PnZ1IEXo+WsDhC6AAMzNAC2AAAABWQAIAAAAACzA+oEP81uMPXznA3c7qV8pCA58lDR0NT/iEqyVfKzzAVzACAAAAAAQzLm/tjbyoPH+6/hjwx8Td4v60tHWQoWOxb0okJPZvgFbAAgAAAAAFqulIZBUKEDkjlYPIdjAuxA2P/4P5GskwrwcDU5Y+J2BXAAMQAAAABFLBfdvhm38dtcRq03HLv/POy3Y3MXYCt9Ej6ZIa8Rid6p6vCvk0Xctg6YrK8XkjcBAAMzNQC2AAAABWQAIAAAAACV4RD4O6SwNow2/kuPKHUcPfkrTMHHwdb5ykZy7mJS1QVzACAAAAAAmm5VzzTNscO9wo2t/i5aC1WqGPpHni+PAbcYSy2aDAEFbAAgAAAAADb7zFcql5cNOBjOKNBvweGAzKx4P19i2wexGXLCXh93BXAAMQAAAAAAsEfvBskXBYi0ZIMzC43cCsiMUNgg5iOPOs9zcE+frBGJTnOaO5lwd3fRDCfKr2PoAAMzNgC2AAAABWQAIAAAAADMttdZKNoZw8/nw9OXM4jISVdA2kzCaBUy/suUnD1iwgVzACAAAAAAfLEUMUZjCrpCx3XA1q1nD02ummV8ajILMuwfTZ+zLyAFbAAgAAAAAMYwVaNXQUuuqgdqTGaQHrQnxit+yeukxCTH48vYTLCQBXAAMQAAAABTqP4JnwQRdUZi5eG7H7mit1UuZMuejWfHE6A3elocJwu58JvmFqdIDL9Fb0u9VPbJAAMzNwC2AAAABWQAIAAAAADNkyjEAMD8H3MPKwbGKj/Ht04Poy+4vXfXu+iQvQDROgVzACAAAAAAhJNO2x0KuOd/j26Tqt7Vno4Ywodt2vDiKb2g0oq3AkkFbAAgAAAAABR3MlJ6rgv0Zfc2ijSbjGjDPwL/jf8E3DIz3tk1chsvBXAAMQAAAAD0Hwcr1N8f5pUMNHFGjPXxaQVZgoGUHeHQNP1reEzejswGNFUSpcJN2df4LI/EEpHVAAMzOAC2AAAABWQAIAAAAAD0P5YrYfTGO93Gx7KBthTBF80W6JgWl8qqji8y81J/3wVzACAAAAAAn3ZjoE1gg56PxBTSrn253VFbcA4Ji1cJ/Gga9OouHLQFbAAgAAAAAAJ2ajVGqAIjMgGrQqAhNqvS4JV9PlYTGwQITdPl/Ku8BXAAMQAAAACVyHMEeLeABPJQy7Xf8GROkpj/5e32bUqVExbEWRdrf5wLcKi8oamS1XbHuOhtzjv5AAMzOQC2AAAABWQAIAAAAACOALLoAAJm5ZmJGTmNZs44bkBbp5tOePwW8LFQZ1mqKgVzACAAAAAAsu3VFAVJHsgyASrvhpQL+O83bEMcsNaaTgrE044gU3sFbAAgAAAAAPj63mN/kWpgf0DrAUJSNpjZ8pfmU+ajEXCq7R2el/jpBXAAMQAAAABEhMw4ORJxy4cd/FlsRxztj26ReCVtyzEwa38rZkwGxi1FTgy3w2AWiFpYvJTYtMiwAAM0MAC2AAAABWQAIAAAAACyS96tIDD3xADZQ3ldblAf4OpFt+4YPC6r4vOD3FO/VgVzACAAAAAAoEUolLXX0tJLsrMyYY4rDncvNcR9/QHwlK11rZEg4OwFbAAgAAAAAKSzG8oIBM1BXenO4w7zNxDyTcbSjFQ+Qvcia8lowde8BXAAMQAAAAD2Kc8xpU0Ejay1z98AiXw4EZP9e4FPE9c703f2JzEPhi+2tQQ5VdmWAmveAYh2TuDkAAM0MQC2AAAABWQAIAAAAAAVi20GzI/brbE1bgiynbh1rQsleEeZEILkhSHCOjymMQVzACAAAAAArYmIGgfXl32cNKbht69CRKcnzODwImnQfePOgGp2QLsFbAAgAAAAADRf3eUUW9ViXkf/pVInJ198Umc8kwYLQL4FwKlBwkDHBXAAMQAAAAA741268zI9ZKz2Fn2+rwknB80KHmz+lKSQVKEeZQ02y4xeGTQMUUY/qscT22BhEgXwAAM0MgC2AAAABWQAIAAAAAByIr6BiD3k9JYrFuDBmeytqoMTztiFcdCNyRmq3U6ssQVzACAAAAAApT1M5XhIdWV1EE/ANEULVU87vQc8kn10l0IGgoutsQ8FbAAgAAAAAJMHNLDKWsOEs/z3qlTtkWTXKQiyKPl+evUezTStPZbABXAAMQAAAAAQASNUSEZV16xAygs+bJlZjCH9hailpHcyWCAaD9auHv6xD0zqa4lh68YrG9zDesU8AAM0MwC2AAAABWQAIAAAAADEW7z/hDrTEZcrA8/4KItlNNRjxmjg5a+C0Hh3yaF2VgVzACAAAAAAGPXrYasqJfW2E7yt8HecW/g2v3Kk1AFl3SkxawAnNZgFbAAgAAAAAF4lgAbQELFPtcEy6pDac6t5Sf9bCudWtETHf4DHQT+wBXAAMQAAAACj2nkdDIQ5VoOIp+U1ElOyzRaWP6spnL1OmY9Yb4QbAQoa/3kiiiILEo8AUrvC0lqOAAM0NAC2AAAABWQAIAAAAAB1UsLJPSe71Y6FnDh90JOJUyTBF1PDxfLKyG23Ri6/RwVzACAAAAAASTqVFk3Y6IqgrRGn1g8TqFhX/BgN+1DkzMGO+aoRHCMFbAAgAAAAAIxOdL5/7+YaOHqbp7GFiOBeUgDYLdnA5TsE0FfTail4BXAAMQAAAAAbHDrtrLL0zvjqylp3LXqQ+FSvBVDDn8bbBq5jENXt6k+eGNF+3A7rEyW+cK6Wwc02AAM0NQC2AAAABWQAIAAAAACPcSXrSzZ8S3CqiRI7JuUH0uPWiPPSZeiOQeTA2TFJiAVzACAAAAAAcOcETOxrqsv00OMl2pvTCDrtXTyUT18UQxc3wh1mAGIFbAAgAAAAABAu+gJuXen4f17cFzELTU1kOIrz5aMzOe1rXDPp5bRJBXAAMQAAAADx5Wiy6rkM8b9Aj9yyySfwWkn4s4Jb1zVMSN72ENFWYu1tyisyUUA44Z0SsQffCwp4AAM0NgC2AAAABWQAIAAAAAC1Tu+s1duJtpHgb7f5HQyV6Y2AaXXP5DBz3PnndACylQVzACAAAAAATCD4E3i71Su7uiQP2UzGc7WV0ZPKYxvlC98D/u8TxAMFbAAgAAAAAJ5JOxHPVOyFZdZ8QtkixsPu3n+y0JjM6NjpBW7lVditBXAAMQAAAACwzWerYAx8G8J9OpZnF7IGGMm3HEUg/JLu95pTFcHbNouyI7Qpxdks2hZKei+lNmCLAAM0NwC2AAAABWQAIAAAAABk/oAKerGN3euVTRweKtixhxNxjtqVYnaNPXTMA/zBUgVzACAAAAAAbKpxACdR6i9VMVy2HUkRUfFUFltJcHB8hlpyxU6l8xwFbAAgAAAAAAQHSf3WP5kdrz9vI3WKLooFLLYrXEhXulRXqM/pAkK2BXAAMQAAAABvBNGI2t2rzMhm3WCIAEsQ1XEVyBy810zFr6/0M1aqm1ZwN2F/4ZIVhHrRJdKi+EszAAM0OAC2AAAABWQAIAAAAAD4+u3fM/3at7tBpyYoU4m+eeJa6zY/Y5tbjm6BzuqelwVzACAAAAAAYLqrqoULNk9vAkVkRCL1czWakf6dR6NrNgFhfkUS1j0FbAAgAAAAAET0i4+dw59ptCy8ULqgZKtQxYyPlQBNvQv/ypEHvWEWBXAAMQAAAABmm2dHyYdHGwRXYn/bDkGO500gLqOpzQrw4QZ13zM2t8mwfxqk3JpxzvAh42Zlc60SAAM0OQC2AAAABWQAIAAAAABsTZ+uvJMs4zKiJNSGl3DvwjibNAweewC3tP0SC5wwyQVzACAAAAAAbhdyrk6xUUMD1OUaWKYPOduUupCD2ZXbVl+oLyZ7VS0FbAAgAAAAACSitMnP4DfMEiVsHPDj3XAe6bDF83FdmfDrGQigMdx+BXAAMQAAAADy+uEi4ykFbRbGQqzjD6y2NuwWisZt+5aMc4iPpYvGYtudHMQTpPowUaMhK3IvsdHmAAM1MAC2AAAABWQAIAAAAACjTueWwH/ET9RamAgbmwRKT2hiN7IKSMugpbXENZ62yAVzACAAAAAAMppd7TAqyAdm+Yna1T/9vRQWHJXpy9Vua6ejl2wC2XcFbAAgAAAAAGtC9edagBA7/ryn5K3zXNk2N9yN+2k1ZW7TYkfcugzMBXAAMQAAAAAAycs11ehab+YLEbpqNLTTLSPkcoTGrG8g8UzmNVUvv2ZVX4OjyBfoZul+h9I6ejrrAAM1MQC2AAAABWQAIAAAAACp9s1+0UAC3CMqjvOJnXcM9qm3JgWxPvLR65g8rV1fmwVzACAAAAAAN0hzS6P2ImuYJso+d0fa3+DhaPE4LO5W377PyCdW3vsFbAAgAAAAAPPcv3EDlStEEfK2LSacAcEDhcKaWnKBwhEGf1rZNnKjBXAAMQAAAAAf8jg/pE+pJMbybIazpDO8gGbhEfzDrEEa0odoRJ70NEahIyVa7MEia3o1yR2D9tPJAAM1MgC2AAAABWQAIAAAAAC6Q1TO1/uEj8mk/CGXILEKLRmCqHH36BB9n2u7Lxt9vQVzACAAAAAAJzWRA9hC1bKRlfuz053NuSkmZ7PC7U/+5XY98NZftQcFbAAgAAAAANGlYMIYg9PqAQZQSngCHv25tbDDqB6VvYYlKw8uuG+FBXAAMQAAAABaYZn2UYnFaBiHosa2f9K3CrkkWE320pqReqb4Km++P/SdgOHEDgBITIY9Vjpa5o1aAAM1MwC2AAAABWQAIAAAAADes0j8DL5m0zkzp2Ue3rABY6B0Rkr7HkrcfLV1wYkszgVzACAAAAAAhyiI8pZzN3dqiN1eJIm0CVSjVwTYL32cstDYPI7jNxsFbAAgAAAAAA8zMrJjY0lxhCfclQm01WM9CaQRXPZ+A4nU0tTY/ehABXAAMQAAAABtIJOpKqEkVmiolvHPYDjBJewP+v708lrIeQe8cb1rd2gjs5DTK90F7Kt5Dag/q2xtAAM1NAC2AAAABWQAIAAAAABVg85dbtBTN799DXTAxvD0ui6zK8piAQlKim/m0ASdnwVzACAAAAAATy3CKscXn9CkUxiWdlCZX7yYNKIRVGjAOLwoVdyxLU0FbAAgAAAAAHviUYTHciy8NfaffAACzgGcpdGNmd8fP5BeTlmEiIyrBXAAMQAAAADVtzJBXq5tybtNT8yi5KqU3QrDwmbRCTXHyGQMtKS/R+GzcIuy2FpQ8tabshKE5qLLAAM1NQC2AAAABWQAIAAAAAAa6yS30Z3tYsSU+TUWzgnkJZoO0muWJRSxlZFjfMPEzQVzACAAAAAAZK9S0f7ADAKMFDf7lbLJPjbXeR8OPPGpv4EjtrWDFuQFbAAgAAAAAAmaxOAU4yvBYhCNYcDYvX+khoOw+RSUCaYg1Wj8ciB7BXAAMQAAAAAELHbjHwoPOy8MqEcP8XAr5UuzasrS064lhBfz3L5bdjy4G3q+e5AnEJrxuEZwzUKQAAASc3AAAQAAAAAAAAAQdGYABgAAABJtbgAAAAAAAAAAABJteAAVgel99BAiEQA=",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "escCollection": "fle2.test.esc",
                "ecocCollection": "fle2.test.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "long",
                        "queries": {
                            "queryType": "range",
                            "contention": {
                                "$numberInt": "0"
                            },
                            "sparsity": {
                                "$numberInt": "1"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int64-v2/mongocryptd-reply.json000066400000000000000000000033721521103432300274670ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A4kAAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASA3YAKgAAABJ2AIdLa11U3CsAEm1pbgAAAAAAAAAAABJtYXgAFYHpffQQIhEAEmNtAAAAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "long",
                            "queries": {
                                "queryType": "range",
                                "contention": {
                                    "$numberInt": "0"
                                },
                                "sparsity": {
                                    "$numberInt": "1"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}
libmongocrypt-1.19.0/test/data/fle2-insert-range/int64/000077500000000000000000000000001521103432300225445ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-range/int64/RNG_DATA.h000066400000000000000000000173401521103432300241410ustar00rootroot00000000000000// Defines the random data for IVs to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\x50\x4a\x70\x7c\xc2\x7e\xf1\xd8\x65\xd2\x5c\x3d\x08\x17\xc3\xcf"                                                 \
    "\x42\x49\x8b\x63\xde\x0f\xaf\x47\xd9\x43\x7c\xb3\xc2\x68\x14\xb8"                                                 \
    "\xd1\xf8\x67\xfa\xd8\xe2\x38\x3e\x74\x3a\x7a\xed\xbe\xea\x5a\x8a"                                                 \
    "\xc5\xb0\x91\x96\x35\xb5\xa0\x90\x31\x29\xae\x2f\xa0\x8e\x7c\x80"                                                 \
    "\x0d\xa7\x53\x7c\xbf\x46\x25\xdd\xc0\x42\x51\xc7\x07\xc5\xa0\x4d"                                                 \
    "\xc1\x7d\x5c\x03\x46\xc3\xb9\xa5\x03\x59\xa2\x1b\x8b\x0b\x46\x93"                                                 \
    "\x27\xf0\x5e\xf2\xf3\x3e\x89\xcf\x28\xb8\x25\x3a\xa5\x23\x8c\xa2"                                                 \
    "\x69\xd6\x6c\xb4\xaf\x50\x32\x6e\x82\x33\x47\xf7\x1f\xd5\x86\x13"                                                 \
    "\x48\x4f\xef\xb6\x76\x2a\x07\xa7\x9c\x82\x26\xf6\x1d\x29\x9d\xfe"                                                 \
    "\xde\x21\x88\x61\x2f\x0e\x42\x19\x69\x46\xe6\xbe\xef\x14\x1e\x05"                                                 \
    "\x0f\xb5\xfe\xe1\xab\x3f\x7f\x8d\x36\x38\x6e\xea\xb7\xed\x4c\x33"                                                 \
    "\x35\x81\x15\x77\x99\x97\xc7\x51\xf5\x34\xbb\x16\x0b\xa4\xba\x30"                                                 \
    "\xe9\x40\x71\xb2\x55\xad\xd6\xbb\x6d\x8a\x1f\x2c\x27\xa5\xbb\xb0"                                                 \
    "\xfb\x6a\x42\x97\xb5\x4b\x9a\x63\x1d\x84\xa8\x24\x15\x62\xf1\xb2"                                                 \
    "\x1c\x9f\x2b\xa6\x53\x04\x45\x5e\xd5\x9a\xc7\x0c\x28\x83\x17\x42"                                                 \
    "\x4a\x94\x82\xbc\x8c\x19\xe7\xf2\xa4\xba\xdf\xb6\xfb\x2a\x90\x97"                                                 \
    "\xb8\xba\xa4\x79\xa9\x48\x2b\x65\xcf\x4f\xa9\x14\xc0\x51\x66\x88"                                                 \
    "\x3c\x86\x08\xed\xf3\x8f\xfa\xbf\x60\x81\xf4\xde\x89\xc3\x39\xce"                                                 \
    "\xd8\x05\x8a\x18\x9f\x46\xe2\xa9\x5b\x6c\xac\x6f\x61\x5f\xf2\x28"                                                 \
    "\xde\xcc\xa7\x27\x4a\xea\x7f\xd4\xc4\x15\xa5\x33\x94\xe8\x08\x42"                                                 \
    "\x9b\x23\xa1\xf6\x9b\xd0\x14\x05\xe1\xf7\x63\x6a\x21\x76\xd4\x18"                                                 \
    "\x2b\xe3\xa1\xbe\x17\xd6\xb8\x26\x61\xf6\xec\xe1\x22\x6e\xdb\x94"                                                 \
    "\xfd\x5c\x68\x13\x17\x8a\xd1\x86\x68\x1c\x8e\xaf\xea\x1b\xc7\xf6"                                                 \
    "\x0a\xa2\x7d\x25\xf5\x4e\x50\xb3\x38\xe6\xb7\x20\xcc\xcc\x5f\x4b"                                                 \
    "\x37\xe6\x4e\x3b\x70\x75\x7f\xe7\x32\x27\xc3\x25\x09\xa2\x54\xb8"                                                 \
    "\x3b\x79\x7d\x93\xd2\x98\x6d\x03\xfb\x35\xe0\xed\xb1\x28\x75\x93"                                                 \
    "\x49\xa3\x69\x26\xb1\x06\xcc\xd9\x2b\x4a\xa8\x6a\x81\x3f\xae\xf0"                                                 \
    "\x89\x1a\x07\x74\x73\x28\x00\x79\x82\xa4\x13\x55\xcc\x3d\xab\x28"                                                 \
    "\xc5\xb6\xdc\xf8\xa2\xf9\x59\x66\xd6\x95\xe0\xc5\x45\x25\x60\x9d"                                                 \
    "\xd0\x73\xb2\xc0\x41\x2d\xd8\xc9\xbb\xdb\xd0\xf3\x78\x9a\x8a\xf5"                                                 \
    "\x20\x7b\x91\x4f\xc8\x60\x61\x5e\x5d\x1b\xaa\x16\x6f\xa1\x42\x82"                                                 \
    "\x5c\x3f\xd0\x1b\x41\xf7\xdd\x8e\xac\xcf\xd0\x27\x50\xce\xbc\x6e"                                                 \
    "\xcd\x2b\x9f\xed\x82\xc7\xfc\xfa\xaa\x37\x06\xaa\x8d\x19\xdc\x20"                                                 \
    "\x71\xf4\x75\x64\x3d\x28\xfc\x7e\xa9\x89\xe9\x41\x01\x94\x17\xf1"                                                 \
    "\x60\x5d\x40\x5e\x2e\x95\x9c\x13\x85\x86\x78\x73\x57\x9b\xfa\xad"                                                 \
    "\xd8\xd4\x48\x03\x63\xa0\xc3\x27\xa7\x1d\x22\x46\x3c\xed\xbb\xa7"                                                 \
    "\x45\x2c\x17\xdd\xbe\x19\xb7\xf1\xdb\x5c\x46\xad\x37\x1c\xbb\xff"                                                 \
    "\x00\xb0\x47\xef\x06\xc9\x17\x05\x88\xb4\x64\x83\x33\x0b\x8d\xdc"                                                 \
    "\x53\xa8\xfe\x09\x9f\x04\x11\x75\x46\x62\xe5\xe1\xbb\x1f\xb9\xa2"                                                 \
    "\xf4\x1f\x07\x2b\xd4\xdf\x1f\xe6\x95\x0c\x34\x71\x46\x8c\xf5\xf1"                                                 \
    "\x95\xc8\x73\x04\x78\xb7\x80\x04\xf2\x50\xcb\xb5\xdf\xf0\x64\x4e"                                                 \
    "\x44\x84\xcc\x38\x39\x12\x71\xcb\x87\x1d\xfc\x59\x6c\x47\x1c\xed"                                                 \
    "\xf6\x29\xcf\x31\xa5\x4d\x04\x8d\xac\xb5\xcf\xdf\x00\x89\x7c\x38"                                                 \
    "\x3b\xe3\x5d\xba\xf3\x32\x3d\x64\xac\xf6\x16\x7d\xbe\xaf\x09\x27"                                                 \
    "\x10\x01\x23\x54\x48\x46\x55\xd7\xac\x40\xca\x0b\x3e\x6c\x99\x59"                                                 \
    "\xa3\xda\x79\x1d\x0c\x84\x39\x56\x83\x88\xa7\xe5\x35\x12\x53\xb2"                                                 \
    "\x1b\x1c\x3a\xed\xac\xb2\xf4\xce\xf8\xea\xca\x5a\x77\x2d\x7a\x90"                                                 \
    "\xf1\xe5\x68\xb2\xea\xb9\x0c\xf1\xbf\x40\x8f\xdc\xb2\xc9\x27\xf0"                                                 \
    "\xb0\xcd\x67\xab\x60\x0c\x7c\x1b\xc2\x7d\x3a\x96\x67\x17\xb2\x06"                                                 \
    "\x6f\x04\xd1\x88\xda\xdd\xab\xcc\xc8\x66\xdd\x60\x88\x00\x4b\x10"                                                 \
    "\x66\x9b\x67\x47\xc9\x87\x47\x1b\x04\x57\x62\x7f\xdb\x0e\x41\x8e"                                                 \
    "\xf2\xfa\xe1\x22\xe3\x29\x05\x6d\x16\xc6\x42\xac\xe3\x0f\xac\xb6"                                                 \
    "\x00\xc9\xcb\x35\xd5\xe8\x5a\x6f\xe6\x0b\x11\xba\x6a\x34\xb4\xd3"                                                 \
    "\x1f\xf2\x38\x3f\xa4\x4f\xa9\x24\xc6\xf2\x6c\x86\xb3\xa4\x33\xbc"                                                 \
    "\x5a\x61\x99\xf6\x51\x89\xc5\x68\x18\x87\xa2\xc6\xb6\x7f\xd2\xb7"                                                 \
    "\x6d\x20\x93\xa9\x2a\xa1\x24\x56\x68\xa8\x96\xf1\xcf\x60\x38\xc1"                                                 \
    "\xd5\xb7\x32\x41\x5e\xae\x6d\xc9\xbb\x4d\x4f\xcc\xa2\xe4\xaa\x94"                                                 \
    "\x04\x2c\x76\xe3\x1f\x0a\x0f\x3b\x2f\x0c\xa8\x47\x0f\xf1\x70\x2b"                                                 \
    "\x94\xca\xf5\xcb\x42\x0a\xf6\x94\xb6\xb2\x74\xeb\x1e\xeb\xc8\xdc"                                                 \
    "\xe4\x0e\xf4\x25\x32\xa8\x44\xea\xe0\x2d\xa5\x4b\x1b\xbc\xa0\x7f"                                                 \
    "\x23\x7d\x77\xc9\xc3\xd3\x3c\x91\x41\x82\x6e\x18\xc2\xd8\xaf\x10"                                                 \
    "\x79\x94\xe3\x1d\x95\x06\x71\x5d\x89\x91\x2f\xe2\x5a\xb9\x69\x5a"                                                 \
    "\xed\xf6\x40\x6b\xf4\xd2\xe8\xf5\x86\x78\xf5\x28\xfc\x74\x3e\x1e"                                                 \
    "\x31\xf0\xfe\xf5\x07\x37\x99\xfa\x60\x92\x94\x24\xf0\xfd\x86\xe5"
libmongocrypt-1.19.0/test/data/fle2-insert-text-search-preview/000077500000000000000000000000001521103432300244125ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-preview/cmd.json000066400000000000000000000001551521103432300260510ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": "value123"
      }
   ]
}encrypted-field-map-prefixPreview.json000066400000000000000000000010641521103432300337150ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-preview{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "prefixPreview",
               "contention": {
                  "$numberLong": "0"
               }
            }
         }
      ]
   }
}
encrypted-field-map-suffixPreview.json000066400000000000000000000010641521103432300337240ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-preview{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "suffixPreview",
               "contention": {
                  "$numberLong": "0"
               }
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-text-search-with-str-encode-version/000077500000000000000000000000001521103432300274305ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-with-str-encode-version/cmd.json000066400000000000000000000001551521103432300310670ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": "value123"
      }
   ]
}encrypted-field-map.json000066400000000000000000000014251521103432300340770ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-with-str-encode-version{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "ssn",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ],
      "strEncodeVersion": 1
   }
}encrypted-payload.json000066400000000000000000000032641521103432300336750ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-with-str-encode-version{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": {
            "$binary": {
               "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
               "subType": "06"
            }
         }
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "fle2.test.esc",
            "ecocCollection": "fle2.test.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "ssn",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "substringPreview",
                     "contention": {
                           "$numberLong": "0"
                     },
                     "strMaxLength": 100,
                     "strMinQueryLength": 5,
                     "strMaxQueryLength": 20,
                     "caseSensitive": false,
                     "diacriticSensitive": true
                  }
               }
            ],
            "strEncodeVersion": 1
         }
      }
   }
}
mongocryptd-reply.json000066400000000000000000000034531521103432300337470ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search-with-str-encode-version{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "_id": 1,
                "ssn": {
                    "$binary": {
                        "base64": "A2EAAAAQdAABAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
                        "subType": "06"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                   "escCollection": "fle2.test.esc",
                   "ecocCollection": "fle2.test.ecoc",
                   "fields": [
                      {
                         "keyId": {
                            "$binary": {
                               "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                               "subType": "04"
                            }
                         },
                         "path": "ssn",
                         "bsonType": "string",
                         "queries": {
                            "queryType": "substringPreview",
                            "contention": {
                                  "$numberLong": "0"
                            },
                            "strMaxLength": 100,
                            "strMinQueryLength": 5,
                            "strMaxQueryLength": 20,
                            "caseSensitive": false,
                            "diacriticSensitive": true
                         }
                      }
                   ],
                   "strEncodeVersion": 1
                }
             }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-text-search/000077500000000000000000000000001521103432300227335ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-text-search/RNG_DATA.h000066400000000000000000000201071521103432300243230ustar00rootroot00000000000000// Defines the random data for to obtain a deterministic
// FLE2InsertUpdatePayload.
#define RNG_DATA                                                                                                       \
    "\xc1\xa1\x03\x4e\x80\xc5\x22\xf1\x6b\x0c\xa9\x39\x2f\xda\x68\x88"                                                 \
    "\xc0\x05\x26\x23\xfb\xa7\x85\x1e\xf8\x95\x4c\x23\xfe\x13\x26\x72"                                                 \
    "\x67\xa3\x19\xe1\xb7\x2c\x24\xef\x36\xa0\x14\x16\xc8\x57\xf0\xee"                                                 \
    "\xc9\xee\x1c\xa7\x25\x8e\x07\xe1\x47\x25\x72\x03\x32\x70\x7a\x9a"                                                 \
    "\x1c\x9d\x9c\x3f\x0c\xbd\xe3\x13\xff\x27\xba\xdd\xaa\x8c\x08\xa4"                                                 \
    "\x6b\x2f\xad\x5d\xce\x5f\xca\xaf\x75\xc3\xd9\xff\xe4\x5c\x36\xb9"                                                 \
    "\x02\xd9\xc9\x6e\xee\x43\x49\x28\x4e\x76\x94\xe9\x5a\xb9\x46\xb5"                                                 \
    "\x7c\xe2\xb7\x67\x1f\xfc\x02\xc6\xdd\xc9\x82\x26\x00\xd1\xd6\x67"                                                 \
    "\xe1\x89\x54\x31\x24\x9c\xb1\x3f\xd0\xd9\xde\xe4\x63\xd9\x9e\xd5"                                                 \
    "\x5a\xb7\x20\x26\x30\xab\x48\xbd\xac\x23\x5f\x95\x4b\x96\x17\x4f"                                                 \
    "\xd5\x97\x4d\xdc\x24\xd7\x46\xd8\x73\x6d\x95\x3f\x0a\x51\xdb\x60"                                                 \
    "\xe7\x60\xfc\x68\x5b\x78\x8e\x02\x19\x85\x44\x90\xb7\xe5\x60\xa4"                                                 \
    "\x8a\x93\x06\x13\xcb\x1d\x3d\x66\xba\x01\x26\x4b\x0c\x4d\xcf\x69"                                                 \
    "\x7a\xde\xb9\x2e\x40\xd1\x3b\x73\x7c\x6a\x9e\x3e\x1f\xa5\xff\x20"                                                 \
    "\x4e\x94\xd0\x1d\x7c\x9e\x22\xbe\xa2\xb1\xb5\xa2\xff\xf5\x47\xeb"                                                 \
    "\x59\xe7\xc6\x4a\x67\x0f\x0e\x7c\xef\x9a\x6f\x49\x63\xe5\x64\x2f"                                                 \
    "\xcc\x73\x63\xdd\x36\x28\x03\x4c\x9b\x7e\xd2\x30\x83\xce\x0b\xb1"                                                 \
    "\x20\x30\x83\xbf\x0c\x21\x44\x3d\xc3\x73\x42\x49\x57\x47\xcc\xfc"                                                 \
    "\x97\xa9\x1a\x8b\xd2\xc2\x2d\xe9\xbf\x14\xe2\x9a\x05\xc3\x27\xe9"                                                 \
    "\xa4\x1d\x55\x4f\xea\x78\x92\x81\xdb\x9e\x9e\x7d\x14\x12\xd3\x48"                                                 \
    "\x93\xec\xdd\xdf\x4e\xc3\x78\x35\x20\x7a\xde\xa0\x3a\xee\xd5\x8a"                                                 \
    "\xef\x0b\x39\x7d\x8f\xfd\x43\x50\xe2\x42\x0d\x76\xd8\xb9\x42\x0a"                                                 \
    "\x3d\xfe\xbd\x51\x78\x97\x88\xb8\x2f\xf9\x32\x4d\x0f\x70\x58\xf0"                                                 \
    "\xf9\xdf\xab\xc0\x58\x28\x9e\x8f\x7c\x34\x45\x4c\xc1\xc5\x18\x08"                                                 \
    "\xc9\x66\xf1\xfe\xea\xfb\x50\x6a\xbc\x85\x19\x10\x79\x64\x76\xf6"                                                 \
    "\xfa\x5b\xa4\xe7\x3b\xcc\x39\x35\x7d\x2c\xd8\x5f\x8f\xf2\xa1\x93"                                                 \
    "\x15\x34\x87\xc6\x01\xa7\xa0\x00\x94\x8c\x02\x31\x25\x5c\xe9\x97"                                                 \
    "\x9e\x3f\x0c\x72\xe6\x0f\x7f\x21\xe0\xb9\x17\x22\x33\xba\x69\x0c"                                                 \
    "\x9f\x61\xb8\x8d\x6b\x2f\x86\xa7\x2f\xbf\x7b\xec\x82\x8e\x58\x34"                                                 \
    "\xb2\x85\xe2\xaf\xf2\xd4\xa5\x7a\xba\x42\x17\xb5\xc8\xfc\xf4\x49"                                                 \
    "\x65\xf0\xc4\xd5\xbd\xe6\x3d\xba\x91\x36\x5a\x84\x08\x79\x7e\xc8"                                                 \
    "\xe0\x74\xa2\x34\x1a\xa6\xda\x1a\x84\x68\xb7\xa2\x66\x40\xd8\x1f"                                                 \
    "\x48\x81\x47\xaa\xe1\x47\x05\xac\x84\xde\x15\x5b\xdf\x2e\x6b\xdd"                                                 \
    "\x69\xaf\x9e\xca\xed\x99\xaf\x7d\x04\xb3\x8d\xcc\xf4\xa5\x7a\x6f"                                                 \
    "\x7b\x37\x72\x53\xc2\xe0\xbd\x08\x63\xb3\xa3\x12\x84\x25\x55\x1c"                                                 \
    "\x37\x7b\x77\x71\x6c\x19\x27\x27\x03\x97\x89\xaa\x93\x75\xf9\x06"                                                 \
    "\x69\x1a\x22\x7d\x35\x66\x3a\xe7\x72\x91\x23\xfb\x39\x00\xae\xa9"                                                 \
    "\x08\xe7\xae\x2a\xb7\xc2\x4d\xd6\x72\x15\xee\x70\xd1\xaf\x4f\x4d"                                                 \
    "\x22\xe6\xc8\x79\xd7\x41\xb2\x28\x5a\xa2\x3e\x0d\xbd\x33\xac\x7d"                                                 \
    "\x50\xcb\x96\xb5\xc2\x49\xc5\x7d\xd3\x36\x7c\xda\x6a\xc4\x54\xfa"                                                 \
    "\x3f\x7c\xa4\xde\xfe\x60\x2c\x89\xc4\x79\xe4\x31\xdd\xb7\xa1\x76"                                                 \
    "\x31\x43\xe1\xbb\xb8\x56\x78\x1e\x2d\xac\xe7\xff\x34\x8f\xc6\x36"                                                 \
    "\x2c\x6f\x43\x65\x2f\x7f\xd1\xe3\xd1\x89\xf5\xe9\x2b\xd4\x58\xd7"                                                 \
    "\xaf\x1b\xb4\xfb\xec\x61\xf0\xf3\x8f\x53\x9c\xaa\x31\x69\x17\x7d"                                                 \
    "\xca\x6e\x1b\xba\x0d\x60\x43\x4d\x35\x4b\x23\x88\x4a\x51\x29\x7e"                                                 \
    "\xfd\xb6\xe0\xd4\xcb\x94\x8b\xd3\x65\x22\x07\x9b\x39\xea\x46\xff"                                                 \
    "\x5f\xf2\x4e\xee\x2e\x8a\x8d\xe3\x5d\xc7\xa4\xfc\xd4\xec\xb3\xc1"                                                 \
    "\xe7\xbe\x19\x97\x7a\x7a\xc6\x2e\x0b\xcd\xc0\x94\x87\xcd\x5f\xc6"                                                 \
    "\x37\x08\x01\xf3\x38\x90\xb5\x77\x97\xdd\xc4\x42\xa9\xb5\xe6\xdb"                                                 \
    "\x7d\x4b\xfe\x4f\x5d\xc1\xe1\x5d\xde\x3d\x6a\x59\x70\x65\xb6\xeb"                                                 \
    "\xce\x0c\xdf\x97\x88\xee\x72\x08\x64\xd9\x09\x44\x23\x66\x3b\xc4"                                                 \
    "\x2c\x98\xfa\xfd\xd8\x6a\x04\xb9\xd1\xfc\xbf\xcb\xe1\xc1\x77\x3f"                                                 \
    "\x42\x84\xc2\x13\x14\xff\x56\x4c\xf3\x40\x87\xb5\x13\x0d\x06\x38"                                                 \
    "\x37\xc5\x15\xb6\xfd\x23\x80\x6e\x32\x59\xa1\x83\x2d\x73\xbb\xd5"                                                 \
    "\x4e\xfd\x2d\x37\x09\x38\xcd\x3d\xa5\xef\xe1\x0f\xe5\x36\xa9\x8d"                                                 \
    "\xbb\x27\xd4\x81\x5e\xdf\x63\x85\x67\x07\xed\x04\x84\x26\x82\x48"                                                 \
    "\x85\x83\xed\x8b\xe8\x71\x29\x96\x71\x00\xc5\x75\x5b\xe3\x4f\x4d"                                                 \
    "\xef\x2f\xfa\x88\xf2\x9a\xd0\xe3\xb8\x38\xdc\xd3\x12\x7a\x21\x65"                                                 \
    "\x38\xdc\xb9\x36\x86\x23\xa9\xbc\xa5\x4f\x50\xc2\xb6\x3d\x40\xd4"                                                 \
    "\xdc\x5b\x35\xf9\x89\xb0\x45\x7f\x3d\x2a\x61\xfd\x0e\xec\x00\x52"                                                 \
    "\x71\xa5\x1f\xe5\x49\xe0\x26\xf9\xd0\x2a\x1d\x5f\x3f\x9e\x82\x07"                                                 \
    "\xfb\xf7\xdb\xa1\x87\x56\xd6\x13\x4b\x2c\x8e\x73\xbc\x56\x98\x1d"                                                 \
    "\xae\xe6\x9d\xab\x15\x83\x0e\x0a\x4c\x3c\xd0\x11\x41\x96\xc0\x56"                                                 \
    "\x94\x7a\x1e\x0b\xc8\xec\x23\xa3\x9d\x6d\x01\x01\x22\xa7\xbd\x96"                                                 \
    "\x51\xb0\x6a\xab\x98\x7c\xcd\xd3\xff\xf0\x67\x63\x16\xf2\x2f\x62"                                                 \
    "\x3c\xbd\x8b\x4a\x49\xb0\xe4\xe3\x12\xd4\x6b\x12\x41\x32\x22\xc0"                                                 \
    "\x51\x0c\xaf\x7e\x01\x46\x2b\xd5\x50\x9c\xd6\xfd\x42\xb5\x11\x4b"
libmongocrypt-1.19.0/test/data/fle2-insert-text-search/cmd.json000066400000000000000000000001551521103432300243720ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": "value123"
      }
   ]
}libmongocrypt-1.19.0/test/data/fle2-insert-text-search/encrypted-field-map.json000066400000000000000000000013701521103432300274600ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "ssn",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ]
   }
}libmongocrypt-1.19.0/test/data/fle2-insert-text-search/encrypted-payload.json000066400000000000000000000032641521103432300272570ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": {
            "$binary": {
               "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
               "subType": "06"
            }
         }
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "fle2.test.esc",
            "ecocCollection": "fle2.test.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "ssn",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "substringPreview",
                     "contention": {
                           "$numberLong": "0"
                     },
                     "strMaxLength": 100,
                     "strMinQueryLength": 5,
                     "strMaxQueryLength": 20,
                     "caseSensitive": false,
                     "diacriticSensitive": true
                  }
               }
            ],
            "strEncodeVersion": 1
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-text-search/mongocryptd-reply.json000066400000000000000000000034531521103432300273310ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "_id": 1,
                "ssn": {
                    "$binary": {
                        "base64": "A2EAAAAQdAABAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
                        "subType": "06"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                   "escCollection": "fle2.test.esc",
                   "ecocCollection": "fle2.test.ecoc",
                   "fields": [
                      {
                         "keyId": {
                            "$binary": {
                               "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                               "subType": "04"
                            }
                         },
                         "path": "ssn",
                         "bsonType": "string",
                         "queries": {
                            "queryType": "substringPreview",
                            "contention": {
                                  "$numberLong": "0"
                            },
                            "strMaxLength": 100,
                            "strMinQueryLength": 5,
                            "strMaxQueryLength": 20,
                            "caseSensitive": false,
                            "diacriticSensitive": true
                         }
                      }
                   ],
                   "strEncodeVersion": 1
                }
             }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-unindexed-v2/000077500000000000000000000000001521103432300230145ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-unindexed-v2/cmd.json000066400000000000000000000001771521103432300244570ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "plainText":"sample",
         "encrypted":"value123"
      }
   ]
}
libmongocrypt-1.19.0/test/data/fle2-insert-unindexed-v2/encrypted-field-map.json000066400000000000000000000006771521103432300275520ustar00rootroot00000000000000{
    "db.test": {
        "escCollection": "fle2.test.esc",
        "ecocCollection": "fle2.test.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string"
            }
        ]
    }
}libmongocrypt-1.19.0/test/data/fle2-insert-unindexed-v2/encrypted-payload.json000066400000000000000000000005251521103432300273350ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "plainText": "sample",
         "encrypted": {
            "$binary": {
               "base64": "EKvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFVl3jjP90PgD4T+Mtubn/mm4CKsKGaV1yxlic9Dty1Adef4Y+bsLGKhBbCa5eojM/A==",
               "subType": "06"
            }
         }
      }
   ]
}libmongocrypt-1.19.0/test/data/fle2-insert-unindexed-v2/mongocryptd-reply.json000066400000000000000000000024121521103432300274040ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A2EAAAAQdAABAAAAEGEAAQAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
                        "subType": "6"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "string"
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-v2-with-str-encode-version/000077500000000000000000000000001521103432300255305ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-v2-with-str-encode-version/cmd.json000066400000000000000000000001551521103432300271670ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": "value123"
      }
   ]
}libmongocrypt-1.19.0/test/data/fle2-insert-v2-with-str-encode-version/encrypted-field-map.json000066400000000000000000000010171521103432300322530ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "ssn",
            "bsonType": "string",
            "queries": {
               "queryType": "equality",
               "contention": 0
            }
         }
      ],
      "strEncodeVersion": 1
   }
}libmongocrypt-1.19.0/test/data/fle2-insert-v2-with-str-encode-version/encrypted-payload.json000066400000000000000000000026041521103432300320510ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": {
            "$binary": {
               "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
               "subType": "06"
            }
         }
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "fle2.test.esc",
            "ecocCollection": "fle2.test.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "ssn",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ],
            "strEncodeVersion": 1
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-v2-with-str-encode-version/mongocryptd-reply.json000066400000000000000000000031261521103432300321230ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "_id": 1,
                "ssn": {
                    "$binary": {
                        "base64": "A2EAAAAQdAABAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
                        "subType": "06"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "ssn",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                    "$numberInt": "0"
                                }
                            }
                        }
                    ],
                    "strEncodeVersion": 1
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-insert-v2/000077500000000000000000000000001521103432300210335ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-insert-v2/cmd.json000066400000000000000000000001551521103432300224720ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": "value123"
      }
   ]
}libmongocrypt-1.19.0/test/data/fle2-insert-v2/encrypted-field-map.json000066400000000000000000000007621521103432300255640ustar00rootroot00000000000000{
   "db.test": {
      "escCollection": "fle2.test.esc",
      "ecocCollection": "fle2.test.ecoc",
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "ssn",
            "bsonType": "string",
            "queries": {
               "queryType": "equality",
               "contention": 0
            }
         }
      ]
   }
}libmongocrypt-1.19.0/test/data/fle2-insert-v2/encrypted-payload.json000066400000000000000000000025411521103432300253540ustar00rootroot00000000000000{
   "insert": "test",
   "documents": [
      {
         "_id": 1,
         "ssn": {
            "$binary": {
               "base64": "C18BAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVwADAAAAAAx0PWdXaep4jV5cRA2yQN+ULLwjv8e++oMonpfGOGs9BZ0uqPP7waiwZSwHsDx57+BXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AFAAAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSHYikH9u4e644rfZY9N9UQR4h76qKAmcbo43utRcXMQy+FXXIxSuNntFHZHTcNJhJoFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsBWwAIAAAAABpn2zcb7jOd/FK3F45nBxnLU6HOMwZzmGOZ0w35v/DqRJrAAAAAAAAAAAAAA==",
               "subType": "06"
            }
         }
      }
   ],
   "encryptionInformation": {
      "type": 1,
      "schema": {
         "db.test": {
            "escCollection": "fle2.test.esc",
            "ecocCollection": "fle2.test.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                     }
                  },
                  "path": "ssn",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/fle2-insert-v2/mongocryptd-reply.json000066400000000000000000000030531521103432300254250ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "insert": "test",
        "documents": [
            {
                "_id": 1,
                "ssn": {
                    "$binary": {
                        "base64": "A2EAAAAQdAABAAAAEGEAAgAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAASrze+rEjSYdhI0EjRWeJASAnYACQAAAHZhbHVlMTIzABJjbQAAAAAAAAAAAAA=",
                        "subType": "06"
                    }
                }
            }
        ],
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "escCollection": "fle2.test.esc",
                    "ecocCollection": "fle2.test.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "ssn",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                    "$numberInt": "0"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version/000077500000000000000000000000001521103432300335135ustar00rootroot00000000000000cmd-to-mongocryptd.json000066400000000000000000000033301521103432300400540ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version{
    "create": "coll",
    "encryptedFields": {
      "fields": [
        {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
        }
      ],
      "strEncodeVersion": 1
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "string",
                        "queries": {
                           "queryType": "substringPreview",
                           "contention": {
                                 "$numberLong": "0"
                           },
                           "strMaxLength": 100,
                           "strMinQueryLength": 5,
                           "strMaxQueryLength": 20,
                           "caseSensitive": false,
                           "diacriticSensitive": true
                        }
                    }
                ],
                "strEncodeVersion": 1
            }
        }
    }
}
cmd.json000066400000000000000000000010711521103432300350710ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ],
      "strEncodeVersion": 1
   }
}encrypted-field-config-map.json000066400000000000000000000015541521103432300414300ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version{
    "db.coll": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                   "queryType": "substringPreview",
                   "contention": {
                         "$numberLong": "0"
                   },
                   "strMaxLength": 100,
                   "strMinQueryLength": 5,
                   "strMaxQueryLength": 20,
                   "caseSensitive": false,
                   "diacriticSensitive": true
                }
            }
        ],
        "strEncodeVersion": 1
    }
}
encrypted-payload.json000066400000000000000000000010711521103432300377520ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ],
      "strEncodeVersion": 1
   }
}mongocryptd-reply.json000066400000000000000000000040311521103432300400230ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection-with-str-encode-version{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
          "fields": [
            {
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                   "queryType": "substringPreview",
                   "contention": {
                         "$numberLong": "0"
                   },
                   "strMaxLength": 100,
                   "strMinQueryLength": 5,
                   "strMaxQueryLength": 20,
                   "caseSensitive": false,
                   "diacriticSensitive": true
                }
            }
          ],
          "strEncodeVersion": 1
       },
       "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "string",
                            "queries": {
                               "queryType": "substringPreview",
                               "contention": {
                                     "$numberLong": "0"
                               },
                               "strMaxLength": 100,
                               "strMinQueryLength": 5,
                               "strMaxQueryLength": 20,
                               "caseSensitive": false,
                               "diacriticSensitive": true
                            }
                        }
                    ],
                    "strEncodeVersion": 1
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection/000077500000000000000000000000001521103432300270165ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection/cmd-to-mongocryptd.json000066400000000000000000000032731521103432300334440ustar00rootroot00000000000000{
    "create": "coll",
    "encryptedFields": {
      "fields": [
        {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
        }
      ]
   },
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "escCollection": "esc",
                "ecocCollection": "ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "string",
                        "queries": {
                           "queryType": "substringPreview",
                           "contention": {
                                 "$numberLong": "0"
                           },
                           "strMaxLength": 100,
                           "strMinQueryLength": 5,
                           "strMaxQueryLength": 20,
                           "caseSensitive": false,
                           "diacriticSensitive": true
                        }
                    }
                ],
                "strEncodeVersion": 1
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection/cmd.json000066400000000000000000000010341521103432300304520ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ]
   }
}encrypted-field-config-map.json000066400000000000000000000015151521103432300347300ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection{
    "db.coll": {
        "escCollection": "esc",
        "ecocCollection": "ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                        "subType": "04"
                    }
                },
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                   "queryType": "substringPreview",
                   "contention": {
                         "$numberLong": "0"
                   },
                   "strMaxLength": 100,
                   "strMinQueryLength": 5,
                   "strMaxQueryLength": 20,
                   "caseSensitive": false,
                   "diacriticSensitive": true
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection/encrypted-payload.json000066400000000000000000000010711521103432300333340ustar00rootroot00000000000000{
   "create": "coll",
   "encryptedFields": {
      "fields": [
         {
            "path": "encrypted",
            "bsonType": "string",
            "queries": {
               "queryType": "substringPreview",
               "contention": {
                     "$numberLong": "0"
               },
               "strMaxLength": 100,
               "strMinQueryLength": 5,
               "strMaxQueryLength": 20,
               "caseSensitive": false,
               "diacriticSensitive": true
            }
         }
      ],
      "strEncodeVersion": 1
   }
}libmongocrypt-1.19.0/test/data/fle2-text-search-create-encrypted-collection/mongocryptd-reply.json000066400000000000000000000037701521103432300334160ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    },
    "result": {
        "create": "coll",
        "encryptedFields": {
          "fields": [
            {
                "path": "encrypted",
                "bsonType": "string",
                "queries": {
                   "queryType": "substringPreview",
                   "contention": {
                         "$numberLong": "0"
                   },
                   "strMaxLength": 100,
                   "strMinQueryLength": 5,
                   "strMaxQueryLength": 20,
                   "caseSensitive": false,
                   "diacriticSensitive": true
                }
            }
          ]
       },
       "encryptionInformation": {
            "type": 1,
            "schema": {
                "db.coll": {
                    "escCollection": "esc",
                    "ecocCollection": "ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "string",
                            "queries": {
                               "queryType": "substringPreview",
                               "contention": {
                                     "$numberLong": "0"
                               },
                               "strMaxLength": 100,
                               "strMinQueryLength": 5,
                               "strMaxQueryLength": 20,
                               "caseSensitive": false,
                               "diacriticSensitive": true
                            }
                        }
                    ],
                    "strEncodeVersion": 1
                }
            }
        }
    },
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/iev-v2/000077500000000000000000000000001521103432300174645ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/iev-v2/FLECrudTest-insertOneRangeV2.json000066400000000000000000000053521521103432300256010ustar00rootroot00000000000000{
	"type": "range",
	"payload": "0f12345678123498761234123456789012100566a74bf7c78e62d0e6e4b49667b2cb8928f44640e36bc6129d6c20ebcc00b0fd90b4ff50a063051a4009e62730a517a8d574f09be4b277c047c74907071385be8caaaadf99885f126e88db7debd816b2a08e34585d3d0de2659c121e5887520c34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
	"S_KeyId": "12345678123498761234123456789012",
	"S_Key": "f502e66502ff5d4559452ce928a0f08557a9853c4dfeacca77cff482434f0ca1251fbd60d200bc35f24309521b45ad781b2d3c4df788cacef3c0e7beca8170b6cfc514ecfcf27e217ed697ae65c08272886324def514b14369c7c60414e80f22",
	"K_KeyId": "ABCDEFAB123498761234123456789012",
	"K_Key": "cbebdf05fe16099fef502a6d045c1cbb77d29d2fe19f51aec5079a81008305d8868358845d2e3ab38e4fa9cbffcd651a0fc07201d7c9ed9ca3279bfa7cd673ec37b362a0aaa92f95062405a999afd49e4b1f7f818f766c49715407011ac37fa9",
	"bson_value_type": 16,
	"bson_value": "05000000",
	"metadata":
	[
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc"
	]
}libmongocrypt-1.19.0/test/data/iev-v2/FLECrudTest-insertOneText.json000066400000000000000000000055021521103432300252560ustar00rootroot00000000000000{
	"type": "text",
	"payload": "11123456781234987612341234567890120205000000020000000100000066a032d1b48e5e9618969290c4260ca0988f9e7d4b1b04e884148c3c2d7e1bdddb1231c09687ce7a794107d9fddb1bac3bfa24cb42d0297c16ad4406960786bebf9ae9d709ce2a8982bf78b53e5ee45610762a6276ceb7d7e72dad8e8beb357d34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
	"S_KeyId": "12345678123498761234123456789012",
	"S_Key": "f502e66502ff5d4559452ce928a0f08557a9853c4dfeacca77cff482434f0ca1251fbd60d200bc35f24309521b45ad781b2d3c4df788cacef3c0e7beca8170b6cfc514ecfcf27e217ed697ae65c08272886324def514b14369c7c60414e80f22",
	"K_KeyId": "ABCDEFAB123498761234123456789012",
	"K_Key": "cbebdf05fe16099fef502a6d045c1cbb77d29d2fe19f51aec5079a81008305d8868358845d2e3ab38e4fa9cbffcd651a0fc07201d7c9ed9ca3279bfa7cd673ec37b362a0aaa92f95062405a999afd49e4b1f7f818f766c49715407011ac37fa9",
	"bson_value_type": 2,
	"bson_value": "0700000073656372657400",
    "substr_tag_count": 2,
    "suffix_tag_count": 1,
	"metadata":
	[
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc"
	]
}libmongocrypt-1.19.0/test/data/iev-v2/FLECrudTest-insertOneTextLarge.json000066400000000000000000012726321521103432300262440ustar00rootroot00000000000000{
	"type": "text",
	"payload": "111234567812349876123412345678901202930300002c0100002c0100009614ca900862b166aea81641a6d6490a04adf94532b7df904b6c8c68049d2ad2ad111094e16caaa4c7d6f9202215ee95cafdda6cde7fda91b92bd06b85231a580552915de1f73980142dee7e4176824f0755405d84cfac71584b914d6a422fc734bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be136678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f585de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
	"S_KeyId": "12345678123498761234123456789012",
	"S_Key": "f502e66502ff5d4559452ce928a0f08557a9853c4dfeacca77cff482434f0ca1251fbd60d200bc35f24309521b45ad781b2d3c4df788cacef3c0e7beca8170b6cfc514ecfcf27e217ed697ae65c08272886324def514b14369c7c60414e80f22",
	"K_KeyId": "ABCDEFAB123498761234123456789012",
	"K_Key": "cbebdf05fe16099fef502a6d045c1cbb77d29d2fe19f51aec5079a81008305d8868358845d2e3ab38e4fa9cbffcd651a0fc07201d7c9ed9ca3279bfa7cd673ec37b362a0aaa92f95062405a999afd49e4b1f7f818f766c49715407011ac37fa9",
	"bson_value_type": 2,
	"bson_value": "0700000073656372657400",
    "substr_tag_count": 300,
    "suffix_tag_count": 300,
	"metadata":
	[
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc",
		"34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13",
		"6678ff929f4f077b0096782fd4ac3c296bbf2aff2520aca5585cfa8c8d14d481c5701e8b2c684b255911d7c3e1a0f1a93d75b8994f6f7621091bd062d86c439ac0d0836b30e0c9d24ed80d233a09ef441f3e1f08b525c9d2682b23e432bc6f58",
		"5de4c0ab39c87e4cb0b3a63e5ea878c10e705acc4c59126f862536ccd79683d318b9b583f23da5713244e4cff9ec8fba5a12adbbb7513ff0304586a4f4cfbe3014a4219179a78618cf889080262b4b96786fb73d4b2ab2c39ab5a0934d9b3106",
		"514d94c07296be6cbd9af53b45ee4507c923cdc230779a75c33772df7b884aa898d39eb1be108e0f285dcd2aa2f0e29dd87a04876db6ef9b88e4338e7a56f2df5267ce74d2ade2078fc5c4c2d2d341e9bfa921912e2768ffe7f81fd02a3fc26a",
		"774adf3322ba11a79d78bf82779ca2884563b4b2d7db084fa129ce9b3b62882101154aeacca3f2615873cf24a72cf39a791a43422c79c6509712dccdaa1632322a2dff00a448ceb0b98918cd1274fee83a7c33c1a40ff17693f78d785b334bdc"
	]
}libmongocrypt-1.19.0/test/data/iev-v2/FLECrudTest-insertOneV2.json000066400000000000000000000023321521103432300246170ustar00rootroot00000000000000{
	"type": "equality",
	"payload": "0e123456781234987612341234567890120215da2a70905291b8a5b4b634732dad2b67e3d8b66c02ef4f3d991405627b7b5e685062e36f5743b8890ea02fdbcc67202daac24cf3c3b5f6466c9e3fabeb43b6607eb9b9cb37193e6176c680444b43414fe9a96b643a45b91b3b1b5ca7966a3681740531ddf2920cd9a640d02edf076abf87a8e31bb7b1afd78219c74f24fa538dbe68e7b9fa42edf928cadc495b51460d0c3b3174df01b0e8b19845da8b188af1ca67aaa23e828317e44cb237e3628f76de9ede194321cd9470a2df0b6923ae",
	"S_KeyId": "12345678123498761234123456789012",
	"S_Key": "f502e66502ff5d4559452ce928a0f08557a9853c4dfeacca77cff482434f0ca1251fbd60d200bc35f24309521b45ad781b2d3c4df788cacef3c0e7beca8170b6cfc514ecfcf27e217ed697ae65c08272886324def514b14369c7c60414e80f22",
	"K_KeyId": "ABCDEFAB123498761234123456789012",
	"K_Key": "cbebdf05fe16099fef502a6d045c1cbb77d29d2fe19f51aec5079a81008305d8868358845d2e3ab38e4fa9cbffcd651a0fc07201d7c9ed9ca3279bfa7cd673ec37b362a0aaa92f95062405a999afd49e4b1f7f818f766c49715407011ac37fa9",
	"bson_value_type": 2,
	"bson_value": "0700000073656372657400",
	"metadata": ["81740531ddf2920cd9a640d02edf076abf87a8e31bb7b1afd78219c74f24fa538dbe68e7b9fa42edf928cadc495b51460d0c3b3174df01b0e8b19845da8b188af1ca67aaa23e828317e44cb237e3628f76de9ede194321cd9470a2df0b6923ae"]
}libmongocrypt-1.19.0/test/data/kek-tests.json000066400000000000000000000103331521103432300211610ustar00rootroot00000000000000[
    {
        "input": {
            "keyVaultEndpoint": "keyvault.example.com", 
            "keyVersion": "example keyVersion", 
            "keyName": "example keyName", 
            "provider": "azure"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "keyVaultEndpoint": "invalid endpoint", 
            "keyVersion": "example keyVersion", 
            "keyName": "example keyName", 
            "provider": "azure"
        }, 
        "expect": "invalid endpoint"
    }, 
    {
        "input": {
            "keyVaultEndpoint": "keyvault.example.com", 
            "keyName": "example keyName", 
            "provider": "azure"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "provider": "local"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "region": "example region", 
            "endpoint": "example.com", 
            "key": "example arn", 
            "provider": "aws"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "region": "example region", 
            "endpoint": "invalid endpoint", 
            "key": "example arn", 
            "provider": "aws"
        }, 
        "expect": "invalid endpoint"
    }, 
    {
        "input": {
            "region": "example region", 
            "key": "example arn", 
            "provider": "aws"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "keyVersion": "example keyVersion", 
            "endpoint": "example.com", 
            "projectId": "example projectId", 
            "keyRing": "example keyRing", 
            "keyName": "example keyName", 
            "location": "example location", 
            "provider": "gcp"
        }, 
        "expect": "ok"
    }, 
    {
        "input": {
            "keyVersion": "example keyVersion", 
            "endpoint": "invalid endpoint", 
            "location": "example location", 
            "provider": "gcp", 
            "projectId": "example projectId", 
            "keyRing": "example keyRing", 
            "keyName": "example keyName"
        }, 
        "expect": "invalid endpoint"
    }, 
    {
        "input": {
            "projectId": "example projectId", 
            "keyName": "example keyName", 
            "keyRing": "example keyRing", 
            "location": "example location", 
            "provider": "gcp"
        }, 
        "expect": "ok"
    },
    {
        "input": {
            "projectId": "example projectId", 
            "keyName": "example keyName", 
            "keyRing": "example keyRing", 
            "location": "example location", 
            "provider": "gcp",
            "extra": "invalid"
        }, 
        "expect": "Unexpected field: 'extra'"
    },
    {
        "input": {
            "keyVaultEndpoint": "keyvault.example.com", 
            "keyVersion": "example keyVersion", 
            "keyName": "example keyName", 
            "provider": "azure",
            "extra": "invalid"
        }, 
        "expect": "Unexpected field: 'extra'"
    },
    {
        "input": {
            "region": "example region", 
            "endpoint": "example.com", 
            "key": "example arn", 
            "provider": "aws",
            "extra": "invalid"
        }, 
        "expect": "Unexpected field: 'extra'"
    },
    {
        "input": {
            "provider": "local",
            "extra": "invalid"
        }, 
        "expect": "Unexpected field: 'extra'"
    },
    {
        "input": {
            "provider": "kmip"
        }, 
        "expect": "ok",
        "expect_append": "keyId required for KMIP"
    },
    {
        "input": {
            "provider": "kmip",
            "endpoint": "localhost:5696"
        }, 
        "expect": "ok",
        "expect_append": "keyId required for KMIP"
    },
    {
        "input": {
            "provider": "kmip",
            "endpoint": "localhost:5696",
            "keyId": "myKeyId"
        }, 
        "expect": "ok"
    },
    {
        "input": {
            "provider": "kmip",
            "endpoint": 123
        }, 
        "expect": "expected UTF-8 endpoint"
    },
    {
        "input": {
            "provider": "kmip",
            "keyId": 123
        }, 
        "expect": "expected UTF-8 keyId"
    }
]
libmongocrypt-1.19.0/test/data/key-document-azure.json000066400000000000000000000014421521103432300230000ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "provider": "azure",
        "keyVaultEndpoint": "test.vault.azure.net",
        "keyName": "test"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "pLD/TmeVF1jV/Frp/0f24/Vdsz3bIL3CbtUo4vGiZ0bMzPrngzjBqH+UXdvqzTda1FrsRFfF3r4e1MPXo7oElt9eh76ZidbKhHRMR9tO41aSfxOYdN/J3eG+aNvrecmF",
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    },
    "keyAltNames": [ "keyDocumentName" ]
}libmongocrypt-1.19.0/test/data/key-document-full.json000066400000000000000000000021231521103432300226110ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "AAAAAAAAAAAAAAAAAAAAAA==", 
            "subType": "04"
        }
    }, 
    "version": {
        "$numberLong": "0"
    },
    "keyAltNames": [ "altname1", "altname2" ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", 
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws"
    }
}libmongocrypt-1.19.0/test/data/key-document-gcp.json000066400000000000000000000015021521103432300224200ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "provider": "gcp",
        "projectId": "test",
        "location": "global",
        "keyRing": "test",
        "keyName": "test"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "PwwOESbKs57YTJtGSCsuAJbv9VWRHjLdPdziUVxH0K9woZUka4SghSwJlw9n6TU/dYHFODvSa4p7bfKzS6U8kGFvOnd7LcPU63IkTek4qZEbGpTRwI5lMT4FFWdpYpUf",
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    },
    "keyAltNames": [ "keyDocumentName" ]
}libmongocrypt-1.19.0/test/data/key-document-kmip.json000066400000000000000000000015461521103432300226170ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "RCkVkmNKRVeraJFOfmws6g==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "CWXvc+c2e7hRWf8pWeh8/GBMM7vu206tcClWgn1BuNJRaAZFim+9WcjzYUr6QjYUiHldHugdxliWM9icOJGhWjtEATmN3VyoascCtobkKVb7Jwp3FciK8Ndpqfc81lPffugkXJlY59IvBWEZaRAR6RdoEzcJNmNqDIQLzb2ODTtfdmzQHM6Ldg3AV4CN073ovPbaPQVg/2HvBA28MXxz4Q==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1633204805484"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1633204805484"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "kmip",
        "keyId": "ywxrSj5TLjswd1G4oGFJ6hwWgtTsQip0"
    },
    "keyAltNames": [
        "keyDocumentKMIP"
    ]
}
libmongocrypt-1.19.0/test/data/key-document-local.json000066400000000000000000000011651521103432300227460ustar00rootroot00000000000000{
  "_id": {
    "$binary": {
        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
        "subType": "04"
    }
},
  "keyMaterial": {
    "$binary": {
      "base64": "db27rshiqK4Jqhb2xnwK4RfdFb9JuKeUe6xt5aYQF4o62tS75b7B4wxVN499gND9UVLUbpVKoyUoaZAeA895OENP335b8n8OwchcTFqS44t+P3zmhteYUQLIWQXaIgon7gEgLeJbaDHmSXS6/7NbfDDFlB37N7BP/2hx1yCOTN6NG/8M1ppw3LYT3CfP6EfXVEttDYtPbJpbb7nBVlxD7w==",
      "subType": "00"
    }
  },
  "creationDate": { "$date": { "$numberLong": "1232739599082000" } },
  "updateDate": { "$date": { "$numberLong": "1232739599082000" } },
  "status": { "$numberInt": "0" },
  "masterKey": { "provider": "local" }
}
libmongocrypt-1.19.0/test/data/key-document-no-region.json000066400000000000000000000017271521103432300235550ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", 
        "provider": "aws"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", 
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    }
}libmongocrypt-1.19.0/test/data/key-document-with-alt-name-duplicate-id.json000066400000000000000000000020711521103432300266620ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "AwAAAAAAAAAAAAAAAAAAAA==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "region": "us-east-1", 
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", 
        "provider": "aws"
    }, 
    "keyAltNames": [
        "Sharlene", 
        "Kasey"
    ], 
    "updateDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", 
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }
}libmongocrypt-1.19.0/test/data/key-document-with-alt-name.json000066400000000000000000000016031521103432300243200ustar00rootroot00000000000000{
  "status": {
    "$numberInt": "1"
  },
  "_id": {
    "$binary": {
      "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
      "subType": "04"
    }
  },
  "masterKey": {
    "region": "us-east-1",
    "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
    "provider": "aws"
  },
  "keyAltNames": ["Sharlene", "Kasey"],
  "updateDate": {
    "$date": {
      "$numberLong": "1557827033449"
    }
  },
  "keyMaterial": {
    "$binary": {
      "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHMF9g1IqGKDAyaXKtM6d0oAAAAojCBnwYJKoZIhvcNAQcGoIGRMIGOAgEAMIGIBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOAmS5XNZ9jzDE3DnAIBEIBbgsXmyOVfXoHWra0a+9H+0e2yH8Tu9AdaMdrwwWtZNwiYq+xQ/gmw2L6Bg7SzTSR+du4smjymGDgiaMXjZ7YONJn+th1CXFkDNAAW1gV+rRjakmGadd+vIMj1LA==",
      "subType": "00"
    }
  },
  "creationDate": {
    "$date": {
      "$numberLong": "1557827033449"
    }
  }
}
libmongocrypt-1.19.0/test/data/key-document-with-alt-name2.json000066400000000000000000000017701521103432300244070ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "region": "us-east-1", 
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", 
        "provider": "aws"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1559484564420"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHMF9g1IqGKDAyaXKtM6d0oAAAAojCBnwYJKoZIhvcNAQcGoIGRMIGOAgEAMIGIBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOAmS5XNZ9jzDE3DnAIBEIBbgsXmyOVfXoHWra0a+9H+0e2yH8Tu9AdaMdrwwWtZNwiYq+xQ/gmw2L6Bg7SzTSR+du4smjymGDgiaMXjZ7YONJn+th1CXFkDNAAW1gV+rRjakmGadd+vIMj1LA==", 
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1559484564420"
        }
    },
    "keyAltNames": ["Another alt name"]
}libmongocrypt-1.19.0/test/data/keys/000077500000000000000000000000001521103432300173275ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789012-aws-decrypt-reply.txt000066400000000000000000000010431521103432300267440ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: 4d58d836-9a21-4ce8-a222-d90f89bac7dd
Cache-Control: no-cache, no-store, must-revalidate, private
Expires: 0
Pragma: no-cache
Date: Sat, 02 Apr 2022 15:45:27 GMT
Content-Type: application/x-amz-json-1.1
Content-Length: 272
Connection: close

{"EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0","Plaintext":"fb/rxhmqaKZZ9kuOI8zSFkSsMmy3SiaEDD0kIBdsQK4IgpTQCtbK6WhCN7IbdUz1A/CFwlzTIL8DXDQXQW4eb+PZIZ95WGWCESdAsq3YjhAw2Rkmror8E+5XXPuLuWW3"}libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789012-aws-document.json000066400000000000000000000017571521103432300261250ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gG69plztrxKXh9M8C54okakAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDN5KpQl1MfBbXDngKQIBEIB7zzdKA1fG4Jaqte1TRUzAB7zbS6sr1og+T5lo/WDXjELnvvsfWflvrEK/Dc1dnpSldVgkidjdT//H6HX5cJ+aw2CgNZz2F15PqEvU1h2QKGBbs4Omhb0L8jBv5fMa/9nenxl5j5y4wO0XPGtiV2G/dUztYNB6qz0YhdTl",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1648914327152"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1648914327152"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "aws",
        "region": "us-east-1",
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
    }
}
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789012-key-material.txt000066400000000000000000000003011521103432300257310ustar00rootroot000000000000007dbfebc619aa68a659f64b8e23ccd21644ac326cb74a26840c3d2420176c40ae088294d00ad6cae9684237b21b754cf503f085c25cd320bf035c3417416e1e6fe3d9219f79586582112740b2add88e1030d91926ae8afc13ee575cfb8bb965b7
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789012-local-document.json000066400000000000000000000013741521103432300264200ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "EjRWeBI0mHYSNBI0VniQEg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "1ZbBTB1i/z4LcmBKi9+nnWqkVB4Wl6P4G7/TFQvXATRF2fX0lhBLIM6rT1U547FX2YgMtaP7sid+jpd4Vhz5kS+UlgtmCFfjeO4qOnJ78KEXRzeIebzKWKQz1pMhYZ3OURDL4wCtNqt3tbSr11kfTADmCMuzgp8U8P8T21RWWBU0f2XDcxiIShYncOS3poKu7GJaPCTav4r3h5h2xRklDA==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1648914851981"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1648914851981"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789013-aws-decrypt-reply.txt000066400000000000000000000010711521103432300267460ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: 61524068-10fc-458d-8c85-aa23fd58ff56
Cache-Control: no-cache, no-store, must-revalidate, private
Expires: 0
Pragma: no-cache
Date: Fri, 29 Apr 2022 13:21:22 GMT
Content-Type: application/x-amz-json-1.1
Content-Length: 294
Connection: close

{"EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0","KeyOrigin":"AWS_KMS","Plaintext":"H2XDIj1WU829c8Eaj4VYeq/L1b5+TDCNNXsvAbvPdqmAKTDl8jOSO7w/Xr0L4duYB/BKqHDIlgkhgN2LBYFrj3Vo/3YqGk79NbvAKCY5TrMPNs2ODGRq4vQ99CDlChnr"}libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789013-aws-document.json000066400000000000000000000017561521103432300261250ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gG7JTEihw4JH7KZ7aFD677EAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDANc1smIZE/xHp5+kwIBEIB78OFCdWkE1363uAER/mTCM6ccm0UZbjHXJfLCIvy52tiYLkUsykDXCcFoLaZ4LgK/6bm9816iEthHMKHMdHrn7kVEk+2H0A9TWzW6d5lvN3IT4qNKNy5dzH1ayufINYt/FnH+316Z/HQxvlG4Uf+ajPM21G5/PIUUe0hZ",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1651238481951"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1651238481951"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "aws",
        "region": "us-east-1",
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
    }
}libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789013-key-material.txt000066400000000000000000000003011521103432300257320ustar00rootroot000000000000001f65c3223d5653cdbd73c11a8f85587aafcbd5be7e4c308d357b2f01bbcf76a9802930e5f233923bbc3f5ebd0be1db9807f04aa870c896092180dd8b05816b8f7568ff762a1a4efd35bbc02826394eb30f36cd8e0c646ae2f43df420e50a19eb
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789013-local-document.json000066400000000000000000000013741521103432300264210ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "EjRWeBI0mHYSNBI0VniQEw==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "YQXu48YyDbXvVQ1OhPsodQQNA1gLVWZSV0udYVmCTpVrgyAZePHQmsWWnQzNZj+ZsTxRm02soje/FJCqWGLeth3gKdvIsRg15CDEUOqLdDEpHl46hadosXyJIfo0umZ/LVTkvxRhmDCDxAkd0+Dg4/vWSiG0FgNzGrlvOUsTLGbqWtNMuOdZ8pKAdnFRrqce5cwBGQmd2VVBA2OQ0/IMxQ==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1650631142512"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1650631142512"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789014-key-material.txt000066400000000000000000000003011521103432300257330ustar00rootroot000000000000001f65c3223d5653cdbd73c11a8f85587aafcbd5be7e4c308d357b2f01bbcf76a9802930e5f233923bbc3f5ebd0be1db9807f04aa870c896092180dd8b05816b8f7568ff762a1a4efd35bbc02826394eb30f36cd8e0c646ae2f43df420e50a19eb
libmongocrypt-1.19.0/test/data/keys/12345678123498761234123456789014-local-document.json000066400000000000000000000013741521103432300264220ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "EjRWeBI0mHYSNBI0VniQFA==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "huOrOjM8DWLRP43+oeYYFePEbHVYFgLSBAI6OPTDxW9AhegvodHyTLW9GD3QjhF0QnKqRiCEsBSbVEuGHxGD3Mf946/JYl9Ky8OsMzGeIoKXoxZsdztUDoW7TSWM4joPeVM+0GpmkEWgIPwWX9V9lnRv60CL+USOdOoK3FCOH+8u5NdjtdeDTAUFtbasZRW+r/5P/4LvnGuIfN/CIv7Ovg==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1650631261637"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1650631261637"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/keys/ABCDEFAB123498761234123456789012-aws-decrypt-reply.txt000066400000000000000000000010431521103432300271300ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: adcfdbde-7213-4fcf-a312-370e1d0ccaf0
Cache-Control: no-cache, no-store, must-revalidate, private
Expires: 0
Pragma: no-cache
Date: Sat, 02 Apr 2022 16:00:00 GMT
Content-Type: application/x-amz-json-1.1
Content-Length: 272
Connection: close

{"EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0","Plaintext":"p928TIvgDVH2jZ2OSF81HI7cjSIGsk2ODhgW0AX75SDkiRJQR9ZHsNhoS/vb8JwwQIXtCGq6bCsrFnfMyRztiEenM79eVoLISz7nlp5KX+Dgwh5ePuGQWVpV+DFH2N4q"}libmongocrypt-1.19.0/test/data/keys/ABCDEFAB123498761234123456789012-aws-document.json000066400000000000000000000017571521103432300263110ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "q83vqxI0mHYSNBI0VniQEg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gECARBA9u8bNDBpw/x3UP0GAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDGo4wmA5gMfyL7UCoQIBEIB7k7qshUFeiG8B1dlxR0zjcbM8z7tKHlC+mH6IJ9tdSncldQ1Mb+Ur28J2OF9IVxA5poU/HV+JbI0unldTWJ57k70wu11k5v7PU0gikw0OCQiBdfwaoC9MvvpJ6r4t+15Rf0G35jSk7ZCPZa8dwYuzEoAoKJhxNuUjDhVY",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1648915200516"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1648915200516"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "aws",
        "region": "us-east-1",
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"
    }
}
libmongocrypt-1.19.0/test/data/keys/ABCDEFAB123498761234123456789012-key-material.txt000066400000000000000000000003011521103432300261150ustar00rootroot00000000000000a7ddbc4c8be00d51f68d9d8e485f351c8edc8d2206b24d8e0e1816d005fbe520e489125047d647b0d8684bfbdbf09c304085ed086aba6c2b2b1677ccc91ced8847a733bf5e5682c84b3ee7969e4a5fe0e0c21e5e3ee190595a55f83147d8de2a
libmongocrypt-1.19.0/test/data/keys/ABCDEFAB123498761234123456789012-local-document.json000066400000000000000000000013741521103432300266040ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "q83vqxI0mHYSNBI0VniQEg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "27OBvUqHAuYFy60nwCdvq2xmZ4kFzVySphXzBGq+HEot13comCoydEfnltBzLTuXLbV9cnREFJIO5f0jMqrlkxIuvAV8yO84p5VJTEa8j/xSNe7iA594rx7UeKT0fOt4VqM47fht8h+8PZYc5JVezvEMvwk115IBCwENxDjLtT0g+y8Hf+aTUEGtxrYToH8zf1/Y7S16mHiIc4jK3/vxHw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1648915408923"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1648915408923"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/keys/README.md000066400000000000000000000021131521103432300206030ustar00rootroot00000000000000This directory contains Data Encryption Key (DEKs) encrypted by various Key Encryption Keys (KEKs) for testing.

Files are named as follows:

- `-key-material.txt` is the decrypted key material.
- `-local-document.json` is a key document with "_id" of  encrypted with a local KEK.
- `-aws-document.json` is a key document with "_id" of  encrypted with an AWS KEK.
- `-aws-decrypt-reply.txt` is an HTTP reply from AWS KMS decrypting the DEK.

The key material of the local KEK 96 bytes of 0.

The `csfle` CLI tool was used to generate output for these files. Here is an example command used for creating a "-aws-document.json" file:

```bash
./cmake-build/csfle create_datakey \
        --kms_providers_file ~/.csfle/kms_providers.json \
        --kms_provider aws \
        --aws_kek_region us-east-1 \
        --aws_kek_key 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0' \
        --key_material "p928TIvgDVH2jZ2OSF81HI7cjSIGsk2ODhgW0AX75SDkiRJQR9ZHsNhoS/vb8JwwQIXtCGq6bCsrFnfMyRztiEenM79eVoLISz7nlp5KX+Dgwh5ePuGQWVpV+DFH2N4q"
```
libmongocrypt-1.19.0/test/data/kms-aws/000077500000000000000000000000001521103432300177365ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/kms-aws/decrypt-response.txt000066400000000000000000000005561521103432300240130ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 233

{"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "Plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/test/data/kms-aws/encrypt-response-partial.txt000066400000000000000000000002531521103432300254510ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 446
Connection: close

{"KeyId": "arn:aws:klibmongocrypt-1.19.0/test/data/kms-aws/encrypt-response.txt000066400000000000000000000011251521103432300240160ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 446
Connection: close

{"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "CiphertextBlob": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHCPOT4UQIpMTvAVABLqnXlAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDLxAm0nO3rccdoWA6AIBEIB7HUe6+aPvgNu/4sLEXBQVDIJVBueI3q7zdOMBSkRKkgZWqEuQgA6iDuEZbhHhOVCUXPBaLX6QWRwyMmjvIy/2Bg5q+TmwnfRo6QKdw2vee1W32/FdPWIoQy1yKOoIhNy6XMWldS3JuWK8ffQOYkssEqx0V4LW6PKuFv7D"}libmongocrypt-1.19.0/test/data/kms-azure/000077500000000000000000000000001521103432300202725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/kms-azure/decrypt-response.txt000066400000000000000000000013421521103432300243410ustar00rootroot00000000000000HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
x-ms-keyvault-region: eastus
x-ms-request-id: 87ad5ae4-696a-46a4-87e0-cf13832a70ce
x-ms-keyvault-service-version: 1.9.1054.1
x-ms-keyvault-network-info: conn_type=Ipv4;addr=98.110.73.14;act_addr_fam=InterNetwork;
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000;includeSubDomains
Date: Tue, 21 Nov 2023 13:41:26 GMT
Connection: close
Content-Length: 241

{"kid":"https://key-vault-csfle.vault.azure.net/keys/key-name-csfle/bf035425d3f44c51bb22244b7f58e350","value":"u3FgBLOThp0jDn3FqUPLnETuRNPYDCMET2F7gPkRHUNBrcc45ugX4sOB6Qx_p7TXO_PajifMQaFG5V3yH8hLz_vcMOJ1iSXQ5OZxf8aJ-YZQ2NJkZJofIBul97v6663Z"}libmongocrypt-1.19.0/test/data/kms-azure/encrypt-response.txt000066400000000000000000000016701521103432300243570ustar00rootroot00000000000000HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
x-ms-keyvault-region: eastus
x-ms-request-id: 180ebf78-d158-4349-bdc3-b6868fdcb7d0
x-ms-keyvault-service-version: 1.9.1054.1
x-ms-keyvault-network-info: conn_type=Ipv4;addr=98.110.73.14;act_addr_fam=InterNetwork;
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000;includeSubDomains
Date: Tue, 21 Nov 2023 13:35:21 GMT
Connection: close
Content-Length: 455

{"kid":"https://key-vault-csfle.vault.azure.net/keys/key-name-csfle/bf035425d3f44c51bb22244b7f58e350","value":"3hrs0cMlsmS7DDN2GNzR8n-CF59mzvrMTP3_dmBV1zzREob5muan2rYCcs7MU1k_ANZGaP4lWuDDVFuzwMTbhioFu64ypBDdMcKJ1VXMiKKNia1KgxJyDhDcc9Mq1fROvf5zXuceSBm1LjRenTMieBnFfO-V24cyDnj7KLdbdQHuf49RMHCWN7yq_vQ35Hobvzh1AO5WLB4MJUJLYh1r3BI2CNhMQT45L9Jkn_A4sTyKWfCpdVYgsi6XOv8bYSgM3fqhZnrtp2Q8cNY-_Nft08px8RGuLTLIzdrLyrtZ6dwpv7tWyamBJYxB4AQNKSiuKLxyvYLIxwQGasZrcfQqUQ"}libmongocrypt-1.19.0/test/data/kms-azure/oauth-response.txt000066400000000000000000000014651521103432300240150ustar00rootroot00000000000000HTTP/1.1 200 OK
Cache-Control: no-store, no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
x-ms-request-id: 0f1342b1-6c07-45a7-8dfa-a5f90dc46b01
x-ms-ests-server: 2.1.16729.8 - WUS3 ProdSlices
X-XSS-Protection: 0
Set-Cookie: fpc=AvrLAj-gkztInBUWCCCIFDXJ8q5BAQAAABmp7twOAAAA; expires=Thu, 21-Dec-2023 13:35:21 GMT; path=/; secure; HttpOnly; SameSite=None
Set-Cookie: x-ms-gateway-slice=estsfd; path=/; secure; httponly
Set-Cookie: stsservicecookie=estsfd; path=/; secure; httponly
Date: Tue, 21 Nov 2023 13:35:21 GMT
Connection: close
Content-Length: 98

{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"test-access-token"}libmongocrypt-1.19.0/test/data/kms-encrypt-reply.txt000066400000000000000000000011251521103432300225210ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 446
Connection: close

{"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "CiphertextBlob": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHCPOT4UQIpMTvAVABLqnXlAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDLxAm0nO3rccdoWA6AIBEIB7HUe6+aPvgNu/4sLEXBQVDIJVBueI3q7zdOMBSkRKkgZWqEuQgA6iDuEZbhHhOVCUXPBaLX6QWRwyMmjvIy/2Bg5q+TmwnfRo6QKdw2vee1W32/FdPWIoQy1yKOoIhNy6XMWldS3JuWK8ffQOYkssEqx0V4LW6PKuFv7D"}libmongocrypt-1.19.0/test/data/kms-gcp/000077500000000000000000000000001521103432300177155ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/kms-gcp/decrypt-response.txt000066400000000000000000000003441521103432300237650ustar00rootroot00000000000000HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 145

{"plaintext": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f"}libmongocrypt-1.19.0/test/data/kms-gcp/encrypt-response.txt000066400000000000000000000005251521103432300240000ustar00rootroot00000000000000HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 258

{"ciphertext": "CiQAg4LDqo6XZ3CbRYdQkQhuUyeJIah2hBIBc90q1ViWx1G+HMkSiQEAkWNo/U0dCh7GuWaRAK8NK00/VUDpHJDEJ6VxS07AiVUkYRsvLqYNSw8rLZ9hb646nCB8spXd5GQIIQyaTbcGWytZOQT2v9aVXl8UmSK5jTXwhFGwH/kZFjM6cJBfpwnK5C8q3GLBJ4xqJVk42WKKsA25gdZTFQXrYQBNLVEmhw6q30X6bCy0VQ=="}libmongocrypt-1.19.0/test/data/kms-gcp/oauth-response.txt000066400000000000000000000002361521103432300234330ustar00rootroot00000000000000HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 76

{"access_token":"test-access-token","expires_in":3599,"token_type":"Bearer"}libmongocrypt-1.19.0/test/data/kms-tests.json000066400000000000000000000304441521103432300212060ustar00rootroot00000000000000[
  {
    "description": "Successful encryption response",
    "ctx": ["datakey"],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 446\r\n",
      "Connection: close\r\n",
      "\r\n",
      "{\"KeyId\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\", \"CiphertextBlob\": \"AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gHCPOT4UQIpMTvAVABLqnXlAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDLxAm0nO3rccdoWA6AIBEIB7HUe6+aPvgNu/4sLEXBQVDIJVBueI3q7zdOMBSkRKkgZWqEuQgA6iDuEZbhHhOVCUXPBaLX6QWRwyMmjvIy/2Bg5q+TmwnfRo6QKdw2vee1W32/FdPWIoQy1yKOoIhNy6XMWldS3JuWK8ffQOYkssEqx0V4LW6PKuFv7D\"}"
    ],
    "expect": "ok"
  },
  {
    "description": "Successful decryption response",
    "ctx": ["decrypt"],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 233\r\n",
      "\r\n",
      "{\"KeyId\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\", \"Plaintext\": \"TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR\"}"
    ],
    "expect": "ok"
  },
  {
    "description": "Error message included in body",
    "ctx": ["datakey", "decrypt"],
    "http_reply": [
      "HTTP/1.1 400 Bad Request\r\n",
      "x-amzn-RequestId: b0e91dc8-3807-11e2-83c6-5912bf8ad066\r\n",
      "x-amzn-ErrorType: ValidationException\r\n",
      "Content-Type: application/json\r\n",
      "Content-Length: 128\r\n",
      "Date: Mon, 26 Nov 2012 20:27:25 GMT\r\n",
      "\r\n",
      "{\"message\":\"1 validation error detected: Value null at 'InstallS3Bucket' failed to satisfy constraint: Member must not be null\"}"
    ],
    "expect": "validation error"
  },
  {
    "ctx": ["datakey", "decrypt"],
    "description": "Empty body",
    "http_reply": ["HTTP/1.1 418 I'm a teapot\r\n", "\r\n"],
    "expect": "Error in KMS response. HTTP status=418"
  },
  {
    "ctx": ["datakey", "decrypt"],
    "description": "Content-Length too large",
    "http_reply": [
      "HTTP/1.1 200 Don't worry about it. Everything is ok\r\n",
      "Content-Length: 1234\r\n",
      "\r\n",
      "abc"
    ],
    "expect": "KMS response unfinished"
  },
  {
    "ctx": ["datakey", "decrypt"],
    "description": "Content-Length too small",
    "http_reply": [
      "HTTP/1.1 200 Don't worry about it. Everything is ok\r\n",
      "Content-Length: 1\r\n",
      "\r\n",
      "abcasdfasdf"
    ],
    "expect": "KMS response fed too much data"
  },
  {
    "ctx": ["datakey", "decrypt"],
    "description": "Content-Length explicitly 0",
    "http_reply": [
      "HTTP/1.1 418 I'm a teapot\r\n",
      "Content-Length: 0\r\n",
      "\r\n"
    ],
    "expect": "Error in KMS response. HTTP status=418"
  },
  {
    "description": "Non-error non-200 HTTP status",
    "ctx": ["datakey", "decrypt"],
    "http_reply": [
      "HTTP/1.1 100 Continue\r\n",
      "\r\n"
    ],
    "expect": "Unsupported HTTP code in KMS response. HTTP status=100. Response body=\n"
  },
  {
    "description": "Bad JSON in response",
    "ctx": [
      "datakey",
      "decrypt",
      "azure_oauth_datakey",
      "azure_oauth_decrypt",
      "azure_datakey",
      "azure_decrypt",
      "gcp_oauth_datakey",
      "gcp_oauth_decrypt",
      "gcp_datakey",
      "gcp_decrypt"
    ],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 7\r\n",
      "\r\n",
      "BADJSON"
    ],
    "expect": [
      "Error parsing JSON in KMS response 'Got parse error at \"B\", position 0: \"SPECIAL_EXPECTED\"'. HTTP status=200. Response body=\n",
      "BADJSON"
    ]
  },
  {
    "description": "Non-numeric status in response",
    "ctx": ["decrypt"],
    "http_reply": [
      "HTTP/1.1 abc OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 233\r\n",
      "\r\n"
    ],
    "expect": "Could not parse Status-Code"
  },
  {
    "description": "Status exceeding int range in response",
    "ctx": ["decrypt"],
    "http_reply": [
      "HTTP/1.1 18446744073709551617 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 233\r\n",
      "\r\n"
    ],
    "expect": "Could not parse Status-Code"
  },
  {
    "description": "Successful oauth request",
    "ctx": ["azure_oauth_datakey", "azure_oauth_decrypt", "gcp_oauth_datakey", "gcp_oauth_decrypt"],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "Cache-Control: no-store, no-cache\r\n",
      "Pragma: no-cache\r\n",
      "Content-Type: application/json; charset=utf-8\r\n",
      "Expires: -1\r\n",
      "Strict-Transport-Security: max-age=31536000; includeSubDomains\r\n",
      "X-Content-Type-Options: nosniff\r\n",
      "P3P: CP=\"DSP CUR OTPi IND OTRi ONL FIN\"\r\n",
      "x-ms-request-id: d5f2911d-6574-42fd-bb6f-2e5755ef1200\r\n",
      "x-ms-ests-server: 2.1.11140.11 - EST ProdSlices\r\n",
      "Set-Cookie: fpc=Al-86TFxDiZDvC96TyXEMKU9q7ekAQAAAC3jGtcOAAAA; expires=Sun, 15-Nov-2020 00:41:18 GMT; path=/; secure; HttpOnly; SameSite=None\r\n",
      "Set-Cookie: x-ms-gateway-slice=prod; path=/; secure; samesite=none; httponly\r\n",
      "Set-Cookie: stsservicecookie=ests; path=/; secure; samesite=none; httponly\r\n",
      "Date: Fri, 16 Oct 2020 00:41:17 GMT\r\n",
      "Content-Length: 85\r\n",
      "\r\n",
      "{\"token_type\":\"Bearer\",\"expires_in\":3599,\"ext_expires_in\":3599,\"access_token\":\"AAAA\"}"
    ],
    "expect": "ok"
  },
  {
    "description": "Invalid client id in Azure oauth request",
    "ctx": ["azure_oauth_datakey", "azure_oauth_decrypt"],
    "http_reply": [
      "HTTP/1.1 400 Bad Request\r\n",
      "Cache-Control: no-store, no-cache\r\n",
      "Pragma: no-cache\r\n",
      "Content-Type: application/json; charset=utf-8\r\n",
      "Expires: -1\r\n",
      "Strict-Transport-Security: max-age=31536000; includeSubDomains\r\n",
      "X-Content-Type-Options: nosniff\r\n",
      "P3P: CP=\"DSP CUR OTPi IND OTRi ONL FIN\"\r\n",
      "x-ms-request-id: a1029d4e-fcae-4d24-bb46-ad4f3d8c1200\r\n",
      "x-ms-ests-server: 2.1.11140.11 - EST ProdSlices\r\n",
      "Set-Cookie: fpc=At8uDzjFi7ZAqLOAe9gcjXu3BobWAQAAALfoGtcOAAAA; expires=Sun, 15-Nov-2020 01:04:56 GMT; path=/; secure; HttpOnly; SameSite=None\r\n",
      "Set-Cookie: x-ms-gateway-slice=prod; path=/; secure; samesite=none; httponly\r\n",
      "Set-Cookie: stsservicecookie=ests; path=/; secure; samesite=none; httponly\r\n",
      "Date: Fri, 16 Oct 2020 01:04:56 GMT\r\n",
      "Content-Length: 752\r\n",
      "\r\n",
      "{\"error\":\"unauthorized_client\",\"error_description\":\"AADSTS700016: Application with identifier 'CLIENT_ID' was not found in the directory 'a1d0ca08-34e5-4a1b-924d-ef3cea0e4b49'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.\\r\\nTrace ID: 5458a904-9ab0-4f95-97e5-24f47ca21400\\r\\nCorrelation ID: e43f3081-dd2e-4989-8975-d5e4cdd597c1\\r\\nTimestamp: 2020-10-16 01:11:04Z\",\"error_codes\":[700016],\"timestamp\":\"2020-10-16 01:11:04Z\",\"trace_id\":\"5458a904-9ab0-4f95-97e5-24f47ca21400\",\"correlation_id\":\"e43f3081-dd2e-4989-8975-d5e4cdd597c1\",\"error_uri\":\"https://login.microsoftonline.com/error?code=700016\"}"
    ],
    "expect": "Application with identifier 'CLIENT_ID' was not found"
  },
  {
    "description": "GCP invalid signature",
    "ctx": ["gcp_oauth_datakey", "gcp_oauth_decrypt"],
    "http_reply": [
      "HTTP/1.1 400 Bad Request\r\n",
      "Content-Type: application/json; charset=UTF-8\r\n",
      "Vary: X-Origin\r\n",
      "Vary: Referer\r\n",
      "Date: Fri, 16 Oct 2020 01:22:34 GMT\r\n",
      "Server: scaffolding on HTTPServer2\r\n",
      "Cache-Control: private\r\n",
      "X-XSS-Protection: 0\r\n",
      "X-Frame-Options: SAMEORIGIN\r\n",
      "X-Content-Type-Options: nosniff\r\n",
      "Alt-Svc: h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"\r\n",
      "Accept-Ranges: none\r\n",
      "Vary: Origin,Accept-Encoding\r\n",
      "Transfer-Encoding: chunked\r\n",
      "\r\n",
      "46\r\n",
      "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid JWT Signature.\"}\r\n",
      "0\r\n",
      "\r\n"
    ],
    "expect": "Invalid JWT Signature"
  },
  {
    "description": "GCP invalid ciphertext",
    "ctx": ["gcp_datakey", "gcp_decrypt"],
    "http_reply": [
      "HTTP/1.1 400 Bad Request\r\n",
      "Alt-Svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"\r\n",
      "Cache-Control: private\r\n",
      "Content-Encoding: gzip\r\n",
      "Content-Type: application/json; charset=UTF-8\r\n",
      "Date: Wed, 20 Jul 2022 17:36:54 GMT\r\n",
      "Server: ESF\r\n",
      "Transfer-Encoding: chunked\r\n",
      "Vary: Origin, X-Origin, Referer\r\n",
      "X-Content-Type-Options: nosniff\r\n",
      "X-Frame-Options: SAMEORIGIN\r\n",
      "X-XSS-Protection: 0\r\n",
      "\r\n",
      "87\r\n",
      "{\n",
      "  \"error\": {\n",
      "    \"code\": 400,\n",
      "    \"message\": \"Decryption failed: the ciphertext is invalid.\",\n",
      "    \"status\": \"INVALID_ARGUMENT\"\n",
      "  }\n",
      "}\n",
      "\r\n",
      "0\r\n",
      "\r\n"      
    ],
    "expect": [
      "Error in KMS response. HTTP status=400. Response body=\n",
      "{\n",
      "  \"error\": {\n",
      "    \"code\": 400,\n",
      "    \"message\": \"Decryption failed: the ciphertext is invalid.\",\n",
      "    \"status\": \"INVALID_ARGUMENT\"\n",
      "  }\n",
      "}\n"
    ]
  },
  {
    "description": "JSON in response missing needed field",
    "ctx": [
      "datakey",
      "decrypt",
      "azure_oauth_datakey",
      "azure_oauth_decrypt",
      "azure_datakey",
      "azure_decrypt",
      "gcp_oauth_datakey",
      "gcp_oauth_decrypt",
      "gcp_datakey",
      "gcp_decrypt"
    ],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 8\r\n",
      "\r\n",
      "{\"x\": 1}"
    ],
    "expect": [
      "HTTP status=200. Response body=\n",
      "{\"x\": 1}"
    ]
  },
  {
    "description": "Encryption response with invalid base64",
    "ctx": ["datakey"],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\r\n",
      "Content-Type: application/x-amz-json-1.1\r\n",
      "Content-Length: 111\r\n",
      "Connection: close\r\n",
      "\r\n",
      "{\"KeyId\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\", \"CiphertextBlob\": \"A\"}"
    ],
    "expect": "Failed to base64 decode"
  },
  {
    "description": "Chunked transfer with INT_MAX chunk size (MONGOCRYPT-910)",
    "ctx": [
      "datakey",
      "decrypt",
      "azure_oauth_datakey",
      "azure_oauth_decrypt",
      "azure_datakey",
      "azure_decrypt",
      "gcp_oauth_datakey",
      "gcp_oauth_decrypt",
      "gcp_datakey",
      "gcp_decrypt"
    ],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "Transfer-Encoding: chunked\r\n",
      "\r\n",
      "7fffffff\r\nX"
    ],
    "expect": "Failed to parse hex chunk length"
  },
  {
    "description": "Content-Length too large",
    "ctx": [
      "datakey",
      "decrypt",
      "azure_oauth_datakey",
      "azure_oauth_decrypt",
      "azure_datakey",
      "azure_decrypt",
      "gcp_oauth_datakey",
      "gcp_oauth_decrypt",
      "gcp_datakey",
      "gcp_decrypt"
    ],
    "http_reply": [
      "HTTP/1.1 200 OK\r\n",
      "Content-Length: 123123123\r\n",
      "\r\n",
      "{}"
    ],
    "expect": "Could not parse Content-Length"
  }
]
libmongocrypt-1.19.0/test/data/lookup/000077500000000000000000000000001521103432300176655ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-facet/000077500000000000000000000000001521103432300220415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-facet/cmd.json000066400000000000000000000006201521103432300234750ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$facet": {
            "output": [
               {
                  "$lookup": {
                     "from": "c2",
                     "localField": "joinme",
                     "foreignField": "joinme",
                     "as": "matched"
                  }
               }
            ]
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-mismatch/000077500000000000000000000000001521103432300225645ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-mismatch/cmd.json000066400000000000000000000003621521103432300242230ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-mismatch/collInfo-c1.json000066400000000000000000000020531521103432300255250ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-mismatch/collInfo-c3.json000066400000000000000000000020531521103432300255270ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c3",
    "idIndex": {
        "ns": "db.c3",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e2": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-nested/000077500000000000000000000000001521103432300222415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-nested/cmd.json000066400000000000000000000010241521103432300236740ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched",
            "pipeline": [
               {
                  "$lookup": {
                     "from": "c3",
                     "localField": "joinme",
                     "foreignField": "joinme",
                     "as": "matched"
                  }
               }
            ]
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-only-schemaMap/000077500000000000000000000000001521103432300236345ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-only-schemaMap/cmd-to-mongocryptd.json000066400000000000000000000031401521103432300302530ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": false
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": false
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-only-schemaMap/cmd.json000066400000000000000000000003621521103432300252730ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-only-schemaMap/schemaMap.json000066400000000000000000000017511521103432300264310ustar00rootroot00000000000000{
   "db.c1": {
      "properties": {
         "e1": {
            "encrypt": {
               "keyId": [
                  {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  }
               ],
               "bsonType": "string",
               "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
         }
      },
      "bsonType": "object"
   },
   "db.c2": {
      "properties": {
         "e2": {
            "encrypt": {
               "keyId": [
                  {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  }
               ],
               "bsonType": "string",
               "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
         }
      },
      "bsonType": "object"
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-schemaMap/000077500000000000000000000000001521103432300226555ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-schemaMap/cmd-to-mongocryptd.json000066400000000000000000000031371521103432300273020ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": false
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-schemaMap/cmd.json000066400000000000000000000003621521103432300243140ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-schemaMap/collInfo-c1.json000066400000000000000000000020531521103432300256160ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-schemaMap/schemaMap.json000066400000000000000000000007661521103432300254570ustar00rootroot00000000000000{
   "db.c2": {
      "properties": {
         "e2": {
            "encrypt": {
               "keyId": [
                  {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  }
               ],
               "bsonType": "string",
               "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
         }
      },
      "bsonType": "object"
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-self/000077500000000000000000000000001521103432300217105ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-self/cmd-to-mongocryptd.json000066400000000000000000000014051521103432300263310ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c1",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "jsonSchema": {
      "properties": {
         "e1": {
            "encrypt": {
               "keyId": [
                  {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  }
               ],
               "bsonType": "string",
               "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
         }
      },
      "bsonType": "object"
   },
   "isRemoteSchema": true
}
libmongocrypt-1.19.0/test/data/lookup/csfle-self/cmd.json000066400000000000000000000003621521103432300233470ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c1",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-self/collInfo-c1.json000066400000000000000000000020531521103432300246510ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/000077500000000000000000000000001521103432300224065ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/cmd-to-mongocryptd.json000066400000000000000000000022751521103432300270350ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "unencrypted": {
                  "bsonType": "string"
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/cmd.json000066400000000000000000000003621521103432300240450ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/collInfo-c1.json000066400000000000000000000020531521103432300253470ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/collInfo-c2.json000066400000000000000000000011141521103432300253450ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "unencrypted": {
                        "bsonType": "string"
                    }
                },
                "bsonType": "object"
            },
            "foo": "bar"
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-sibling/reply-from-mongocryptd.json000066400000000000000000000005661521103432300277470ustar00rootroot00000000000000{
   "result": {
      "aggregate": "c1",
      "pipeline": [
         {
            "$lookup": {
               "from": "c2",
               "localField": "joinme",
               "foreignField": "joinme",
               "as": "matched"
            }
         }
      ],
      "cursor": {}
   },
   "hasEncryptionPlaceholders": false,
   "schemaRequiresEncryption": true
}
libmongocrypt-1.19.0/test/data/lookup/csfle-unionWith/000077500000000000000000000000001521103432300227435ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-unionWith/cmd.json000066400000000000000000000006601521103432300244030ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$unionWith": {
            "coll": "c2",
            "pipeline": [
               {
                  "$lookup": {
                     "from": "c3",
                     "localField": "joinme",
                     "foreignField": "joinme",
                     "as": "matched"
                  }
               }
            ]
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-view/000077500000000000000000000000001521103432300217315ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle-view/cmd.json000066400000000000000000000003621521103432300233700ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "v1",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle-view/collInfo-c1.json000066400000000000000000000020531521103432300246720ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle-view/collInfo-v1.json000066400000000000000000000002361521103432300247160ustar00rootroot00000000000000{
    "name": "v1",
    "type": "view",
    "options": {
        "viewOn": "c1",
        "pipeline": []
    },
    "info": {
        "readOnly": true
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle/000077500000000000000000000000001521103432300207615ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/csfle/cmd-to-mongocryptd.json000066400000000000000000000031361521103432300254050ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/csfle/cmd.json000066400000000000000000000003621521103432300224200ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/csfle/collInfo-c1.json000066400000000000000000000020531521103432300237220ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/csfle/collInfo-c2.json000066400000000000000000000020531521103432300237230ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e2": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/000077500000000000000000000000001521103432300207735ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/000077500000000000000000000000001521103432300220675ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/000077500000000000000000000000001521103432300231635ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/cmd-to-mongocryptd.json000066400000000000000000000032471521103432300276120ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/cmd-to-mongod.json000066400000000000000000000011311521103432300265160ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": {
               "$eq": {
                  "$binary": {
                     "base64": "AbidjY8vGEA7mCm87trxCkIC8kZKVHsnE5QVf4xhKcl5AesA5UowxrXhd2IOU6/bQ5EjCTRjLABgU4qpPkwCtbkZX4COgHCmiyzOenkRVBV7NQ==",
                     "subType": "06"
                  }
               }
            }
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/cmd.json000066400000000000000000000004731521103432300246250ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/collInfo-c1.json000066400000000000000000000020531521103432300261240ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/collInfo-c2.json000066400000000000000000000020531521103432300261250ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e2": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/key-doc.json000066400000000000000000000013741521103432300254160ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/csfle/reply-from-mongocryptd.json000066400000000000000000000014031521103432300305130ustar00rootroot00000000000000{
   "hasEncryptionPlaceholders": false,
   "schemaRequiresEncryption": true,
   "result": {
      "aggregate": "c1",
      "pipeline": [
         {
            "$lookup": {
               "from": "c2",
               "localField": "joinme",
               "foreignField": "joinme",
               "as": "matched"
            }
         },
         {
            "$match": {
               "e1": {
                  "$eq": {
                     "$binary": {
                        "base64": "ADAAAAAQYQABAAAABWtpABAAAAAEuJ2Njy8YQDuYKbzu2vEKQgJ2AAQAAABmb28AAA==",
                        "subType": "06"
                     }
                  }
               }
            }
         }
      ],
      "cursor": {}
   },
   "ok": {
      "$numberDouble": "1.0"
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/000077500000000000000000000000001521103432300237415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/cmd-to-mongocryptd.json000066400000000000000000000021311521103432300303570ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      },
      "db.c2": {
         "jsonSchema": {},
         "isRemoteSchema": false
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/cmd-to-mongod.json000066400000000000000000000011311521103432300272740ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": {
               "$eq": {
                  "$binary": {
                     "base64": "AbidjY8vGEA7mCm87trxCkIC8kZKVHsnE5QVf4xhKcl5AesA5UowxrXhd2IOU6/bQ5EjCTRjLABgU4qpPkwCtbkZX4COgHCmiyzOenkRVBV7NQ==",
                     "subType": "06"
                  }
               }
            }
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/cmd.json000066400000000000000000000004731521103432300254030ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/collInfo-c1.json000066400000000000000000000020531521103432300267020ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/collInfo-c2.json000066400000000000000000000004411521103432300267020ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/key-doc.json000066400000000000000000000013741521103432300261740ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/no-schema/reply-from-mongocryptd.json000066400000000000000000000014031521103432300312710ustar00rootroot00000000000000{
   "hasEncryptionPlaceholders": false,
   "schemaRequiresEncryption": true,
   "result": {
      "aggregate": "c1",
      "pipeline": [
         {
            "$lookup": {
               "from": "c2",
               "localField": "joinme",
               "foreignField": "joinme",
               "as": "matched"
            }
         },
         {
            "$match": {
               "e1": {
                  "$eq": {
                     "$binary": {
                        "base64": "ADAAAAAQYQABAAAABWtpABAAAAAEuJ2Njy8YQDuYKbzu2vEKQgJ2AAQAAABmb28AAA==",
                        "subType": "06"
                     }
                  }
               }
            }
         }
      ],
      "cursor": {}
   },
   "ok": {
      "$numberDouble": "1.0"
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/qe/000077500000000000000000000000001521103432300224745ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/qe/cmd-to-mongocryptd.json000066400000000000000000000033331521103432300271170ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   },
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {
            "properties": {
               "e1": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/qe/cmd.json000066400000000000000000000004731521103432300241360ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/qe/collInfo-c1.json000066400000000000000000000020531521103432300254350ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e1": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/csfle/qe/collInfo-c2.json000066400000000000000000000020201521103432300254300ustar00rootroot00000000000000{
    "name": "c2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/000077500000000000000000000000001521103432300226455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/000077500000000000000000000000001521103432300237415ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/cmd-to-mongocryptd.json000066400000000000000000000021411521103432300303600ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "matched.e2": "foo"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {},
         "isRemoteSchema": false
      },
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/cmd-to-mongod.json000066400000000000000000000011411521103432300272750ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "matched.e2": {
               "$eq": {
                  "$binary": {
                     "base64": "AbidjY8vGEA7mCm87trxCkIC8kZKVHsnE5QVf4xhKcl5AesA5UowxrXhd2IOU6/bQ5EjCTRjLABgU4qpPkwCtbkZX4COgHCmiyzOenkRVBV7NQ==",
                     "subType": "06"
                  }
               }
            }
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/cmd.json000066400000000000000000000005031521103432300253750ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "matched.e2": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/collInfo-c1.json000066400000000000000000000004411521103432300267010ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/collInfo-c2.json000066400000000000000000000020531521103432300267030ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e2": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/key-doc.json000066400000000000000000000013741521103432300261740ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/csfle/reply-from-mongocryptd.json000066400000000000000000000014131521103432300312720ustar00rootroot00000000000000{
   "hasEncryptionPlaceholders": false,
   "schemaRequiresEncryption": true,
   "result": {
      "aggregate": "c1",
      "pipeline": [
         {
            "$lookup": {
               "from": "c2",
               "localField": "joinme",
               "foreignField": "joinme",
               "as": "matched"
            }
         },
         {
            "$match": {
               "matched.e2": {
                  "$eq": {
                     "$binary": {
                        "base64": "ADAAAAAQYQABAAAABWtpABAAAAAEuJ2Njy8YQDuYKbzu2vEKQgJ2AAQAAABmb28AAA==",
                        "subType": "06"
                     }
                  }
               }
            }
         }
      ],
      "cursor": {}
   },
   "ok": {
      "$numberDouble": "1.0"
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/000077500000000000000000000000001521103432300245175ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/cmd-to-mongocryptd.json000066400000000000000000000010131521103432300311330ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "csfleEncryptionSchemas": {
      "db.c1": {
         "jsonSchema": {},
         "isRemoteSchema": false
      },
      "db.c2": {
         "jsonSchema": {},
         "isRemoteSchema": false
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/cmd-to-mongod.json000066400000000000000000000004731521103432300300620ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/cmd.json000066400000000000000000000004731521103432300261610ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/collInfo-c1.json000066400000000000000000000004411521103432300274570ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/collInfo-c2.json000066400000000000000000000004411521103432300274600ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/no-schema/reply-from-mongocryptd.json000066400000000000000000000007741521103432300320610ustar00rootroot00000000000000{
   "hasEncryptionPlaceholders": false,
   "schemaRequiresEncryption": true,
   "result": {
      "aggregate": "c1",
      "pipeline": [
         {
            "$lookup": {
               "from": "c2",
               "localField": "joinme",
               "foreignField": "joinme",
               "as": "matched"
            }
         },
         {
            "$match": {
               "e1": "foo"
            }
         }
      ],
      "cursor": {}
   },
   "ok": {
      "$numberDouble": "1.0"
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/000077500000000000000000000000001521103432300232525ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/cmd-to-mongocryptd.json000066400000000000000000000023651521103432300277010ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "matched.e2": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": []
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": {
                        "$numberInt": "0"
                     }
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/cmd-to-mongod.json000066400000000000000000000034121521103432300266110ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "matched.e2": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": []
            },
            "db.c2": {
                "escCollection": "enxcol_.c2.esc",
                "ecocCollection": "enxcol_.c2.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e2",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberInt": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/cmd.json000066400000000000000000000005031521103432300247060ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "matched.e2": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/collInfo-c1.json000066400000000000000000000004411521103432300262120ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c1",
    "idIndex": {
        "ns": "db.c1",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/collInfo-c2.json000066400000000000000000000020201521103432300262060ustar00rootroot00000000000000{
    "name": "c2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/key-doc.json000066400000000000000000000013741521103432300255050ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/no-schema/qe/reply-from-mongocryptd.json000066400000000000000000000041041521103432300306030ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "matched.e2": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": []
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e2",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": {
                                    "$numberInt": "0"
                                }
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/000077500000000000000000000000001521103432300214005ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/qe/csfle/000077500000000000000000000000001521103432300224745ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/qe/csfle/cmd-to-mongocryptd.json000066400000000000000000000033331521103432300271170ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   },
   "csfleEncryptionSchemas": {
      "db.c2": {
         "jsonSchema": {
            "properties": {
               "e2": {
                  "encrypt": {
                     "keyId": [
                        {
                           "$binary": {
                              "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                              "subType": "04"
                           }
                        }
                     ],
                     "bsonType": "string",
                     "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                  }
               }
            },
            "bsonType": "object"
         },
         "isRemoteSchema": true
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/csfle/cmd.json000066400000000000000000000004731521103432300241360ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/csfle/collInfo-c1.json000066400000000000000000000020201521103432300254270ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/csfle/collInfo-c2.json000066400000000000000000000020531521103432300254360ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "e2": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/000077500000000000000000000000001521103432300232525ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/cmd-to-mongocryptd.json000066400000000000000000000023551521103432300277000ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": {
                        "$numberInt": "0"
                     }
                  }
               }
            ]
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": []
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/cmd-to-mongod.json000066400000000000000000000032621521103432300266140ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "e1": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    }
                ]
            },
            "db.c2": {
                "escCollection": "enxcol_.c2.esc",
                "ecocCollection": "enxcol_.c2.ecoc",
                "fields": []
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/cmd.json000066400000000000000000000004731521103432300247140ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/collInfo-c1.json000066400000000000000000000020201521103432300262050ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/collInfo-c2.json000066400000000000000000000004411521103432300262130ustar00rootroot00000000000000{
    "type": "collection",
    "name": "c2",
    "idIndex": {
        "ns": "db.c2",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/key-doc.json000066400000000000000000000013741521103432300255050ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/no-schema/reply-from-mongocryptd.json000066400000000000000000000037441521103432300306140ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "e1": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": []
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/000077500000000000000000000000001521103432300245225ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/cmd-to-mongocryptd.json000066400000000000000000000017011521103432300311420ustar00rootroot00000000000000{
  "aggregate": "c1",
  "pipeline": [
    {
      "$lookup": {
        "from": "c2",
        "localField": "joinme",
        "foreignField": "joinme",
        "as": "matched"
      }
    },
    {
      "$match": {
        "encryptedUnindexed": "foo"
      }
    }
  ],
  "cursor": {},
  "encryptionInformation": {
    "type": {
      "$numberInt": "1"
    },
    "schema": {
      "db.c1": {
        "escCollection": "enxcol_.c1.esc",
        "ecocCollection": "enxcol_.c1.ecoc",
        "fields": [
          {
            "keyId": {
              "$binary": {
                "base64": "q83vqxI0mHYSNBI0VniQEg==",
                "subType": "04"
              }
            },
            "path": "encryptedUnindexed",
            "bsonType": "string"
          }
        ]
      }
    }
  },
  "csfleEncryptionSchemas": {
    "db.c2": {
      "jsonSchema": {
        "required": [
          "foo"
        ]
      },
      "isRemoteSchema": true
    }
  }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/cmd-to-mongod.json000066400000000000000000000020641521103432300300630ustar00rootroot00000000000000{
  "aggregate": "c1",
  "pipeline": [
    {
      "$lookup": {
        "from": "c2",
        "as": "matched",
        "localField": "joinme",
        "foreignField": "joinme"
      }
    },
    {
      "$match": {
        "encryptedUnindexed": {
          "$binary": {
            "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
            "subType": "06"
          }
        }
      }
    }
  ],
  "cursor": {},
  "encryptionInformation": {
    "type": {
      "$numberInt": "1"
    },
    "schema": {
      "db.c1": {
        "escCollection": "enxcol_.c1.esc",
        "ecocCollection": "enxcol_.c1.ecoc",
        "fields": [
          {
            "keyId": {
              "$binary": {
                "base64": "q83vqxI0mHYSNBI0VniQEg==",
                "subType": "04"
              }
            },
            "path": "encryptedUnindexed",
            "bsonType": "string"
          }
        ]
      }
    }
  }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/cmd.json000066400000000000000000000004371521103432300261640ustar00rootroot00000000000000{
  "aggregate": "c1",
  "pipeline": [
    {
      "$lookup": {
        "from": "c2",
        "localField": "joinme",
        "foreignField": "joinme",
        "as": "matched"
      }
    },
    {
      "$match": {
        "encryptedUnindexed": "foo"
      }
    }
  ],
  "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/collInfo-c1.json000066400000000000000000000012651521103432300274670ustar00rootroot00000000000000{
  "name": "c1",
  "type": "collection",
  "options": {
    "encryptedFields": {
      "escCollection": "enxcol_.c1.esc",
      "ecocCollection": "enxcol_.c1.ecoc",
      "fields": [
        {
          "keyId": {
            "$binary": {
              "base64": "q83vqxI0mHYSNBI0VniQEg==",
              "subType": "04"
            }
          },
          "path": "encryptedUnindexed",
          "bsonType": "string"
        }
      ]
    }
  },
  "info": {
    "readOnly": false,
    "uuid": {
      "$binary": {
        "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
        "subType": "04"
      }
    }
  },
  "idIndex": {
    "v": 2,
    "key": {
      "_id": 1
    },
    "name": "_id_"
  }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/collInfo-c2.json000066400000000000000000000005441521103432300274670ustar00rootroot00000000000000{
  "type": "collection",
  "name": "c2",
  "idIndex": {
    "ns": "db.c2",
    "name": "_id_",
    "key": {
      "_id": {
        "$numberInt": "1"
      }
    },
    "v": {
      "$numberInt": "2"
    }
  },
  "options": {
    "validator": {
      "$jsonSchema": {
        "required": [
          "foo"
        ]
      },
      "foo": "bar"
    }
  }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/key-doc.json000066400000000000000000000013741521103432300267550ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/non-csfle-schema/reply-from-mongocryptd.json000066400000000000000000000023301521103432300320520ustar00rootroot00000000000000{
  "hasEncryptionPlaceholders": false,
  "schemaRequiresEncryption": true,
  "result": {
    "aggregate": "c1",
    "pipeline": [
      {
        "$lookup": {
          "from": "c2",
          "as": "matched",
          "localField": "joinme",
          "foreignField": "joinme"
        }
      },
      {
        "$match": {
          "encryptedUnindexed": {
            "$binary": {
              "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
              "subType": "06"
            }
          }
        }
      }
    ],
    "cursor": {},
    "encryptionInformation": {
      "type": {
        "$numberInt": "1"
      },
      "schema": {
        "db.c1": {
          "escCollection": "enxcol_.c1.esc",
          "ecocCollection": "enxcol_.c1.ecoc",
          "fields": [
            {
              "keyId": {
                "$binary": {
                  "base64": "q83vqxI0mHYSNBI0VniQEg==",
                  "subType": "04"
                }
              },
              "path": "encryptedUnindexed",
              "bsonType": "string"
            }
          ]
        }
      }
    }
  },
  "ok": {
    "$numberDouble": "1.0"
  }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/000077500000000000000000000000001521103432300220055ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/cmd-to-mongocryptd.json000066400000000000000000000031721521103432300264310ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/cmd-to-mongod.json000066400000000000000000000043361521103432300253520ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "e1": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    }
                ]
            },
            "db.c2": {
                "escCollection": "enxcol_.c2.esc",
                "ecocCollection": "enxcol_.c2.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e2",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/cmd.json000066400000000000000000000004731521103432300234470ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/collInfo-c1.json000066400000000000000000000020201521103432300247400ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/collInfo-c2.json000066400000000000000000000020201521103432300247410ustar00rootroot00000000000000{
    "name": "c2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/key-doc.json000066400000000000000000000013741521103432300242400ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/mixed/qe/qe/reply-from-mongocryptd.json000066400000000000000000000051141521103432300273400ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "e1": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e2",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/000077500000000000000000000000001521103432300240525ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/cmd-to-mongocryptd.json000066400000000000000000000031721521103432300304760ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/cmd-to-mongod.json000066400000000000000000000043361521103432300274170ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "e1": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    }
                ]
            },
            "db.c2": {
                "escCollection": "enxcol_.c2.esc",
                "ecocCollection": "enxcol_.c2.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e2",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": 0
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/cmd.json000066400000000000000000000004731521103432300255140ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/collInfo-c1.json000066400000000000000000000020201521103432300270050ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/encryptedFieldsMap.json000066400000000000000000000010721521103432300305270ustar00rootroot00000000000000{
    "db.c2": {
        "escCollection": "enxcol_.c2.esc",
        "ecocCollection": "enxcol_.c2.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                    }
                },
                "path": "e2",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 0
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/key-doc.json000066400000000000000000000013741521103432300263050ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-encryptedFieldsMap/reply-from-mongocryptd.json000066400000000000000000000051141521103432300314050ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "e1": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e2",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/000077500000000000000000000000001521103432300212215ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/qe-self/cmd-to-mongocryptd.json000066400000000000000000000020231521103432300256370ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c1",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/cmd-to-mongod.json000066400000000000000000000031271521103432300245630ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c1",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "e1": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberInt": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/cmd.json000066400000000000000000000004731521103432300226630ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c1",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/collInfo-c1.json000066400000000000000000000020201521103432300241540ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/key-doc.json000066400000000000000000000013741521103432300234540ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-self/reply-from-mongocryptd.json000066400000000000000000000034451521103432300265610ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c1",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "e1": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/000077500000000000000000000000001521103432300226725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/cmd-to-mongocryptd.json000066400000000000000000000031721521103432300273160ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/cmd-to-mongod.json000066400000000000000000000045761521103432300262450ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        },
        {
            "$match": {
                "e1": {
                    "$binary": {
                        "base64": "DIkAAAAFZAAgAAAAAJg7KMGBFzQvSG5ipqeBSZ9oz6bVVQ7VWhOlAEb/g286BXMAIAAAAAAyC0j6Jl0oR17xumRgOxHlZSHkHtaQDkaV+ooVn7dVXgVsACAAAAAAxCCKn0pGHRJv9x2o1rDoFdTQfqbGN0anbP9RVNoYbrESY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        }
    ],
    "cursor": {},
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.c1": {
                "escCollection": "enxcol_.c1.esc",
                "ecocCollection": "enxcol_.c1.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e1",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberInt": "0"
                            }
                        }
                    }
                ]
            },
            "db.c2": {
                "escCollection": "enxcol_.c2.esc",
                "ecocCollection": "enxcol_.c2.ecoc",
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                "subType": "04"
                            }
                        },
                        "path": "e2",
                        "bsonType": "string",
                        "queries": {
                            "queryType": "equality",
                            "contention": {
                                "$numberInt": "0"
                            }
                        }
                    }
                ]
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/cmd.json000066400000000000000000000004731521103432300243340ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      },
      {
         "$match": {
            "e1": "foo"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/collInfo-c1.json000066400000000000000000000020201521103432300256250ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/collInfo-c2.json000066400000000000000000000020201521103432300256260ustar00rootroot00000000000000{
    "name": "c2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/key-doc.json000066400000000000000000000013741521103432300251250ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "NTpfCae5j0TnbRGsSvHOw7LcIPDhlg8//4N+TQllLZfH0nlj/G3+huCZmWcra+DPH2VbDnmcEmUTCmyPA+Qijjo0v+oL7qNNWUttL4dS8w5GOagQ3/kUtH+ZppgrjbYb1EcPP2G783iYLrTN+9J+fsLV3G36u2hLatGhDmRqDeV9erJOB0bEC69ouki054RWCNJ3AUockMNxxUDe/aQBKw==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1733927626824"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe-with-payload/reply-from-mongocryptd.json000066400000000000000000000051141521103432300302250ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            },
            {
                "$match": {
                    "e1": {
                        "$binary": {
                            "base64": "A1wAAAAQdAACAAAAEGEAAgAAAAVraQAQAAAABLidjY8vGEA7mCm87trxCkIFa3UAEAAAAAS4nY2PLxhAO5gpvO7a8QpCAnYABAAAAGZvbwASY20AAAAAAAAAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e2",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe/000077500000000000000000000000001521103432300202725ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/lookup/qe/cmd-to-mongocryptd.json000066400000000000000000000030611521103432300247130ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {},
   "encryptionInformation": {
      "type": {
         "$numberInt": "1"
      },
      "schema": {
         "db.c1": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e1",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         },
         "db.c2": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
               {
                  "keyId": {
                     "$binary": {
                        "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                        "subType": "04"
                     }
                  },
                  "path": "e2",
                  "bsonType": "string",
                  "queries": {
                     "queryType": "equality",
                     "contention": 0
                  }
               }
            ]
         }
      }
   }
}
libmongocrypt-1.19.0/test/data/lookup/qe/cmd-to-mongod.json000066400000000000000000000004201521103432300236250ustar00rootroot00000000000000{
    "aggregate": "c1",
    "pipeline": [
        {
            "$lookup": {
                "from": "c2",
                "as": "matched",
                "localField": "joinme",
                "foreignField": "joinme"
            }
        }
    ],
    "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/qe/cmd.json000066400000000000000000000003621521103432300217310ustar00rootroot00000000000000{
   "aggregate": "c1",
   "pipeline": [
      {
         "$lookup": {
            "from": "c2",
            "localField": "joinme",
            "foreignField": "joinme",
            "as": "matched"
         }
      }
   ],
   "cursor": {}
}
libmongocrypt-1.19.0/test/data/lookup/qe/collInfo-c1.json000066400000000000000000000020201521103432300232250ustar00rootroot00000000000000{
    "name": "c1",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c1.esc",
            "ecocCollection": "enxcol_.c1.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e1",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe/collInfo-c2.json000066400000000000000000000020201521103432300232260ustar00rootroot00000000000000{
    "name": "c2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.c2.esc",
            "ecocCollection": "enxcol_.c2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                            "subType": "04"
                        }
                    },
                    "path": "e2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 0
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "1dVWZUeyRZGZ3/NwfKTdLQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/lookup/qe/reply-from-mongocryptd.json000066400000000000000000000042761521103432300256350ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "aggregate": "c1",
        "pipeline": [
            {
                "$lookup": {
                    "from": "c2",
                    "as": "matched",
                    "localField": "joinme",
                    "foreignField": "joinme"
                }
            }
        ],
        "cursor": {},
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.c1": {
                    "escCollection": "enxcol_.c1.esc",
                    "ecocCollection": "enxcol_.c1.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e1",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                },
                "db.c2": {
                    "escCollection": "enxcol_.c2.esc",
                    "ecocCollection": "enxcol_.c2.ecoc",
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "uJ2Njy8YQDuYKbzu2vEKQg==",
                                    "subType": "04"
                                }
                            },
                            "path": "e2",
                            "bsonType": "string",
                            "queries": {
                                "queryType": "equality",
                                "contention": 0
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/mongocryptd-cmd.json000066400000000000000000000012631521103432300223570ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "ssn": "457-55-5462"
    },
    "jsonSchema": {
        "properties": {
            "ssn": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        },
        "bsonType": "object"
    },
    "isRemoteSchema": true
}libmongocrypt-1.19.0/test/data/mongocryptd-ismaster-17.json000066400000000000000000000003621521103432300236670ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1653058237588
    },
    "maxWireVersion": 17,
    "minWireVersion": 0,
    "ok": 1.0
}
libmongocrypt-1.19.0/test/data/mongocryptd-ismaster-26.json000066400000000000000000000003621521103432300236670ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1739534884679
    },
    "maxWireVersion": 26,
    "minWireVersion": 0,
    "ok": 1.0
}
libmongocrypt-1.19.0/test/data/mongocryptd-ismaster-27.json000066400000000000000000000003621521103432300236700ustar00rootroot00000000000000{
    "ismaster": true,
    "iscryptd": true,
    "maxBsonObjectSize": 16777216,
    "maxMessageSizeBytes": 48000000,
    "localTime": {
        "$date": 1739534884679
    },
    "maxWireVersion": 27,
    "minWireVersion": 0,
    "ok": 1.0
}
libmongocrypt-1.19.0/test/data/mongocryptd-reply-existing-ciphertext.json000066400000000000000000000013121521103432300267470ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA", 
                    "subType": "06"
                }
            },
            "existing_ciphertext": {
                "$binary": {
                    "base64": "AgFkgAAAAAAAAAAAAAAAAAABm5MUrpGaLc8+PUdwNPpanCc3Jk1Ql+URqTAZV0IPhLTiw0JkZTN8gcNjA0PGjUwGzR08ZIeDhreuVLvKnX+jZw==",
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-invalid.json000066400000000000000000000006311521103432300243710ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ACUAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQA=", 
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-key-alt-name.json000066400000000000000000000006511521103432300252310ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ADAAAAAQYQABAAAAAmthAAkAAABTaGFybGVuZQACdgAMAAAANDU3LTU1LTU0NjIAAA==", 
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-key-alt-name2.json000066400000000000000000000006611521103432300253140ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ADgAAAAQYQABAAAAAmthABEAAABBbm90aGVyIGFsdCBuYW1lAAJ2AAwAAAA0NTctNTUtNTQ2MgAA", 
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-no-encryption-needed.json000066400000000000000000000001771521103432300267760ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    }, 
    "schemaRequiresEncryption": false, 
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-no-markings.json000066400000000000000000000001761521103432300251740ustar00rootroot00000000000000{
    "ok": {
        "$numberInt": "1"
    }, 
    "schemaRequiresEncryption": true, 
    "hasEncryptedPlaceholders": false
}libmongocrypt-1.19.0/test/data/mongocryptd-reply-random.json000066400000000000000000000006611521103432300242260ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ADgAAAAQYQACAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA", 
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/data/multikey/000077500000000000000000000000001521103432300202175ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/multikey/command.json000066400000000000000000000001471521103432300225320ustar00rootroot00000000000000{
    "find": "coll",
    "filter": {
        "uses_key_a": "foo",
        "uses_key_b": "bar"
    }
}
libmongocrypt-1.19.0/test/data/multikey/key-document-a.json000066400000000000000000000020461521103432300237360ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyAltNames": [
        "aaaaaaaaaaaaaaaa"
    ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1641016800000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1641016860000"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws"
    }
}
libmongocrypt-1.19.0/test/data/multikey/key-document-b.json000066400000000000000000000021461521103432300237400ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YmJiYmJiYmJiYmJiYmJiYg==",
            "subType": "04"
        }
    },
    "keyAltNames": [
        "bbbbbbbbbbbbbbbb",
        "additionalAltName"
    ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEGkNTybTc7Eyif0f+qqE0lAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDB2j78AeuIQxcRh8cQIBEIB7vj9buHEaT7XHFIsKBJiyzZRmNnjvqMK5LSdzonKdx97jlqauvPvTDXSsdQDcspUs5oLrGmAXpbFResscxmbwZoKgUtWiuIOpeAcYuszCiMKt15s1WIMLDXUhYtfCmhRhekvgHnRAaK4HJMlGE+lKJXYI84E0b86Cd/g+",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1641020400000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1641020460000"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws",
        "endpoint": "example.com"
    }
}
libmongocrypt-1.19.0/test/data/multikey/mongocryptd_reply.json000066400000000000000000000017701521103432300246770ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "find": "coll",
        "filter": {
            "$and": [
                {
                    "uses_key_a": {
                        "$eq": {
                            "$binary": {
                                "base64": "ADAAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAQAAABmb28AAA==",
                                "subType": "06"
                            }
                        }
                    }
                },
                {
                    "uses_key_b": {
                        "$eq": {
                            "$binary": {
                                "base64": "ADAAAAAQYQABAAAABWtpABAAAAAEYmJiYmJiYmJiYmJiYmJiYgJ2AAQAAABiYXIAAA==",
                                "subType": "06"
                            }
                        }
                    }
                }
            ]
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/multikey/schema_map.json000066400000000000000000000021161521103432300232070ustar00rootroot00000000000000{
    "db.coll": {
        "properties": {
            "uses_key_a": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            },
            "uses_key_b": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "YmJiYmJiYmJiYmJiYmJiYg==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        },
        "bsonType": "object"
    }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/000077500000000000000000000000001521103432300211005ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/no-trimFactor/find/000077500000000000000000000000001521103432300220205ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/no-trimFactor/find/cmd.json000066400000000000000000000002211521103432300234510ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "plainText": "sample",
        "encrypted": {
            "$numberInt": "123456"
        }
    }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/find/encrypted-field-map.json000066400000000000000000000006011521103432300265410ustar00rootroot00000000000000{
   "db.test": {
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range"
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/find/encrypted-payload.json000066400000000000000000000042501521103432300263400ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "$and": [
            {
                "plainText": {
                    "$eq": "sample"
                }
            },
            {
                "$and": [
                    {
                        "encrypted": {
                            "$gte": {
                                "$binary": {
                                    "base64": "DQEBAAADcGF5bG9hZACZAAAABGcAhQAAAAMwAH0AAAAFZAAgAAAAABVoe9wkcCyHpbOGCU+k9auwnEcS2CtNpRqiecg98ONpBXMAIAAAAABvqHHsxgHdi12FXVttMVYYwNK4R2zYUjeXQHAURNBvzwVsACAAAAAAfmp4QWZF7LiYXH6RYjf1kc6OpgRnCadlHki6cRrMilsAABJjbQAIAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAACAEG14AP///38A",
                                    "subType": "06"
                                }
                            }
                        }
                    },
                    {
                        "encrypted": {
                            "$lte": {
                                "$binary": {
                                    "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                                    "subType": "06"
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range"
                        }
                    }
                ],
                "escCollection": "enxcol_.test.esc",
                "ecocCollection": "enxcol_.test.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/find/mongocryptd-reply.json000066400000000000000000000052551521103432300264200ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "find": "test",
        "filter": {
            "$and": [
                {
                    "plainText": {
                        "$eq": "sample"
                    }
                },
                {
                    "$and": [
                        {
                            "encrypted": {
                                "$gte": {
                                    "$binary": {
                                        "base64": "AwABAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAoQAAAANlZGdlc0luZm8AWwAAABBsb3dlckJvdW5kAEDiAQAIbGJJbmNsdWRlZAABEHVwcGVyQm91bmQAQOIBAAh1YkluY2x1ZGVkAAEQaW5kZXhNaW4AAAAAgBBpbmRleE1heAD///9/ABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAIAAAAQc2Vjb25kT3BlcmF0b3IABAAAAAASY20ACAAAAAAAAAAScwABAAAAAAAAAAA=",
                                        "subType": "06"
                                    }
                                }
                            }
                        },
                        {
                            "encrypted": {
                                "$lte": {
                                    "$binary": {
                                        "base64": "A5oAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAOwAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAIAAAAQc2Vjb25kT3BlcmF0b3IABAAAAAASY20ACAAAAAAAAAAScwABAAAAAAAAAAA=",
                                        "subType": "06"
                                    }
                                }
                            }
                        }
                    ]
                }
            ]
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range"
                            }
                        }
                    ],
                    "escCollection": "enxcol_.test.esc",
                    "ecocCollection": "enxcol_.test.ecoc"
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/insert/000077500000000000000000000000001521103432300224045ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/no-trimFactor/insert/cmd.json000066400000000000000000000002711521103432300240420ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberInt": "123456"
            }
        }
    ]
}libmongocrypt-1.19.0/test/data/no-trimFactor/insert/encrypted-field-map.json000066400000000000000000000006011521103432300271250ustar00rootroot00000000000000{
   "db.test": {
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range"
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/insert/encrypted-payload.json000066400000000000000000000201431521103432300267230ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "CyAVAAAFZAAgAAAAAFX/OCQuK7sgFC5OgvIzRF0iZF5yZhqqgVEw+BDk04D/BXMAIAAAAADf8ZGTnPqqur9tusELGqTbSNtoJgj8i071DXm7kQpcSgVwADEAAAAAAwECAwECAwECAwECAwECAz+VM8kdZjiqXJ2Kga/NSZuITHP/iMRHFXZ17XdXy4FMWwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAABI0VngSNJh2EjQSNFZ4kBIBAgMBAgMBAgMBAgMBAgMB978eymjOG8LzTgudRZqpbK4Tm1RK3tB9QMB9lTWKiy6Wqp7twzRA/ZbjQ69RKj60BWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAGAAAAAAAAAARnAJkTAAADMAC2AAAABWQAIAAAAABx4c6MPMR3Yyxzi0dTMjQ7UdRNgI8uP2LTi1vhGYnLMAVzACAAAAAAon13gVyXE18K3ylqp/ovYStGulVBaekS2mKVRFq7RQgFbAAgAAAAAH5qeEFmRey4mFx+kWI39ZHOjqYEZwmnZR5IunEazIpbBXAAMQAAAAACAwECAwECAwECAwECAwECy273HayEz3hgoMYG56e0sxacPePKwPiqCCS575wlTLMxAAMxALYAAAAFZAAgAAAAALhKoC9+IzmWByMV7+lxHqBz30ckyd4wjXTJIaxvSHGQBXMAIAAAAADOFYCxSi5cl0dHBBpG2MuoCMK4VW7bnHkT4W0b8/S9QAVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgMucSLry7LOh6S3NFriDyboyFWjjO7jUCKQmfnXNTVgRlsAAzIAtgAAAAVkACAAAAAA+QfEdnHiCdqN2uZKt8sulOoCI4Je4Lf2KfPWurcO6kcFcwAgAAAAAFvZ404SMykRwVDQtyApzVblLcZf0hl98cHQjbouG3VABWwAIAAAAAAYo/AvCQYlIUoNWV6sySJLiHk24w/YX3vRwwLVDjDsUgVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAele8PzNsBFG/0E0aRIa/STYmSf8Er7TW/aD841prevDywADMwC2AAAABWQAIAAAAADp37F20ZoHis9Y04z918sGyOCzOAnedki7o0xjs8aV4wVzACAAAAAA6gskIxd1RdPkI/t10Xs2RuQfNNaYAl0RCc/fEb9W590FbAAgAAAAAMf1cfJpJNlRk23xnpecqLu+IubHI7xMG6hGbCEA6QiiBXAAMQAAAAACAwECAwECAwECAwECAwECgxikv+dmmfSOXBQZkSatlNnFs2ATq0yp24nzunnI7mYwAAM0ALYAAAAFZAAgAAAAAG1JiCyGj5jLeMtQ0/UdOjg7P/gpwnYf6zlmRUnSU9tXBXMAIAAAAACtFe8XxEL7jbB4o17AIUTZ77heOzagFgk+Z2eL+QFeDQVsACAAAAAADYD3CyooN8IiEWsc1sPc1xCoRwH4F/+IYxDCubSkq+AFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgNNcU1NRd5pnVOIkx5k9qmZLy9F4raY2lK9H/NHP8CDC1sAAzUAtgAAAAVkACAAAAAAR3PxltBrzRZyAM57P9ciNMRpfyK+XB+QS0HJB/pFd3wFcwAgAAAAAEJoiSyFo2mxiXooeBm6E+rf3J90hTozFIFKSa5kPF1BBWwAIAAAAADmWysyKhf8UccKx78IliHTWao9pe/AwmLMDbMAnVJHPQVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAfDvmp5aIFHmt2vMpiuJI5jiaH7XRZ2dvrYZN5kjisPCywADNgC2AAAABWQAIAAAAACXnb80p8rHO1VZHz+NTqjUpoimkBybXrjMNLPk0Y2OUwVzACAAAAAAwRn8p8mm9fil7GXa4feiUPoaknS8X/eHqhu+Ynx2HEoFbAAgAAAAAAMUtyBHzqCfsTv4qaEnDPoB5Yb8qT4VjUppsq0/OY0mBXAAMQAAAAACAwECAwECAwECAwECAwECqAp8Ozm1Kd/Pk4q2oao5gsfAFcI39uY/eF2SybroFfEwAAM3ALYAAAAFZAAgAAAAAOW+ccgZnspe3Zk0B15sNoxYs+++vkIhLYDssUWrpf+ZBXMAIAAAAAC13nmuhE1cSoIdkJleu6iHCYZARpCxvagwSrdjvgaRUgVsACAAAAAAIrk7klUCamSAp05f9qK6PqR041yRVCaJ7dctpSUm5/gFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgNVutv0BdHOWmHtoNn6bEXHyRFbnxCJcfOzMiOveMdMVFsAAzgAtgAAAAVkACAAAAAAxbzcIam2ob0pklDpzLYr/BteO9cOCoJ0aWD6fGucXl0FcwAgAAAAAP5nX0Erewnh2CHdBDf3QsCG2XW2z90KD5B6Y+rzAl6ZBWwAIAAAAAAuKpEiz1IaNOIZ1+6lpo/M5wuUuwcW1GwgOCkqjExoOAVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAUzgTPP0+DG25jA52gXEcrK7bZQVD3qkpacpHd20tMAaywADOQC2AAAABWQAIAAAAAAcPMjPbOYfcAFEE0S3iUwoG9sWv8iy+fXbRTI5lfZ87QVzACAAAAAALfwKPHpI8mYj7xwh0i1P8MCp0LElos16pjLHMYy17gYFbAAgAAAAAHaai/vRhi3N4fiKYQomGNBSheDejcjnijIsVNmCu7vhBXAAMQAAAAACAwECAwECAwECAwECAwECRO+KoIpbLkFJkPNNknDUIv1zVweuC9zCdHTrmkor570wAAMxMAC2AAAABWQAIAAAAAAmoUbqGpreYSpgpkRV3PcLPwT90pv4J6Sq9hkzr1UyawVzACAAAAAAYK4KHqanlnpDmejdMXMo9/IdK47uVhuMyw1Lmg2S53gFbAAgAAAAAELpvtDOvYFsTO0EEkccCZpolG2C2oRSTimCz5l11QLTBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDgMqoRCc7BGqgadidlaTFtzKKMFdubtfXSHXfVstTOn5bAAMxMQC2AAAABWQAIAAAAAD0ugfk8nq6aFeln4+QLIhFyOkLafGcozN8MvybVLzaRgVzACAAAAAAC1ORUt0477aATkfBAN86Ky6TzUN4zYnGFdZw5VLJ3hcFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5BXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBudSC4AK71+G+X6MfMuwKWRMnLOC4aidsIoUO0hV/QJTLAAMxMgC2AAAABWQAIAAAAADyKcEZ3l9iBRpFzfpofEfy1FkKktV1wzi2YMq438HAiQVzACAAAAAAJKWF4iNZdB6uI0t36zEsQpTi3cjhjMlUgJKxrQ5j19UFbAAgAAAAADj8Gb1dH1WJp8Z7W4Byzvjen38gy94jJ+bF4zcH8tB9BXAAMQAAAAACAwECAwECAwECAwECAwECTbYFftNKqDnEXKQbq2y3kKk4Wn5qJdjsUtSdBsj93m4wAAMxMwC2AAAABWQAIAAAAADQjZ9MCafSB+PIyoWl6zDRGcS/vGxSwQyuHHXESorF8AVzACAAAAAAMGC5dQYjHcHV9A+JDoOjcv7wEoZMiy6WCzo//5fJ7/EFbAAgAAAAAA8R9s/RrnxTOkU+fqYFsnAm0kmNKXhr0V3k/1OrtuTRBXAAMQAAAAADAQIDAQIDAQIDAQIDAQID0AQbL4e/j9E2BD/JqlROMj5nCV/Ms+LNiEKrM1EIMvdbAAMxNAC2AAAABWQAIAAAAADIgkDOvv4jRMk0e6Hw1NmMK/mrkkE68c74ahE/puHvbQVzACAAAAAA4CP/wdQvRwOl5t4mMgbwyhqjv01uiZveIj+U0dDmeJoFbAAgAAAAADpXRUP1KWfQopIEZgILyQjoq1rNc1SAgsP1U5QU0CREBXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBUqTscwusf1Sb9zr4ADXAuCcXXu6uLjV0FWzq5pdQ5hnLAAMxNQC2AAAABWQAIAAAAAClQOAmtdtHutim1zys0oMAxMkmClbHfAnkSLoTEjhwqwVzACAAAAAAVgt1FbCG6npznV3DHPsm1Zn2GJZMjjlE0wTUcCI0tOUFbAAgAAAAAArpkUe9A6CGOWFewttmaEMuVb6cJXc2tkP1R7vLf/SeBXAAMQAAAAACAwECAwECAwECAwECAwECPxj1iUCVNl0Z4rKvXKa9B6QsnyDHJyj8AUL42+SqvV4wAAMxNgC2AAAABWQAIAAAAACsc01M1lll1fAJc/LmqhR8KmQqlJ0ZGjHJ/Af0Jt3dzAVzACAAAAAAJ2n9ToriAiOreicCfmkRn3naQ7bIul0Xyjvnp6XTJbwFbAAgAAAAAKF92FNer/Q+vMlgRHGXnQP+EX9HQa2YYjTSon7O2IxDBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDxw1fFAt+kDNIihdC2r7837lNWG9IgpFMSUNza2MS+LpbAAMxNwC2AAAABWQAIAAAAACnNRIgrtUTrL9bvDzNfdzpGsdqBElHVWVpYyPXwSwrvgVzACAAAAAAjmBoFQHWGwST1BE0yaBiekIcYVsoUf/BSrLwO9UhtEsFbAAgAAAAAEe9aNvZUOhoa4IYjG0TUFY0Lbx6KYY//F0HqCDbN/u7BXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBPOd7p95VI1OtxfXq+5NSCH+ogPjo9lFrfeGODJKXKsjLAAMxOAC2AAAABWQAIAAAAAB69tLt+1PAUprgtKZWy3B9JL6GclTOi3dOgCCHgqPBnwVzACAAAAAAB8hNPjMyYRMHnH9XCVrcICu4ISWW3eCEUcSXZng35MAFbAAgAAAAAEcoyl5TBd6oEp+t0qD1tN0yjRtlGoa54w65U3dhexqRBXAAMQAAAAACAwECAwECAwECAwECAwECbtvNosMhvTRt45A7SQdH8hZippMddPE8g4K7zb6p7XswAAMxOQC2AAAABWQAIAAAAADO9uLhaBzFY2pp5k2huL0y7Ia9bbX9Td+8jbnze8vEoAVzACAAAAAAqE4YrbpDF0KstI1C6t+5gafrNKpWWc0gzHNa1VbxPjgFbAAgAAAAABkNIn+3njU/Zxu3Jz1yp3hxtx8KveIfzSQx6uLAsQLYBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDSCq69zvfhVJPRL0CTghUwWd8L3PWYQF7TwvOGZAw4z5bAAMyMAC2AAAABWQAIAAAAADUDjjIXc3gEKI0xh3UbzZvVMjQEJ3pHZDlee13ah248wVzACAAAAAAWfptH702DcaNwRMi0N3AN7Tr5qZhuecBsaFvVotkeOwFbAAgAAAAAO8B7P2R0zSBT5JbmwInpRsNZ2IJUMuL8l9VZjftP3WiBXAAMQAAAAABAgMBAgMBAgMBAgMBAgMB631+rWK1NZGz0Pf84u7wRYlfBwWhHkmrhvIRYczS5m/LAAMyMQC2AAAABWQAIAAAAABugPtcm/HV5MD8yyuw4bpcdO1+K8CkSbE/dhzK93fyJAVzACAAAAAAuKJP+IQVI2kYA02OeeHFQEmvuVVPFYtpD7nAcl4U8rYFbAAgAAAAAJRkWtjoKRlC5wNyXtyBUyPeH8hFa0fcZTw8R8alLt7QBXAAMQAAAAACAwECAwECAwECAwECAwEC0bHPZHQG/05yfKLiObxeknR1PuPEvJrR3f/s2ZiK+w0wAAMyMgC2AAAABWQAIAAAAAAtxq6iNLgn0d0g2oKCMOXZWf8LooZXa7CkhMlzX3U12gVzACAAAAAALUryeDpD1NktbkYTXUpsNQIJYr8I+fZBz4mFLSgtk44FbAAgAAAAAA9jJQaYWTOtI4mag6rZ7zycKpIHLEW0IkS8GCF5qSaMBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDzS5QIrvfRsnOnnZT+Z2BdcKeeWaIwToaTPER4e7sTohbAAMyMwC2AAAABWQAIAAAAAC6uNqCo6p+ohuqlioUo7bhj6L+0kJpFl3yLYfYhqbnrAVzACAAAAAAH6WDk481/wTTL/Y6t765GCYPH0niPXWzamTeZ6lEQkcFbAAgAAAAABhXLMOXrnco598ejjUHAkNGZR8eNVnDMWrAuQ16j2O/BXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBrSKQIVC2x1PtPhLkhY2Jahu7/uoimtsZXTegUO7y3MTLAAMyNAC2AAAABWQAIAAAAABkREAiEohu2Q1tDoksY567QbeolBSWlN9kY2VxQk2FnAVzACAAAAAAvDKcTZwlJBLt/1aRXy5ekX9Z/YcbhakokeaeY74zV4kFbAAgAAAAAEnd5ZUyXOLvEAhNT4ZKIDWE3wcv5mdo0crUrZauKMbnBXAAMQAAAAACAwECAwECAwECAwECAwEC1SEc0Ww2+DWHgLn9H3PFQ0KDejGQLLiQQ6CyyHitXjIwAAMyNQC2AAAABWQAIAAAAABn8GOJ0BUhqxsCMujadpEuMjQH/JqjdSe+d7ck4BJpBgVzACAAAAAA/+5+gxu0s/JHvMroY0hgg4/pNDl6+FbjkVVL4pp0wKAFbAAgAAAAAIZjTmkgXeFszdl7b+WoFTth6LSUyRYvg6eh5JiOaoBYBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDH4rc2ZooIeKkTPqox5+Nw09+L+D6wJq4Ei3fLly1HaZbAAMyNgC2AAAABWQAIAAAAAA+kw8ODbANBx/7vTQYS+vKeqcxI7s64zzuGVo9ISg+EAVzACAAAAAAijiBsW795UOqu7fNmYqbj7uSBToUCGU/mdufHc5RCl0FbAAgAAAAAFIO6h0rFmqi1XsueSDzF02jzB81vUi+aMj/LuTEHIdqBXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBOL+SA7F+3RSUqlMTq7mr/YYm5JnUr8uVrojhKonnlN7LAAASc3AAAQAAAAAAAAAQdGYABgAAABBtbgAAAACAEG14AP///38A",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range"
                        }
                    }
                ],
                "escCollection": "enxcol_.test.esc",
                "ecocCollection": "enxcol_.test.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/no-trimFactor/insert/mongocryptd-reply.json000066400000000000000000000030141521103432300267730ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "insert": "test",
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range"
                            }
                        }
                    ],
                    "escCollection": "enxcol_.test.esc",
                    "ecocCollection": "enxcol_.test.ecoc"
                }
            }
        },
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A30AAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAHgAAABB2AEDiAQAQbWluAAAAAIAQbWF4AP///38AEmNtAAgAAAAAAAAAEnMAAQAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        ]
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/000077500000000000000000000000001521103432300210735ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/qe_keyAltName/cleanup/000077500000000000000000000000001521103432300225225ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/qe_keyAltName/cleanup/cmd-to-mongod.json000066400000000000000000000004021521103432300260550ustar00rootroot00000000000000{
    "cleanupStructuredEncryptionData": "coll",
    "cleanupTokens": {
        "secret": {
            "$binary": {
                "base64" : "PZsCLRX3GB1seYw7TB452mz1LjJQq1P+hH1cX0ECbO0=",
                "subType" : "00"
            }
        }
    }
}

libmongocrypt-1.19.0/test/data/qe_keyAltName/cleanup/cmd.json000066400000000000000000000000631521103432300241570ustar00rootroot00000000000000{
    "cleanupStructuredEncryptionData": "coll"
}

libmongocrypt-1.19.0/test/data/qe_keyAltName/cleanup/key-document.json000066400000000000000000000014711521103432300260240ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "db27rshiqK4Jqhb2xnwK4RfdFb9JuKeUe6xt5aYQF4o62tS75b7B4wxVN499gND9UVLUbpVKoyUoaZAeA895OENP335b8n8OwchcTFqS44t+P3zmhteYUQLIWQXaIgon7gEgLeJbaDHmSXS6/7NbfDDFlB37N7BP/2hx1yCOTN6NG/8M1ppw3LYT3CfP6EfXVEttDYtPbJpbb7nBVlxD7w==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    },
    "keyAltNames": [
        "keyDocumentName"
    ]
}

libmongocrypt-1.19.0/test/data/qe_keyAltName/cmd-to-mongocryptd.json000066400000000000000000000014401521103432300255130ustar00rootroot00000000000000{
    "insert": "coll",
    "documents": [
        {
            "secret": "bar"
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.coll": {
                "fields": [
                    {
                        "path": "secret",
                        "bsonType": "string",
                        "keyId": {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    }
                ],
                "escCollection": "enxcol_.coll.esc",
                "ecocCollection": "enxcol_.coll.ecoc"
            }
        }
    }
}libmongocrypt-1.19.0/test/data/qe_keyAltName/compact/000077500000000000000000000000001521103432300225215ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/qe_keyAltName/compact/cmd-to-mongod.json000066400000000000000000000004041521103432300260560ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "coll",
    "compactionTokens": {
        "secret": {
            "$binary": {
                "base64" : "PZsCLRX3GB1seYw7TB452mz1LjJQq1P+hH1cX0ECbO0=",
                "subType" : "00"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/compact/cmd.json000066400000000000000000000000621521103432300241550ustar00rootroot00000000000000{
    "compactStructuredEncryptionData": "coll"
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/compact/key-document.json000066400000000000000000000014671521103432300260300ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "db27rshiqK4Jqhb2xnwK4RfdFb9JuKeUe6xt5aYQF4o62tS75b7B4wxVN499gND9UVLUbpVKoyUoaZAeA895OENP335b8n8OwchcTFqS44t+P3zmhteYUQLIWQXaIgon7gEgLeJbaDHmSXS6/7NbfDDFlB37N7BP/2hx1yCOTN6NG/8M1ppw3LYT3CfP6EfXVEttDYtPbJpbb7nBVlxD7w==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    },
    "keyAltNames": [
        "keyDocumentName"
    ]
}libmongocrypt-1.19.0/test/data/qe_keyAltName/create/000077500000000000000000000000001521103432300223365ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/qe_keyAltName/create/cmd-to-mongocryptd.json000066400000000000000000000017561521103432300267700ustar00rootroot00000000000000{
    "create": "coll",
   "encryptionInformation": {
        "type": 1,
        "schema": {
            "db.coll": {
                "fields": [
                    {
                        "path": "secret",
                        "bsonType": "string",
                        "keyId": {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    }
                ],
                "escCollection": "enxcol_.coll.esc",
                "ecocCollection": "enxcol_.coll.ecoc"
            }
        }
    },
    "encryptedFields": {
      "fields": [
         {
            "path": "secret",
            "bsonType": "string",
            "keyId": {
                "$binary": {
                    "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                    "subType": "04"
                }
            }
         }
      ]
   }
}libmongocrypt-1.19.0/test/data/qe_keyAltName/create/cmd-to-mongod.json000066400000000000000000000006051521103432300256760ustar00rootroot00000000000000{
    "create": "coll",
    "encryptedFields": {
        "fields": [
            {
                "path": "secret",
                "bsonType": "string",
                "keyId": {
                    "$binary": {
                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                        "subType": "04"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/create/cmd.json000066400000000000000000000003531521103432300237750ustar00rootroot00000000000000{
    "create": "coll",
    "encryptedFields": {
        "fields": [
            {
                "path": "secret",
                "bsonType": "string",
                "keyAltName": "keyDocumentName"
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/create/key-document.json000066400000000000000000000014671521103432300256450ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "db27rshiqK4Jqhb2xnwK4RfdFb9JuKeUe6xt5aYQF4o62tS75b7B4wxVN499gND9UVLUbpVKoyUoaZAeA895OENP335b8n8OwchcTFqS44t+P3zmhteYUQLIWQXaIgon7gEgLeJbaDHmSXS6/7NbfDDFlB37N7BP/2hx1yCOTN6NG/8M1ppw3LYT3CfP6EfXVEttDYtPbJpbb7nBVlxD7w==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    },
    "keyAltNames": [
        "keyDocumentName"
    ]
}libmongocrypt-1.19.0/test/data/qe_keyAltName/create/reply-from-mongocryptd.json000066400000000000000000000011271521103432300276710ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": false,
    "schemaRequiresEncryption": true,
    "result": {
        "create": "coll",
        "encryptedFields": {
            "fields": [
                {
                    "path": "secret",
                    "bsonType": "string",
                    "keyId": {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    }
                }
            ]
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/key-document-aws.json000066400000000000000000000021301521103432300251560ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "version": {
        "$numberLong": "0"
    },
    "keyAltNames": [
        "keyDocumentName"
    ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1553026537755"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws"
    }
}
libmongocrypt-1.19.0/test/data/qe_keyAltName/key-document.json000066400000000000000000000014671521103432300244020ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "db27rshiqK4Jqhb2xnwK4RfdFb9JuKeUe6xt5aYQF4o62tS75b7B4wxVN499gND9UVLUbpVKoyUoaZAeA895OENP335b8n8OwchcTFqS44t+P3zmhteYUQLIWQXaIgon7gEgLeJbaDHmSXS6/7NbfDDFlB37N7BP/2hx1yCOTN6NG/8M1ppw3LYT3CfP6EfXVEttDYtPbJpbb7nBVlxD7w==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1232739599082000"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    },
    "keyAltNames": [
        "keyDocumentName"
    ]
}libmongocrypt-1.19.0/test/data/qe_keyAltName/reply-from-mongocryptd.json000066400000000000000000000024711521103432300264310ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "insert": "coll",
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.coll": {
                    "fields": [
                        {
                            "path": "secret",
                            "bsonType": "string",
                            "keyId": {
                                "$binary": {
                                    "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                    "subType": "04"
                                }
                            }
                        }
                    ],
                    "escCollection": "enxcol_.coll.esc",
                    "ecocCollection": "enxcol_.coll.ecoc"
                }
            }
        },
        "documents": [
            {
                "secret": {
                    "$binary": {
                        "base64": "A1wAAAAQdAABAAAAEGEAAQAAAAVraQAQAAAABGFhYWFhYWFhYWFhYWFhYWEFa3UAEAAAAARhYWFhYWFhYWFhYWFhYWFhAnYABAAAAGJhcgASY20AAAAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        ]
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/range-edge-generation/000077500000000000000000000000001521103432300225035ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-edge-generation/edges_decimal128.cstruct000066400000000000000000033755451521103432300271420ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128(0),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0",
    "01",
    "010",
    "0101",
    "01010",
    "010100",
    "0101000",
    "01010001",
    "010100011",
    "0101000111",
    "01010001110",
    "010100011100",
    "0101000111001",
    "01010001110011",
    "010100011100110",
    "0101000111001101",
    "01010001110011010",
    "010100011100110101",
    "0101000111001101011",
    "01010001110011010111",
    "010100011100110101110",
    "0101000111001101011100",
    "01010001110011010111000",
    "010100011100110101110001",
    "0101000111001101011100010",
    "01010001110011010111000100",
    "010100011100110101110001001",
    "0101000111001101011100010010",
    "01010001110011010111000100100",
    "010100011100110101110001001001",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100",
    "0101000111001101011100010010011001",
    "01010001110011010111000100100110010",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001",
    "01010001110011010111000100100110010010",
    "010100011100110101110001001001100100101",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100",
    "010100011100110101110001001001100100101000",
    "0101000111001101011100010010011001001010000",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011",
    "0101000111001101011100010010011001001010000111",
    "01010001110011010111000100100110010010100001110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111",
    "010100011100110101110001001001100100101000011100001110",
    "0101000111001101011100010010011001001010000111000011101",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101",
    "0101000111001101011100010010011001001010000111000011101011",
    "01010001110011010111000100100110010010100001110000111010110",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "0101000111001101011100010010011001001010000111000011101011000101000",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111110",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01",
    "0101",
    "010100",
    "01010001",
    "0101000111",
    "010100011100",
    "01010001110011",
    "0101000111001101",
    "010100011100110101",
    "01010001110011010111",
    "0101000111001101011100",
    "010100011100110101110001",
    "01010001110011010111000100",
    "0101000111001101011100010010",
    "010100011100110101110001001001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001",
    "010100011100110101110001001001100100",
    "01010001110011010111000100100110010010",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000",
    "0101000111001101011100010010011001001010000111000011",
    "010100011100110101110001001001100100101000011100001110",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011",
    "010100011100110101110001001001100100101000011100001110101100",
    "01010001110011010111000100100110010010100001110000111010110001",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "010",
    "010100",
    "010100011",
    "010100011100",
    "010100011100110",
    "010100011100110101",
    "010100011100110101110",
    "010100011100110101110001",
    "010100011100110101110001001",
    "010100011100110101110001001001",
    "010100011100110101110001001001100",
    "010100011100110101110001001001100100",
    "010100011100110101110001001001100100101",
    "010100011100110101110001001001100100101000",
    "010100011100110101110001001001100100101000011",
    "010100011100110101110001001001100100101000011100",
    "010100011100110101110001001001100100101000011100001",
    "010100011100110101110001001001100100101000011100001110",
    "010100011100110101110001001001100100101000011100001110101",
    "010100011100110101110001001001100100101000011100001110101100",
    "010100011100110101110001001001100100101000011100001110101100010",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "010100011100110101110001001001100100101000011100001110101100010100001",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101",
    "01010001",
    "010100011100",
    "0101000111001101",
    "01010001110011010111",
    "010100011100110101110001",
    "0101000111001101011100010010",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011",
    "01010001110011010111000100100110010010100001110000111010",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001",
    "0101000111001101",
    "010100011100110101110001",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010",
    "010100011100110101110001001001100100101000011100",
    "01010001110011010111000100100110010010100001110000111010",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111",
    "01010001110011010111",
    "010100011100110101110001001001",
    "0101000111001101011100010010011001001010",
    "01010001110011010111000100100110010010100001110000",
    "010100011100110101110001001001100100101000011100001110101100",
    "0101000111001101011100010010011001001010000111000011101011000101000010",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000000000000000000010111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110",
    "0101000111001101011100",
    "010100011100110101110001001001100",
    "01010001110011010111000100100110010010100001",
    "0101000111001101011100010010011001001010000111000011101",
    "010100011100110101110001001001100100101000011100001110101100010100",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010000",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101",
    "01010001110011010111000100100110",
    "010100011100110101110001001001100100101000011100",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "0101000111001101011100010010011",
    "01010001110011010111000100100110010010100001110000111010110001",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001",
    "0101000111001101011100010010011001001010000111000011101011000101000010111001001101011100010010100000000000000000000101111111",
  },
},
{
  .value = mc_dec128_from_string("-0.00700000000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01010001110011010111000100100110010010100001110000111010110001010000101110010011010111000100101000000000000000000001011111111100",
    "01010001110011010111000100100110",
    "0101000111001101011100010010011001001010000111000011101011000101",
    "010100011100110101110001001001100100101000011100001110101100010100001011100100110101110001001010",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1",
    "10",
    "101",
    "1010",
    "10101",
    "101011",
    "1010111",
    "10101110",
    "101011100",
    "1010111000",
    "10101110001",
    "101011100011",
    "1010111000111",
    "10101110001110",
    "101011100011100",
    "1010111000111001",
    "10101110001110011",
    "101011100011100110",
    "1010111000111001100",
    "10101110001110011000",
    "101011100011100110001",
    "1010111000111001100010",
    "10101110001110011000101",
    "101011100011100110001011",
    "1010111000111001100010110",
    "10101110001110011000101101",
    "101011100011100110001011011",
    "1010111000111001100010110111",
    "10101110001110011000101101111",
    "101011100011100110001011011110",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101",
    "1010111000111001100010110111101010",
    "10101110001110011000101101111010101",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101",
    "10101110001110011000101101111010101010",
    "101011100011100110001011011110101010100",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001",
    "101011100011100110001011011110101010100010",
    "1010111000111001100010110111101010101000100",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011",
    "1010111000111001100010110111101010101000100111",
    "10101110001110011000101101111010101010001001111",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001",
    "101011100011100110001011011110101010100010011111110010",
    "1010111000111001100010110111101010101000100111111100100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010",
    "1010111000111001100010110111101010101000100111111100100100",
    "10101110001110011000101101111010101010001001111111001001000",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "1010111000111001100010110111101010101000100111111100100100010000001",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10",
    "1010",
    "101011",
    "10101110",
    "1010111000",
    "101011100011",
    "10101110001110",
    "1010111000111001",
    "101011100011100110",
    "10101110001110011000",
    "1010111000111001100010",
    "101011100011100110001011",
    "10101110001110011000101101",
    "1010111000111001100010110111",
    "101011100011100110001011011110",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010",
    "101011100011100110001011011110101010",
    "10101110001110011000101101111010101010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111",
    "1010111000111001100010110111101010101000100111111100",
    "101011100011100110001011011110101010100010011111110010",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100",
    "101011100011100110001011011110101010100010011111110010010001",
    "10101110001110011000101101111010101010001001111111001001000100",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "101",
    "101011",
    "101011100",
    "101011100011",
    "101011100011100",
    "101011100011100110",
    "101011100011100110001",
    "101011100011100110001011",
    "101011100011100110001011011",
    "101011100011100110001011011110",
    "101011100011100110001011011110101",
    "101011100011100110001011011110101010",
    "101011100011100110001011011110101010100",
    "101011100011100110001011011110101010100010",
    "101011100011100110001011011110101010100010011",
    "101011100011100110001011011110101010100010011111",
    "101011100011100110001011011110101010100010011111110",
    "101011100011100110001011011110101010100010011111110010",
    "101011100011100110001011011110101010100010011111110010010",
    "101011100011100110001011011110101010100010011111110010010001",
    "101011100011100110001011011110101010100010011111110010010001000",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "101011100011100110001011011110101010100010011111110010010001000000101",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010",
    "10101110",
    "101011100011",
    "1010111000111001",
    "10101110001110011000",
    "101011100011100110001011",
    "1010111000111001100010110111",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100",
    "10101110001110011000101101111010101010001001111111001001",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110",
    "1010111000111001",
    "101011100011100110001011",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000",
    "101011100011100110001011011110101010100010011111",
    "10101110001110011000101101111010101010001001111111001001",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000",
    "10101110001110011000",
    "101011100011100110001011011110",
    "1010111000111001100010110111101010101000",
    "10101110001110011000101101111010101010001001111111",
    "101011100011100110001011011110101010100010011111110010010001",
    "1010111000111001100010110111101010101000100111111100100100010000001010",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100111111111111111101000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001",
    "1010111000111001100010",
    "101011100011100110001011011110101",
    "10101110001110011000101101111010101010001001",
    "1010111000111001100010110111101010101000100111111100100",
    "101011100011100110001011011110101010100010011111110010010001000000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111100",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001",
    "10101110001110011000101101111010",
    "101011100011100110001011011110101010100010011111",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "1010111000111001100010110111101",
    "10101110001110011000101101111010101010001001111111001001000100",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011",
    "1010111000111001100010110111101010101000100111111100100100010000001010100100000110000110000111111001111111111111111010000000",
  },
},
{
  .value = mc_dec128_from_string("32.7770000000000"),
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10101110001110011000101101111010101010001001111111001001000100000010101001000001100001100001111110011111111111111110100000000000",
    "10101110001110011000101101111010",
    "1010111000111001100010110111101010101000100111111100100100010000",
    "101011100011100110001011011110101010100010011111110010010001000000101010010000011000011000011111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128(0) },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("0.123400000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = mc_dec128_from_string("-54.3210000000000") },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1",
    "11",
    "110",
    "1101",
    "11011",
    "110111",
    "1101110",
    "11011100",
    "110111000",
    "1101110001",
    "11011100011",
    "110111000111",
    "1101110001110",
    "11011100011100",
    "110111000111000",
    "1101110001110001",
    "11011100011100011",
    "110111000111000111",
    "1101110001110001110",
    "11011100011100011101",
    "110111000111000111010",
    "1101110001110001110100",
    "11011100011100011101001",
    "110111000111000111010011",
    "1101110001110001110100111",
    "11011100011100011101001111",
    "110111000111000111010011110",
    "1101110001110001110100111100",
    "11011100011100011101001111000",
    "110111000111000111010011110000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001",
    "1101110001110001110100111100000010",
    "11011100011100011101001111000000100",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001",
    "11011100011100011101001111000000100010",
    "110111000111000111010011110000001000100",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010",
    "110111000111000111010011110000001000100101",
    "1101110001110001110100111100000010001001011",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110",
    "1101110001110001110100111100000010001001011101",
    "11011100011100011101001111000000100010010111010",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001",
    "110111000111000111010011110000001000100101110100000010",
    "1101110001110001110100111100000010001001011101000000101",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100",
    "1101110001110001110100111100000010001001011101000000101001",
    "11011100011100011101001111000000100010010111010000001010011",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "1101110001110001110100111100000010001001011101000000101001101010100",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11",
    "1101",
    "110111",
    "11011100",
    "1101110001",
    "110111000111",
    "11011100011100",
    "1101110001110001",
    "110111000111000111",
    "11011100011100011101",
    "1101110001110001110100",
    "110111000111000111010011",
    "11011100011100011101001111",
    "1101110001110001110100111100",
    "110111000111000111010011110000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010",
    "110111000111000111010011110000001000",
    "11011100011100011101001111000000100010",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000",
    "1101110001110001110100111100000010001001011101000000",
    "110111000111000111010011110000001000100101110100000010",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001",
    "110111000111000111010011110000001000100101110100000010100110",
    "11011100011100011101001111000000100010010111010000001010011010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "110",
    "110111",
    "110111000",
    "110111000111",
    "110111000111000",
    "110111000111000111",
    "110111000111000111010",
    "110111000111000111010011",
    "110111000111000111010011110",
    "110111000111000111010011110000",
    "110111000111000111010011110000001",
    "110111000111000111010011110000001000",
    "110111000111000111010011110000001000100",
    "110111000111000111010011110000001000100101",
    "110111000111000111010011110000001000100101110",
    "110111000111000111010011110000001000100101110100",
    "110111000111000111010011110000001000100101110100000",
    "110111000111000111010011110000001000100101110100000010",
    "110111000111000111010011110000001000100101110100000010100",
    "110111000111000111010011110000001000100101110100000010100110",
    "110111000111000111010011110000001000100101110100000010100110101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "110111000111000111010011110000001000100101110100000010100110101010001",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101",
    "11011100",
    "110111000111",
    "1101110001110001",
    "11011100011100011101",
    "110111000111000111010011",
    "1101110001110001110100111100",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000",
    "11011100011100011101001111000000100010010111010000001010",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100",
    "1101110001110001",
    "110111000111000111010011",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001",
    "110111000111000111010011110000001000100101110100",
    "11011100011100011101001111000000100010010111010000001010",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001",
    "11011100011100011101",
    "110111000111000111010011110000",
    "1101110001110001110100111100000010001001",
    "11011100011100011101001111000000100010010111010000",
    "110111000111000111010011110000001000100101110100000010100110",
    "1101110001110001110100111100000010001001011101000000101001101010100010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111111111111111111010000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011",
    "1101110001110001110100",
    "110111000111000111010011110000001",
    "11011100011100011101001111000000100010010111",
    "1101110001110001110100111100000010001001011101000000101",
    "110111000111000111010011110000001000100101110100000010100110101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111111",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001",
    "11011100011100011101001111000000",
    "110111000111000111010011110000001000100101110100",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "1101110001110001110100111100000",
    "11011100011100011101001111000000100010010111010000001010011010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111",
    "1101110001110001110100111100000010001001011101000000101001101010100010101011001010111111111111111111111111111111110100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_POSITIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11011100011100011101001111000000100010010111010000001010011010101000101010110010101111111111111111111111111111111101000000000000",
    "11011100011100011101001111000000",
    "1101110001110001110100111100000010001001011101000000101001101010",
    "110111000111000111010011110000001000100101110100000010100110101010001010101100101011111111111111",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
    "001000",
    "0010001",
    "00100011",
    "001000111",
    "0010001110",
    "00100011100",
    "001000111000",
    "0010001110001",
    "00100011100011",
    "001000111000111",
    "0010001110001110",
    "00100011100011100",
    "001000111000111000",
    "0010001110001110001",
    "00100011100011100010",
    "001000111000111000101",
    "0010001110001110001011",
    "00100011100011100010110",
    "001000111000111000101100",
    "0010001110001110001011000",
    "00100011100011100010110000",
    "001000111000111000101100001",
    "0010001110001110001011000011",
    "00100011100011100010110000111",
    "001000111000111000101100001111",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110",
    "0010001110001110001011000011111101",
    "00100011100011100010110000111111011",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110",
    "00100011100011100010110000111111011101",
    "001000111000111000101100001111110111011",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101",
    "001000111000111000101100001111110111011010",
    "0010001110001110001011000011111101110110100",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001",
    "0010001110001110001011000011111101110110100010",
    "00100011100011100010110000111111011101101000101",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110",
    "001000111000111000101100001111110111011010001011111101",
    "0010001110001110001011000011111101110110100010111111010",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011",
    "0010001110001110001011000011111101110110100010111111010110",
    "00100011100011100010110000111111011101101000101111110101100",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "0010001110001110001011000011111101110110100010111111010110010101011",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00",
    "0010",
    "001000",
    "00100011",
    "0010001110",
    "001000111000",
    "00100011100011",
    "0010001110001110",
    "001000111000111000",
    "00100011100011100010",
    "0010001110001110001011",
    "001000111000111000101100",
    "00100011100011100010110000",
    "0010001110001110001011000011",
    "001000111000111000101100001111",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101",
    "001000111000111000101100001111110111",
    "00100011100011100010110000111111011101",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111",
    "0010001110001110001011000011111101110110100010111111",
    "001000111000111000101100001111110111011010001011111101",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110",
    "001000111000111000101100001111110111011010001011111101011001",
    "00100011100011100010110000111111011101101000101111110101100101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "001",
    "001000",
    "001000111",
    "001000111000",
    "001000111000111",
    "001000111000111000",
    "001000111000111000101",
    "001000111000111000101100",
    "001000111000111000101100001",
    "001000111000111000101100001111",
    "001000111000111000101100001111110",
    "001000111000111000101100001111110111",
    "001000111000111000101100001111110111011",
    "001000111000111000101100001111110111011010",
    "001000111000111000101100001111110111011010001",
    "001000111000111000101100001111110111011010001011",
    "001000111000111000101100001111110111011010001011111",
    "001000111000111000101100001111110111011010001011111101",
    "001000111000111000101100001111110111011010001011111101011",
    "001000111000111000101100001111110111011010001011111101011001",
    "001000111000111000101100001111110111011010001011111101011001010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010",
    "00100011",
    "001000111000",
    "0010001110001110",
    "00100011100011100010",
    "001000111000111000101100",
    "0010001110001110001011000011",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011",
    "0010001110001110",
    "001000111000111000101100",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110",
    "00100011100011100010",
    "001000111000111000101100001111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100",
    "0010001110001110001011",
    "001000111000111000101100001111110",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010111111010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("12.3400000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
    "001000",
    "0010001",
    "00100011",
    "001000111",
    "0010001110",
    "00100011100",
    "001000111000",
    "0010001110001",
    "00100011100011",
    "001000111000111",
    "0010001110001110",
    "00100011100011100",
    "001000111000111000",
    "0010001110001110001",
    "00100011100011100010",
    "001000111000111000101",
    "0010001110001110001011",
    "00100011100011100010110",
    "001000111000111000101100",
    "0010001110001110001011000",
    "00100011100011100010110000",
    "001000111000111000101100001",
    "0010001110001110001011000011",
    "00100011100011100010110000111",
    "001000111000111000101100001111",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110",
    "0010001110001110001011000011111101",
    "00100011100011100010110000111111011",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110",
    "00100011100011100010110000111111011101",
    "001000111000111000101100001111110111011",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101",
    "001000111000111000101100001111110111011010",
    "0010001110001110001011000011111101110110100",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001",
    "0010001110001110001011000011111101110110100010",
    "00100011100011100010110000111111011101101000101",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110",
    "001000111000111000101100001111110111011010001011111101",
    "0010001110001110001011000011111101110110100010111111010",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011",
    "0010001110001110001011000011111101110110100010111111010110",
    "00100011100011100010110000111111011101101000101111110101100",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "0010001110001110001011000011111101110110100010111111010110010101011",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00",
    "0010",
    "001000",
    "00100011",
    "0010001110",
    "001000111000",
    "00100011100011",
    "0010001110001110",
    "001000111000111000",
    "00100011100011100010",
    "0010001110001110001011",
    "001000111000111000101100",
    "00100011100011100010110000",
    "0010001110001110001011000011",
    "001000111000111000101100001111",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101",
    "001000111000111000101100001111110111",
    "00100011100011100010110000111111011101",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111",
    "0010001110001110001011000011111101110110100010111111",
    "001000111000111000101100001111110111011010001011111101",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110",
    "001000111000111000101100001111110111011010001011111101011001",
    "00100011100011100010110000111111011101101000101111110101100101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "001",
    "001000",
    "001000111",
    "001000111000",
    "001000111000111",
    "001000111000111000",
    "001000111000111000101",
    "001000111000111000101100",
    "001000111000111000101100001",
    "001000111000111000101100001111",
    "001000111000111000101100001111110",
    "001000111000111000101100001111110111",
    "001000111000111000101100001111110111011",
    "001000111000111000101100001111110111011010",
    "001000111000111000101100001111110111011010001",
    "001000111000111000101100001111110111011010001011",
    "001000111000111000101100001111110111011010001011111",
    "001000111000111000101100001111110111011010001011111101",
    "001000111000111000101100001111110111011010001011111101011",
    "001000111000111000101100001111110111011010001011111101011001",
    "001000111000111000101100001111110111011010001011111101011001010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010",
    "00100011",
    "001000111000",
    "0010001110001110",
    "00100011100011100010",
    "001000111000111000101100",
    "0010001110001110001011000011",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011",
    "0010001110001110",
    "001000111000111000101100",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110",
    "00100011100011100010",
    "001000111000111000101100001111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100",
    "0010001110001110001011",
    "001000111000111000101100001111110",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010111111010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = mc_dec128_from_string("-21.0980000000000") },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
    "001000",
    "0010001",
    "00100011",
    "001000111",
    "0010001110",
    "00100011100",
    "001000111000",
    "0010001110001",
    "00100011100011",
    "001000111000111",
    "0010001110001110",
    "00100011100011100",
    "001000111000111000",
    "0010001110001110001",
    "00100011100011100010",
    "001000111000111000101",
    "0010001110001110001011",
    "00100011100011100010110",
    "001000111000111000101100",
    "0010001110001110001011000",
    "00100011100011100010110000",
    "001000111000111000101100001",
    "0010001110001110001011000011",
    "00100011100011100010110000111",
    "001000111000111000101100001111",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110",
    "0010001110001110001011000011111101",
    "00100011100011100010110000111111011",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110",
    "00100011100011100010110000111111011101",
    "001000111000111000101100001111110111011",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101",
    "001000111000111000101100001111110111011010",
    "0010001110001110001011000011111101110110100",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001",
    "0010001110001110001011000011111101110110100010",
    "00100011100011100010110000111111011101101000101",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110",
    "001000111000111000101100001111110111011010001011111101",
    "0010001110001110001011000011111101110110100010111111010",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011",
    "0010001110001110001011000011111101110110100010111111010110",
    "00100011100011100010110000111111011101101000101111110101100",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "0010001110001110001011000011111101110110100010111111010110010101011",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00",
    "0010",
    "001000",
    "00100011",
    "0010001110",
    "001000111000",
    "00100011100011",
    "0010001110001110",
    "001000111000111000",
    "00100011100011100010",
    "0010001110001110001011",
    "001000111000111000101100",
    "00100011100011100010110000",
    "0010001110001110001011000011",
    "001000111000111000101100001111",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101",
    "001000111000111000101100001111110111",
    "00100011100011100010110000111111011101",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111",
    "0010001110001110001011000011111101110110100010111111",
    "001000111000111000101100001111110111011010001011111101",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110",
    "001000111000111000101100001111110111011010001011111101011001",
    "00100011100011100010110000111111011101101000101111110101100101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "001",
    "001000",
    "001000111",
    "001000111000",
    "001000111000111",
    "001000111000111000",
    "001000111000111000101",
    "001000111000111000101100",
    "001000111000111000101100001",
    "001000111000111000101100001111",
    "001000111000111000101100001111110",
    "001000111000111000101100001111110111",
    "001000111000111000101100001111110111011",
    "001000111000111000101100001111110111011010",
    "001000111000111000101100001111110111011010001",
    "001000111000111000101100001111110111011010001011",
    "001000111000111000101100001111110111011010001011111",
    "001000111000111000101100001111110111011010001011111101",
    "001000111000111000101100001111110111011010001011111101011",
    "001000111000111000101100001111110111011010001011111101011001",
    "001000111000111000101100001111110111011010001011111101011001010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010",
    "00100011",
    "001000111000",
    "0010001110001110",
    "00100011100011100010",
    "001000111000111000101100",
    "0010001110001110001011000011",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011",
    "0010001110001110",
    "001000111000111000101100",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110",
    "00100011100011100010",
    "001000111000111000101100001111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100",
    "0010001110001110001011",
    "001000111000111000101100001111110",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010111111010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = true, .value = MC_DEC128_LARGEST_NEGATIVE },
  .max = { .set = true, .value = MC_DEC128_LARGEST_POSITIVE },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
    "001000",
    "0010001",
    "00100011",
    "001000111",
    "0010001110",
    "00100011100",
    "001000111000",
    "0010001110001",
    "00100011100011",
    "001000111000111",
    "0010001110001110",
    "00100011100011100",
    "001000111000111000",
    "0010001110001110001",
    "00100011100011100010",
    "001000111000111000101",
    "0010001110001110001011",
    "00100011100011100010110",
    "001000111000111000101100",
    "0010001110001110001011000",
    "00100011100011100010110000",
    "001000111000111000101100001",
    "0010001110001110001011000011",
    "00100011100011100010110000111",
    "001000111000111000101100001111",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110",
    "0010001110001110001011000011111101",
    "00100011100011100010110000111111011",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110",
    "00100011100011100010110000111111011101",
    "001000111000111000101100001111110111011",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101",
    "001000111000111000101100001111110111011010",
    "0010001110001110001011000011111101110110100",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001",
    "0010001110001110001011000011111101110110100010",
    "00100011100011100010110000111111011101101000101",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110",
    "001000111000111000101100001111110111011010001011111101",
    "0010001110001110001011000011111101110110100010111111010",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011",
    "0010001110001110001011000011111101110110100010111111010110",
    "00100011100011100010110000111111011101101000101111110101100",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "0010001110001110001011000011111101110110100010111111010110010101011",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00",
    "0010",
    "001000",
    "00100011",
    "0010001110",
    "001000111000",
    "00100011100011",
    "0010001110001110",
    "001000111000111000",
    "00100011100011100010",
    "0010001110001110001011",
    "001000111000111000101100",
    "00100011100011100010110000",
    "0010001110001110001011000011",
    "001000111000111000101100001111",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101",
    "001000111000111000101100001111110111",
    "00100011100011100010110000111111011101",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111",
    "0010001110001110001011000011111101110110100010111111",
    "001000111000111000101100001111110111011010001011111101",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110",
    "001000111000111000101100001111110111011010001011111101011001",
    "00100011100011100010110000111111011101101000101111110101100101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "001",
    "001000",
    "001000111",
    "001000111000",
    "001000111000111",
    "001000111000111000",
    "001000111000111000101",
    "001000111000111000101100",
    "001000111000111000101100001",
    "001000111000111000101100001111",
    "001000111000111000101100001111110",
    "001000111000111000101100001111110111",
    "001000111000111000101100001111110111011",
    "001000111000111000101100001111110111011010",
    "001000111000111000101100001111110111011010001",
    "001000111000111000101100001111110111011010001011",
    "001000111000111000101100001111110111011010001011111",
    "001000111000111000101100001111110111011010001011111101",
    "001000111000111000101100001111110111011010001011111101011",
    "001000111000111000101100001111110111011010001011111101011001",
    "001000111000111000101100001111110111011010001011111101011001010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "001000111000111000101100001111110111011010001011111101011001010101110",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010",
    "00100011",
    "001000111000",
    "0010001110001110",
    "00100011100011100010",
    "001000111000111000101100",
    "0010001110001110001011000011",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111",
    "00100011100011100010110000111111011101101000101111110101",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011",
    "0010001110001110",
    "001000111000111000101100",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110",
    "001000111000111000101100001111110111011010001011",
    "00100011100011100010110000111111011101101000101111110101",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110",
    "00100011100011100010",
    "001000111000111000101100001111",
    "0010001110001110001011000011111101110110",
    "00100011100011100010110000111111011101101000101111",
    "001000111000111000101100001111110111011010001011111101011001",
    "0010001110001110001011000011111101110110100010111111010110010101011101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000000000000000000110000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100",
    "0010001110001110001011",
    "001000111000111000101100001111110",
    "00100011100011100010110000111111011101101000",
    "0010001110001110001011000011111101110110100010111111010",
    "001000111000111000101100001111110111011010001011111101011001010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000000",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110",
    "00100011100011100010110000111111",
    "001000111000111000101100001111110111011010001011",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "0010001110001110001011000011111",
    "00100011100011100010110000111111011101101000101111110101100101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000",
    "0010001110001110001011000011111101110110100010111111010110010101011101010100110101000000000000000000000000000000001100000000",
  },
},
{
  .value = MC_DEC128_LARGEST_NEGATIVE,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00100011100011100010110000111111011101101000101111110101100101010111010101001101010000000000000000000000000000000011000000000000",
    "00100011100011100010110000111111",
    "0010001110001110001011000011111101110110100010111111010110010101",
    "001000111000111000101100001111110111011010001011111101011001010101110101010011010100000000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-edge-generation/edges_double.cstruct000066400000000000000000013063671521103432300265550ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 0.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0",
    "01",
    "010",
    "0100",
    "01000",
    "010000",
    "0100000",
    "01000000",
    "010000001",
    "0100000010",
    "01000000100",
    "010000001000",
    "0100000010000",
    "01000000100000",
    "010000001000001",
    "0100000010000011",
    "01000000100000110",
    "010000001000001101",
    "0100000010000011010",
    "01000000100000110101",
    "010000001000001101010",
    "0100000010000011010100",
    "01000000100000110101001",
    "010000001000001101010011",
    "0100000010000011010100111",
    "01000000100000110101001111",
    "010000001000001101010011111",
    "0100000010000011010100111111",
    "01000000100000110101001111110",
    "010000001000001101010011111101",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111",
    "0100000010000011010100111111011111",
    "01000000100000110101001111110111110",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001",
    "01000000100000110101001111110111110011",
    "010000001000001101010011111101111100111",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101",
    "010000001000001101010011111101111100111011",
    "0100000010000011010100111111011111001110110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011",
    "0100000010000011010100111111011111001110110110",
    "01000000100000110101001111110111110011101101100",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010",
    "010000001000001101010011111101111100111011011001000101",
    "0100000010000011010100111111011111001110110110010001011",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101",
    "0100000010000011010100111111011111001110110110010001011010",
    "01000000100000110101001111110111110011101101100100010110100",
    "010000001000001101010011111101111100111011011001000101101000",
    "0100000010000011010100111111011111001110110110010001011010000",
    "01000000100000110101001111110111110011101101100100010110100001",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01",
    "0100",
    "010000",
    "01000000",
    "0100000010",
    "010000001000",
    "01000000100000",
    "0100000010000011",
    "010000001000001101",
    "01000000100000110101",
    "0100000010000011010100",
    "010000001000001101010011",
    "01000000100000110101001111",
    "0100000010000011010100111111",
    "010000001000001101010011111101",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111",
    "010000001000001101010011111101111100",
    "01000000100000110101001111110111110011",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100",
    "0100000010000011010100111111011111001110110110010001",
    "010000001000001101010011111101111100111011011001000101",
    "01000000100000110101001111110111110011101101100100010110",
    "0100000010000011010100111111011111001110110110010001011010",
    "010000001000001101010011111101111100111011011001000101101000",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "010",
    "010000",
    "010000001",
    "010000001000",
    "010000001000001",
    "010000001000001101",
    "010000001000001101010",
    "010000001000001101010011",
    "010000001000001101010011111",
    "010000001000001101010011111101",
    "010000001000001101010011111101111",
    "010000001000001101010011111101111100",
    "010000001000001101010011111101111100111",
    "010000001000001101010011111101111100111011",
    "010000001000001101010011111101111100111011011",
    "010000001000001101010011111101111100111011011001",
    "010000001000001101010011111101111100111011011001000",
    "010000001000001101010011111101111100111011011001000101",
    "010000001000001101010011111101111100111011011001000101101",
    "010000001000001101010011111101111100111011011001000101101000",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100",
    "01000000",
    "010000001000",
    "0100000010000011",
    "01000000100000110101",
    "010000001000001101010011",
    "0100000010000011010100111111",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000",
    "0100000010000011",
    "010000001000001101010011",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100010110",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010",
    "01000000100000110101",
    "010000001000001101010011111101",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100",
    "0100000010000011010100",
    "010000001000001101010011111101111",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110010001011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100111011011001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100000110101001111110111",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0",
    "01",
    "010",
    "0100",
    "01000",
    "010000",
    "0100000",
    "01000000",
    "010000001",
    "0100000010",
    "01000000100",
    "010000001000",
    "0100000010000",
    "01000000100000",
    "010000001000001",
    "0100000010000011",
    "01000000100000110",
    "010000001000001101",
    "0100000010000011010",
    "01000000100000110101",
    "010000001000001101010",
    "0100000010000011010100",
    "01000000100000110101001",
    "010000001000001101010011",
    "0100000010000011010100111",
    "01000000100000110101001111",
    "010000001000001101010011111",
    "0100000010000011010100111111",
    "01000000100000110101001111110",
    "010000001000001101010011111101",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111",
    "0100000010000011010100111111011111",
    "01000000100000110101001111110111110",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001",
    "01000000100000110101001111110111110011",
    "010000001000001101010011111101111100111",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101",
    "010000001000001101010011111101111100111011",
    "0100000010000011010100111111011111001110110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011",
    "0100000010000011010100111111011111001110110110",
    "01000000100000110101001111110111110011101101100",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010",
    "010000001000001101010011111101111100111011011001000101",
    "0100000010000011010100111111011111001110110110010001011",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101",
    "0100000010000011010100111111011111001110110110010001011010",
    "01000000100000110101001111110111110011101101100100010110100",
    "010000001000001101010011111101111100111011011001000101101000",
    "0100000010000011010100111111011111001110110110010001011010000",
    "01000000100000110101001111110111110011101101100100010110100001",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01",
    "0100",
    "010000",
    "01000000",
    "0100000010",
    "010000001000",
    "01000000100000",
    "0100000010000011",
    "010000001000001101",
    "01000000100000110101",
    "0100000010000011010100",
    "010000001000001101010011",
    "01000000100000110101001111",
    "0100000010000011010100111111",
    "010000001000001101010011111101",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111",
    "010000001000001101010011111101111100",
    "01000000100000110101001111110111110011",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100",
    "0100000010000011010100111111011111001110110110010001",
    "010000001000001101010011111101111100111011011001000101",
    "01000000100000110101001111110111110011101101100100010110",
    "0100000010000011010100111111011111001110110110010001011010",
    "010000001000001101010011111101111100111011011001000101101000",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "010",
    "010000",
    "010000001",
    "010000001000",
    "010000001000001",
    "010000001000001101",
    "010000001000001101010",
    "010000001000001101010011",
    "010000001000001101010011111",
    "010000001000001101010011111101",
    "010000001000001101010011111101111",
    "010000001000001101010011111101111100",
    "010000001000001101010011111101111100111",
    "010000001000001101010011111101111100111011",
    "010000001000001101010011111101111100111011011",
    "010000001000001101010011111101111100111011011001",
    "010000001000001101010011111101111100111011011001000",
    "010000001000001101010011111101111100111011011001000101",
    "010000001000001101010011111101111100111011011001000101101",
    "010000001000001101010011111101111100111011011001000101101000",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100",
    "01000000",
    "010000001000",
    "0100000010000011",
    "01000000100000110101",
    "010000001000001101010011",
    "0100000010000011010100111111",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000",
    "0100000010000011",
    "010000001000001101010011",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100010110",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010",
    "01000000100000110101",
    "010000001000001101010011111101",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100",
    "0100000010000011010100",
    "010000001000001101010011111101111",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110010001011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100111011011001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100000110101001111110111",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0",
    "01",
    "010",
    "0100",
    "01000",
    "010000",
    "0100000",
    "01000000",
    "010000001",
    "0100000010",
    "01000000100",
    "010000001000",
    "0100000010000",
    "01000000100000",
    "010000001000001",
    "0100000010000011",
    "01000000100000110",
    "010000001000001101",
    "0100000010000011010",
    "01000000100000110101",
    "010000001000001101010",
    "0100000010000011010100",
    "01000000100000110101001",
    "010000001000001101010011",
    "0100000010000011010100111",
    "01000000100000110101001111",
    "010000001000001101010011111",
    "0100000010000011010100111111",
    "01000000100000110101001111110",
    "010000001000001101010011111101",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111",
    "0100000010000011010100111111011111",
    "01000000100000110101001111110111110",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001",
    "01000000100000110101001111110111110011",
    "010000001000001101010011111101111100111",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101",
    "010000001000001101010011111101111100111011",
    "0100000010000011010100111111011111001110110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011",
    "0100000010000011010100111111011111001110110110",
    "01000000100000110101001111110111110011101101100",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010",
    "010000001000001101010011111101111100111011011001000101",
    "0100000010000011010100111111011111001110110110010001011",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101",
    "0100000010000011010100111111011111001110110110010001011010",
    "01000000100000110101001111110111110011101101100100010110100",
    "010000001000001101010011111101111100111011011001000101101000",
    "0100000010000011010100111111011111001110110110010001011010000",
    "01000000100000110101001111110111110011101101100100010110100001",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01",
    "0100",
    "010000",
    "01000000",
    "0100000010",
    "010000001000",
    "01000000100000",
    "0100000010000011",
    "010000001000001101",
    "01000000100000110101",
    "0100000010000011010100",
    "010000001000001101010011",
    "01000000100000110101001111",
    "0100000010000011010100111111",
    "010000001000001101010011111101",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111",
    "010000001000001101010011111101111100",
    "01000000100000110101001111110111110011",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100",
    "0100000010000011010100111111011111001110110110010001",
    "010000001000001101010011111101111100111011011001000101",
    "01000000100000110101001111110111110011101101100100010110",
    "0100000010000011010100111111011111001110110110010001011010",
    "010000001000001101010011111101111100111011011001000101101000",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "010",
    "010000",
    "010000001",
    "010000001000",
    "010000001000001",
    "010000001000001101",
    "010000001000001101010",
    "010000001000001101010011",
    "010000001000001101010011111",
    "010000001000001101010011111101",
    "010000001000001101010011111101111",
    "010000001000001101010011111101111100",
    "010000001000001101010011111101111100111",
    "010000001000001101010011111101111100111011",
    "010000001000001101010011111101111100111011011",
    "010000001000001101010011111101111100111011011001",
    "010000001000001101010011111101111100111011011001000",
    "010000001000001101010011111101111100111011011001000101",
    "010000001000001101010011111101111100111011011001000101101",
    "010000001000001101010011111101111100111011011001000101101000",
    "010000001000001101010011111101111100111011011001000101101000011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100",
    "01000000",
    "010000001000",
    "0100000010000011",
    "01000000100000110101",
    "010000001000001101010011",
    "0100000010000011010100111111",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101",
    "010000001000001101010011111101111100111011011001",
    "0100000010000011010100111111011111001110110110010001",
    "01000000100000110101001111110111110011101101100100010110",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000",
    "0100000010000011",
    "010000001000001101010011",
    "01000000100000110101001111110111",
    "0100000010000011010100111111011111001110",
    "010000001000001101010011111101111100111011011001",
    "01000000100000110101001111110111110011101101100100010110",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010",
    "01000000100000110101",
    "010000001000001101010011111101",
    "0100000010000011010100111111011111001110",
    "01000000100000110101001111110111110011101101100100",
    "010000001000001101010011111101111100111011011001000101101000",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100",
    "0100000010000011010100",
    "010000001000001101010011111101111",
    "01000000100000110101001111110111110011101101",
    "0100000010000011010100111111011111001110110110010001011",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011",
    "01000000100000110101001111110111",
    "010000001000001101010011111101111100111011011001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "0100000010000011010100111111011",
    "01000000100000110101001111110111110011101101100100010110100001",
  },
},
{
  .value = -0.00700000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0100000010000011010100111111011111001110110110010001011010000111",
    "01000000100000110101001111110111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1",
    "11",
    "110",
    "1100",
    "11000",
    "110000",
    "1100000",
    "11000000",
    "110000000",
    "1100000001",
    "11000000010",
    "110000000100",
    "1100000001000",
    "11000000010000",
    "110000000100000",
    "1100000001000000",
    "11000000010000000",
    "110000000100000001",
    "1100000001000000011",
    "11000000010000000110",
    "110000000100000001100",
    "1100000001000000011000",
    "11000000010000000110001",
    "110000000100000001100011",
    "1100000001000000011000110",
    "11000000010000000110001101",
    "110000000100000001100011011",
    "1100000001000000011000110111",
    "11000000010000000110001101110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001",
    "1100000001000000011000110111010010",
    "11000000010000000110001101110100101",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111",
    "11000000010000000110001101110100101111",
    "110000000100000001100011011101001011110",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000",
    "110000000100000001100011011101001011110001",
    "1100000001000000011000110111010010111100011",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101",
    "1100000001000000011000110111010010111100011010",
    "11000000010000000110001101110100101111000110101",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111",
    "110000000100000001100011011101001011110001101010011111",
    "1100000001000000011000110111010010111100011010100111111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101",
    "1100000001000000011000110111010010111100011010100111111011",
    "11000000010000000110001101110100101111000110101001111110111",
    "110000000100000001100011011101001011110001101010011111101111",
    "1100000001000000011000110111010010111100011010100111111011111",
    "11000000010000000110001101110100101111000110101001111110111110",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11",
    "1100",
    "110000",
    "11000000",
    "1100000001",
    "110000000100",
    "11000000010000",
    "1100000001000000",
    "110000000100000001",
    "11000000010000000110",
    "1100000001000000011000",
    "110000000100000001100011",
    "11000000010000000110001101",
    "1100000001000000011000110111",
    "110000000100000001100011011101",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010",
    "110000000100000001100011011101001011",
    "11000000010000000110001101110100101111",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001",
    "1100000001000000011000110111010010111100011010100111",
    "110000000100000001100011011101001011110001101010011111",
    "11000000010000000110001101110100101111000110101001111110",
    "1100000001000000011000110111010010111100011010100111111011",
    "110000000100000001100011011101001011110001101010011111101111",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "110",
    "110000",
    "110000000",
    "110000000100",
    "110000000100000",
    "110000000100000001",
    "110000000100000001100",
    "110000000100000001100011",
    "110000000100000001100011011",
    "110000000100000001100011011101",
    "110000000100000001100011011101001",
    "110000000100000001100011011101001011",
    "110000000100000001100011011101001011110",
    "110000000100000001100011011101001011110001",
    "110000000100000001100011011101001011110001101",
    "110000000100000001100011011101001011110001101010",
    "110000000100000001100011011101001011110001101010011",
    "110000000100000001100011011101001011110001101010011111",
    "110000000100000001100011011101001011110001101010011111101",
    "110000000100000001100011011101001011110001101010011111101111",
    "110000000100000001100011011101001011110001101010011111101111101",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100",
    "11000000",
    "110000000100",
    "1100000001000000",
    "11000000010000000110",
    "110000000100000001100011",
    "1100000001000000011000110111",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110",
    "110000000100000001100011011101001011110001101010",
    "1100000001000000011000110111010010111100011010100111",
    "11000000010000000110001101110100101111000110101001111110",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000",
    "1100000001000000",
    "110000000100000001100011",
    "11000000010000000110001101110100",
    "1100000001000000011000110111010010111100",
    "110000000100000001100011011101001011110001101010",
    "11000000010000000110001101110100101111000110101001111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001",
    "11000000010000000110",
    "110000000100000001100011011101",
    "1100000001000000011000110111010010111100",
    "11000000010000000110001101110100101111000110101001",
    "110000000100000001100011011101001011110001101010011111101111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010",
    "1100000001000000011000",
    "110000000100000001100011011101001",
    "11000000010000000110001101110100101111000110",
    "1100000001000000011000110111010010111100011010100111111",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000",
    "11000000010000000110001101110100",
    "110000000100000001100011011101001011110001101010",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "1100000001000000011000110111010",
    "11000000010000000110001101110100101111000110101001111110111110",
  },
},
{
  .value = 32.77700000000000102,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1100000001000000011000110111010010111100011010100111111011111010",
    "11000000010000000110001101110100",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = 0.12340000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111110",
    "1111111111101",
    "11111111111011",
    "111111111110111",
    "1111111111101111",
    "11111111111011111",
    "111111111110111111",
    "1111111111101111111",
    "11111111111011111111",
    "111111111110111111111",
    "1111111111101111111111",
    "11111111111011111111111",
    "111111111110111111111111",
    "1111111111101111111111111",
    "11111111111011111111111111",
    "111111111110111111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111",
    "1111111111101111111111111111111111",
    "11111111111011111111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111",
    "11111111111011111111111111111111111111",
    "111111111110111111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "1111111111101111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111110",
    "11111111111011",
    "1111111111101111",
    "111111111110111111",
    "11111111111011111111",
    "1111111111101111111111",
    "111111111110111111111111",
    "11111111111011111111111111",
    "1111111111101111111111111111",
    "111111111110111111111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111",
    "111111111110111111111111111111111111",
    "11111111111011111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111110",
    "111111111110111",
    "111111111110111111",
    "111111111110111111111",
    "111111111110111111111111",
    "111111111110111111111111111",
    "111111111110111111111111111111",
    "111111111110111111111111111111111",
    "111111111110111111111111111111111111",
    "111111111110111111111111111111111111111",
    "111111111110111111111111111111111111111111",
    "111111111110111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111110",
    "1111111111101111",
    "11111111111011111111",
    "111111111110111111111111",
    "1111111111101111111111111111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111101111",
    "111111111110111111111111",
    "11111111111011111111111111111111",
    "1111111111101111111111111111111111111111",
    "111111111110111111111111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111011111111",
    "111111111110111111111111111111",
    "1111111111101111111111111111111111111111",
    "11111111111011111111111111111111111111111111111111",
    "111111111110111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111101111111111",
    "111111111110111111111111111111111",
    "11111111111011111111111111111111111111111111",
    "1111111111101111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111",
    "11111111111011111111111111111111",
    "111111111110111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "1111111111101111111111111111111",
    "11111111111011111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111101111111111111111111111111111111111111111111111111111",
    "11111111111011111111111111111111",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 12.33999999999999986 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = 0.00000000000000000 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = -54.32099999999999795 },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = true, .value = DBL_MIN },
  .max = { .set = true, .value = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00000000000000000 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000001",
    "1000000000010",
    "10000000000100",
    "100000000001000",
    "1000000000010000",
    "10000000000100000",
    "100000000001000000",
    "1000000000010000000",
    "10000000000100000000",
    "100000000001000000000",
    "1000000000010000000000",
    "10000000000100000000000",
    "100000000001000000000000",
    "1000000000010000000000000",
    "10000000000100000000000000",
    "100000000001000000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000",
    "1000000000010000000000000000000000",
    "10000000000100000000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000",
    "10000000000100000000000000000000000000",
    "100000000001000000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "1000000000010000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000001",
    "10000000000100",
    "1000000000010000",
    "100000000001000000",
    "10000000000100000000",
    "1000000000010000000000",
    "100000000001000000000000",
    "10000000000100000000000000",
    "1000000000010000000000000000",
    "100000000001000000000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000",
    "100000000001000000000000000000000000",
    "10000000000100000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000001",
    "100000000001000",
    "100000000001000000",
    "100000000001000000000",
    "100000000001000000000000",
    "100000000001000000000000000",
    "100000000001000000000000000000",
    "100000000001000000000000000000000",
    "100000000001000000000000000000000000",
    "100000000001000000000000000000000000000",
    "100000000001000000000000000000000000000000",
    "100000000001000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000001",
    "1000000000010000",
    "10000000000100000000",
    "100000000001000000000000",
    "1000000000010000000000000000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000",
    "1000000000010000",
    "100000000001000000000000",
    "10000000000100000000000000000000",
    "1000000000010000000000000000000000000000",
    "100000000001000000000000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000",
    "10000000000100000000",
    "100000000001000000000000000000",
    "1000000000010000000000000000000000000000",
    "10000000000100000000000000000000000000000000000000",
    "100000000001000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000",
    "1000000000010000000000",
    "100000000001000000000000000000000",
    "10000000000100000000000000000000000000000000",
    "1000000000010000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000",
    "10000000000100000000000000000000",
    "100000000001000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "1000000000010000000000000000000",
    "10000000000100000000000000000000000000000000000000000000000000",
  },
},
{
  .value = DBL_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000010000000000000000000000000000000000000000000000000000",
    "10000000000100000000000000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-edge-generation/edges_int32.cstruct000066400000000000000000006734441521103432300262440ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111",
    "0",
    "01",
    "011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111",
    "01",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111",
    "011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000",
    "1",
    "10",
    "100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000",
    "10",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000",
    "100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000111",
    "0",
    "00",
    "000",
    "0001",
    "00011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000111",
    "00",
    "0001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000111",
    "000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000111",
    "0001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "001000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "001000",
    "00",
    "0010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "001000",
    "001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "001000",
    "0010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000001",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000111",
    "000",
    "000000",
    "000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000111",
    "0000",
    "00000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000111",
    "00000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000111",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000001000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000001",
    "000000010",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000001000",
    "00",
    "0000",
    "000000",
    "00000001",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000001000",
    "000",
    "000000",
    "000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000001000",
    "0000",
    "00000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000001000",
    "00000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000001000",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000001",
    "000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
    "0000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000001",
    "000000000000000000000000000010",
    "0000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "0000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
    "0000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000001",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
    "1000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000111",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "100000",
    "10",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "100000",
    "100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "100000",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "100001",
    "1",
    "10",
    "100",
    "1000",
    "10000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "100001",
    "10",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "100001",
    "100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "100001",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000100000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000001",
    "0000010",
    "00000100",
    "000001000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000100000",
    "00",
    "0000",
    "000001",
    "00000100",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000100000",
    "000",
    "000001",
    "000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000100000",
    "0000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000100000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000100000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000100001",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000001",
    "0000010",
    "00000100",
    "000001000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000100001",
    "00",
    "0000",
    "000001",
    "00000100",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000100001",
    "000",
    "000001",
    "000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000100001",
    "0000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000100001",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000100001",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000001",
    "000000000000000000000000010",
    "0000000000000000000000000100",
    "00000000000000000000000001000",
    "000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000001",
    "0000000000000000000000000100",
    "000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000010",
    "000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
    "0000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000001",
    "0000000000000000000000000010",
    "00000000000000000000000000100",
    "000000000000000000000000001000",
    "0000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000010",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000001",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "0000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
    "0000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000001",
    "1000000000000000000000000010",
    "10000000000000000000000000100",
    "100000000000000000000000001000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000010",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000001",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
    "1000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000100000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111",
    "011",
    "011111",
    "011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111",
    "0111",
    "01111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111",
    "01111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000",
    "100",
    "100000",
    "100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000",
    "1000",
    "10000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000",
    "10000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000001",
    "100000000000000000000011",
    "1000000000000000000000111",
    "10000000000000000000001111",
    "100000000000000000000011111",
    "1000000000000000000000111111",
    "10000000000000000000001111111",
    "100000000000000000000011111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000011",
    "10000000000000000000001111",
    "1000000000000000000000111111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000011",
    "100000000000000000000011111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000011",
    "1000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000001",
    "00000000000000000000011",
    "000000000000000000000111",
    "0000000000000000000001111",
    "00000000000000000000011111",
    "000000000000000000000111111",
    "0000000000000000000001111111",
    "00000000000000000000011111111",
    "000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000001",
    "000000000000000000000111",
    "00000000000000000000011111",
    "0000000000000000000001111111",
    "000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000111",
    "000000000000000000000111111",
    "000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000111",
    "0000000000000000000001111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "00000000",
    "0000000000000000",
    "000000000000000000000111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "00000000000",
    "0000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
    "0000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000001",
    "00000000000000000000010",
    "000000000000000000000100",
    "0000000000000000000001000",
    "00000000000000000000010000",
    "000000000000000000000100000",
    "0000000000000000000001000000",
    "00000000000000000000010000000",
    "000000000000000000000100000000",
    "0000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000001",
    "000000000000000000000100",
    "00000000000000000000010000",
    "0000000000000000000001000000",
    "000000000000000000000100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000100",
    "000000000000000000000100000",
    "000000000000000000000100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000100",
    "0000000000000000000001000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000100",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "00000000000",
    "0000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "0000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
    "0000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000010000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000001",
    "100000000000000000000011",
    "1000000000000000000000111",
    "10000000000000000000001111",
    "100000000000000000000011111",
    "1000000000000000000000111111",
    "10000000000000000000001111111",
    "100000000000000000000011111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000011",
    "10000000000000000000001111",
    "1000000000000000000000111111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000011",
    "100000000000000000000011111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000011",
    "1000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000001",
    "100000000000000000000011",
    "1000000000000000000000111",
    "10000000000000000000001111",
    "100000000000000000000011111",
    "1000000000000000000000111111",
    "10000000000000000000001111111",
    "100000000000000000000011111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000011",
    "10000000000000000000001111",
    "1000000000000000000000111111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000011",
    "100000000000000000000011111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000011",
    "1000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000001",
    "100000000000000000000011",
    "1000000000000000000000111",
    "10000000000000000000001111",
    "100000000000000000000011111",
    "1000000000000000000000111111",
    "10000000000000000000001111111",
    "100000000000000000000011111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000011",
    "10000000000000000000001111",
    "1000000000000000000000111111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000011",
    "100000000000000000000011111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000011",
    "1000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000001",
    "100000000000000000000011",
    "1000000000000000000000111",
    "10000000000000000000001111",
    "100000000000000000000011111",
    "1000000000000000000000111111",
    "10000000000000000000001111111",
    "100000000000000000000011111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000011",
    "10000000000000000000001111",
    "1000000000000000000000111111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000011",
    "100000000000000000000011111",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000011",
    "1000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
    "1000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000001111111111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "0",
    "01",
    "011",
    "0110",
    "01101",
    "011011",
    "0110110",
    "01101101",
    "011011011",
    "0110110111",
    "01101101111",
    "011011011110",
    "0110110111100",
    "01101101111001",
    "011011011110010",
    "0110110111100100",
    "01101101111001001",
    "011011011110010010",
    "0110110111100100101",
    "01101101111001001010",
    "011011011110010010100",
    "0110110111100100101001",
    "01101101111001001010011",
    "011011011110010010100110",
    "0110110111100100101001100",
    "01101101111001001010011001",
    "011011011110010010100110011",
    "0110110111100100101001100111",
    "01101101111001001010011001110",
    "011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "01",
    "0110",
    "011011",
    "01101101",
    "0110110111",
    "011011011110",
    "01101101111001",
    "0110110111100100",
    "011011011110010010",
    "01101101111001001010",
    "0110110111100100101001",
    "011011011110010010100110",
    "01101101111001001010011001",
    "0110110111100100101001100111",
    "011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "011",
    "011011",
    "011011011",
    "011011011110",
    "011011011110010",
    "011011011110010010",
    "011011011110010010100",
    "011011011110010010100110",
    "011011011110010010100110011",
    "011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "0110",
    "01101101",
    "011011011110",
    "0110110111100100",
    "01101101111001001010",
    "011011011110010010100110",
    "0110110111100100101001100111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "01101101",
    "0110110111100100",
    "011011011110010010100110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "0110110111",
    "01101101111001001010",
    "011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "01101101111",
    "0110110111100100101001",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
    "0110110111100100",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "0",
    "00",
    "001",
    "0011",
    "00110",
    "001101",
    "0011011",
    "00110110",
    "001101101",
    "0011011011",
    "00110110111",
    "001101101111",
    "0011011011110",
    "00110110111100",
    "001101101111001",
    "0011011011110010",
    "00110110111100100",
    "001101101111001001",
    "0011011011110010010",
    "00110110111100100101",
    "001101101111001001010",
    "0011011011110010010100",
    "00110110111100100101001",
    "001101101111001001010011",
    "0011011011110010010100110",
    "00110110111100100101001100",
    "001101101111001001010011001",
    "0011011011110010010100110011",
    "00110110111100100101001100111",
    "001101101111001001010011001111",
    "0011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "00",
    "0011",
    "001101",
    "00110110",
    "0011011011",
    "001101101111",
    "00110110111100",
    "0011011011110010",
    "001101101111001001",
    "00110110111100100101",
    "0011011011110010010100",
    "001101101111001001010011",
    "00110110111100100101001100",
    "0011011011110010010100110011",
    "001101101111001001010011001111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "001",
    "001101",
    "001101101",
    "001101101111",
    "001101101111001",
    "001101101111001001",
    "001101101111001001010",
    "001101101111001001010011",
    "001101101111001001010011001",
    "001101101111001001010011001111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "0011",
    "00110110",
    "001101101111",
    "0011011011110010",
    "00110110111100100101",
    "001101101111001001010011",
    "0011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "00110110",
    "0011011011110010",
    "001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "0011011011",
    "00110110111100100101",
    "001101101111001001010011001111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "00110110111",
    "0011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "0011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
    "0011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00110110111100100101001100111100",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1",
    "10",
    "101",
    "1011",
    "10110",
    "101101",
    "1011011",
    "10110110",
    "101101101",
    "1011011011",
    "10110110111",
    "101101101111",
    "1011011011110",
    "10110110111100",
    "101101101111001",
    "1011011011110010",
    "10110110111100100",
    "101101101111001001",
    "1011011011110010010",
    "10110110111100100101",
    "101101101111001001010",
    "1011011011110010010100",
    "10110110111100100101001",
    "101101101111001001010011",
    "1011011011110010010100110",
    "10110110111100100101001100",
    "101101101111001001010011001",
    "1011011011110010010100110011",
    "10110110111100100101001100111",
    "101101101111001001010011001110",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10",
    "1011",
    "101101",
    "10110110",
    "1011011011",
    "101101101111",
    "10110110111100",
    "1011011011110010",
    "101101101111001001",
    "10110110111100100101",
    "1011011011110010010100",
    "101101101111001001010011",
    "10110110111100100101001100",
    "1011011011110010010100110011",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "101",
    "101101",
    "101101101",
    "101101101111",
    "101101101111001",
    "101101101111001001",
    "101101101111001001010",
    "101101101111001001010011",
    "101101101111001001010011001",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011",
    "10110110",
    "101101101111",
    "1011011011110010",
    "10110110111100100101",
    "101101101111001001010011",
    "1011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110",
    "1011011011110010",
    "101101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011",
    "10110110111100100101",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110111",
    "1011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1",
    "10",
    "101",
    "1011",
    "10110",
    "101101",
    "1011011",
    "10110110",
    "101101101",
    "1011011011",
    "10110110111",
    "101101101111",
    "1011011011110",
    "10110110111100",
    "101101101111001",
    "1011011011110010",
    "10110110111100100",
    "101101101111001001",
    "1011011011110010010",
    "10110110111100100101",
    "101101101111001001010",
    "1011011011110010010100",
    "10110110111100100101001",
    "101101101111001001010011",
    "1011011011110010010100110",
    "10110110111100100101001100",
    "101101101111001001010011001",
    "1011011011110010010100110011",
    "10110110111100100101001100111",
    "101101101111001001010011001110",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10",
    "1011",
    "101101",
    "10110110",
    "1011011011",
    "101101101111",
    "10110110111100",
    "1011011011110010",
    "101101101111001001",
    "10110110111100100101",
    "1011011011110010010100",
    "101101101111001001010011",
    "10110110111100100101001100",
    "1011011011110010010100110011",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "101",
    "101101",
    "101101101",
    "101101101111",
    "101101101111001",
    "101101101111001001",
    "101101101111001001010",
    "101101101111001001010011",
    "101101101111001001010011001",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011",
    "10110110",
    "101101101111",
    "1011011011110010",
    "10110110111100100101",
    "101101101111001001010011",
    "1011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110",
    "1011011011110010",
    "101101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011",
    "10110110111100100101",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110111",
    "1011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1",
    "10",
    "101",
    "1011",
    "10110",
    "101101",
    "1011011",
    "10110110",
    "101101101",
    "1011011011",
    "10110110111",
    "101101101111",
    "1011011011110",
    "10110110111100",
    "101101101111001",
    "1011011011110010",
    "10110110111100100",
    "101101101111001001",
    "1011011011110010010",
    "10110110111100100101",
    "101101101111001001010",
    "1011011011110010010100",
    "10110110111100100101001",
    "101101101111001001010011",
    "1011011011110010010100110",
    "10110110111100100101001100",
    "101101101111001001010011001",
    "1011011011110010010100110011",
    "10110110111100100101001100111",
    "101101101111001001010011001110",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10",
    "1011",
    "101101",
    "10110110",
    "1011011011",
    "101101101111",
    "10110110111100",
    "1011011011110010",
    "101101101111001001",
    "10110110111100100101",
    "1011011011110010010100",
    "101101101111001001010011",
    "10110110111100100101001100",
    "1011011011110010010100110011",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "101",
    "101101",
    "101101101",
    "101101101111",
    "101101101111001",
    "101101101111001001",
    "101101101111001001010",
    "101101101111001001010011",
    "101101101111001001010011001",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011",
    "10110110",
    "101101101111",
    "1011011011110010",
    "10110110111100100101",
    "101101101111001001010011",
    "1011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110",
    "1011011011110010",
    "101101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011",
    "10110110111100100101",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110111",
    "1011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1",
    "10",
    "101",
    "1011",
    "10110",
    "101101",
    "1011011",
    "10110110",
    "101101101",
    "1011011011",
    "10110110111",
    "101101101111",
    "1011011011110",
    "10110110111100",
    "101101101111001",
    "1011011011110010",
    "10110110111100100",
    "101101101111001001",
    "1011011011110010010",
    "10110110111100100101",
    "101101101111001001010",
    "1011011011110010010100",
    "10110110111100100101001",
    "101101101111001001010011",
    "1011011011110010010100110",
    "10110110111100100101001100",
    "101101101111001001010011001",
    "1011011011110010010100110011",
    "10110110111100100101001100111",
    "101101101111001001010011001110",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10",
    "1011",
    "101101",
    "10110110",
    "1011011011",
    "101101101111",
    "10110110111100",
    "1011011011110010",
    "101101101111001001",
    "10110110111100100101",
    "1011011011110010010100",
    "101101101111001001010011",
    "10110110111100100101001100",
    "1011011011110010010100110011",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "101",
    "101101",
    "101101101",
    "101101101111",
    "101101101111001",
    "101101101111001001",
    "101101101111001001010",
    "101101101111001001010011",
    "101101101111001001010011001",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011",
    "10110110",
    "101101101111",
    "1011011011110010",
    "10110110111100100101",
    "101101101111001001010011",
    "1011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110",
    "1011011011110010",
    "101101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011",
    "10110110111100100101",
    "101101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "10110110111",
    "1011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
    "1011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10110110111100100101001100111011",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000001",
    "00000010",
    "000000100",
    "0000001000",
    "00000010000",
    "000000100000",
    "0000001000000",
    "00000010000000",
    "000000100000000",
    "0000001000000000",
    "00000010000000000",
    "000000100000000000",
    "0000001000000000000",
    "00000010000000000000",
    "000000100000000000000",
    "0000001000000000000000",
    "00000010000000000000000",
    "000000100000000000000000",
    "0000001000000000000000000",
    "00000010000000000000000000",
    "000000100000000000000000000",
    "0000001000000000000000000000",
    "00000010000000000000000000000",
    "000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000010",
    "0000001000",
    "000000100000",
    "00000010000000",
    "0000001000000000",
    "000000100000000000",
    "00000010000000000000",
    "0000001000000000000000",
    "000000100000000000000000",
    "00000010000000000000000000",
    "0000001000000000000000000000",
    "000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "000",
    "000000",
    "000000100",
    "000000100000",
    "000000100000000",
    "000000100000000000",
    "000000100000000000000",
    "000000100000000000000000",
    "000000100000000000000000000",
    "000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "0000",
    "00000010",
    "000000100000",
    "0000001000000000",
    "00000010000000000000",
    "000000100000000000000000",
    "0000001000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "00000010",
    "0000001000000000",
    "000000100000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "0000001000",
    "00000010000000000000",
    "000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "00000010000",
    "0000001000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
    "0000001000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000001000000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000001",
    "000000010",
    "0000000100",
    "00000001000",
    "000000010000",
    "0000000100000",
    "00000001000000",
    "000000010000000",
    "0000000100000000",
    "00000001000000000",
    "000000010000000000",
    "0000000100000000000",
    "00000001000000000000",
    "000000010000000000000",
    "0000000100000000000000",
    "00000001000000000000000",
    "000000010000000000000000",
    "0000000100000000000000000",
    "00000001000000000000000000",
    "000000010000000000000000000",
    "0000000100000000000000000000",
    "00000001000000000000000000000",
    "000000010000000000000000000000",
    "0000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "00",
    "0000",
    "000000",
    "00000001",
    "0000000100",
    "000000010000",
    "00000001000000",
    "0000000100000000",
    "000000010000000000",
    "00000001000000000000",
    "0000000100000000000000",
    "000000010000000000000000",
    "00000001000000000000000000",
    "0000000100000000000000000000",
    "000000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "000",
    "000000",
    "000000010",
    "000000010000",
    "000000010000000",
    "000000010000000000",
    "000000010000000000000",
    "000000010000000000000000",
    "000000010000000000000000000",
    "000000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "0000",
    "00000001",
    "000000010000",
    "0000000100000000",
    "00000001000000000000",
    "000000010000000000000000",
    "0000000100000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "00000001",
    "0000000100000000",
    "000000010000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "0000000100",
    "00000001000000000000",
    "000000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "00000001000",
    "0000000100000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "0000000100000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
    "0000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000001000000000000000000000001",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000001",
    "100000010",
    "1000000100",
    "10000001000",
    "100000010000",
    "1000000100000",
    "10000001000000",
    "100000010000000",
    "1000000100000000",
    "10000001000000000",
    "100000010000000000",
    "1000000100000000000",
    "10000001000000000000",
    "100000010000000000000",
    "1000000100000000000000",
    "10000001000000000000000",
    "100000010000000000000000",
    "1000000100000000000000000",
    "10000001000000000000000000",
    "100000010000000000000000000",
    "1000000100000000000000000000",
    "10000001000000000000000000000",
    "100000010000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000001",
    "1000000100",
    "100000010000",
    "10000001000000",
    "1000000100000000",
    "100000010000000000",
    "10000001000000000000",
    "1000000100000000000000",
    "100000010000000000000000",
    "10000001000000000000000000",
    "1000000100000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "100",
    "100000",
    "100000010",
    "100000010000",
    "100000010000000",
    "100000010000000000",
    "100000010000000000000",
    "100000010000000000000000",
    "100000010000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000",
    "10000001",
    "100000010000",
    "1000000100000000",
    "10000001000000000000",
    "100000010000000000000000",
    "1000000100000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001",
    "1000000100000000",
    "100000010000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100",
    "10000001000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001000",
    "1000000100000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000001",
    "100000010",
    "1000000100",
    "10000001000",
    "100000010000",
    "1000000100000",
    "10000001000000",
    "100000010000000",
    "1000000100000000",
    "10000001000000000",
    "100000010000000000",
    "1000000100000000000",
    "10000001000000000000",
    "100000010000000000000",
    "1000000100000000000000",
    "10000001000000000000000",
    "100000010000000000000000",
    "1000000100000000000000000",
    "10000001000000000000000000",
    "100000010000000000000000000",
    "1000000100000000000000000000",
    "10000001000000000000000000000",
    "100000010000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000001",
    "1000000100",
    "100000010000",
    "10000001000000",
    "1000000100000000",
    "100000010000000000",
    "10000001000000000000",
    "1000000100000000000000",
    "100000010000000000000000",
    "10000001000000000000000000",
    "1000000100000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "100",
    "100000",
    "100000010",
    "100000010000",
    "100000010000000",
    "100000010000000000",
    "100000010000000000000",
    "100000010000000000000000",
    "100000010000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000",
    "10000001",
    "100000010000",
    "1000000100000000",
    "10000001000000000000",
    "100000010000000000000000",
    "1000000100000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001",
    "1000000100000000",
    "100000010000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100",
    "10000001000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001000",
    "1000000100000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000001",
    "100000010",
    "1000000100",
    "10000001000",
    "100000010000",
    "1000000100000",
    "10000001000000",
    "100000010000000",
    "1000000100000000",
    "10000001000000000",
    "100000010000000000",
    "1000000100000000000",
    "10000001000000000000",
    "100000010000000000000",
    "1000000100000000000000",
    "10000001000000000000000",
    "100000010000000000000000",
    "1000000100000000000000000",
    "10000001000000000000000000",
    "100000010000000000000000000",
    "1000000100000000000000000000",
    "10000001000000000000000000000",
    "100000010000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000001",
    "1000000100",
    "100000010000",
    "10000001000000",
    "1000000100000000",
    "100000010000000000",
    "10000001000000000000",
    "1000000100000000000000",
    "100000010000000000000000",
    "10000001000000000000000000",
    "1000000100000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "100",
    "100000",
    "100000010",
    "100000010000",
    "100000010000000",
    "100000010000000000",
    "100000010000000000000",
    "100000010000000000000000",
    "100000010000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000",
    "10000001",
    "100000010000",
    "1000000100000000",
    "10000001000000000000",
    "100000010000000000000000",
    "1000000100000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001",
    "1000000100000000",
    "100000010000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100",
    "10000001000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001000",
    "1000000100000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000001",
    "100000010",
    "1000000100",
    "10000001000",
    "100000010000",
    "1000000100000",
    "10000001000000",
    "100000010000000",
    "1000000100000000",
    "10000001000000000",
    "100000010000000000",
    "1000000100000000000",
    "10000001000000000000",
    "100000010000000000000",
    "1000000100000000000000",
    "10000001000000000000000",
    "100000010000000000000000",
    "1000000100000000000000000",
    "10000001000000000000000000",
    "100000010000000000000000000",
    "1000000100000000000000000000",
    "10000001000000000000000000000",
    "100000010000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000001",
    "1000000100",
    "100000010000",
    "10000001000000",
    "1000000100000000",
    "100000010000000000",
    "10000001000000000000",
    "1000000100000000000000",
    "100000010000000000000000",
    "10000001000000000000000000",
    "1000000100000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "100",
    "100000",
    "100000010",
    "100000010000",
    "100000010000000",
    "100000010000000000",
    "100000010000000000000",
    "100000010000000000000000",
    "100000010000000000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000",
    "10000001",
    "100000010000",
    "1000000100000000",
    "10000001000000000000",
    "100000010000000000000000",
    "1000000100000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001",
    "1000000100000000",
    "100000010000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100",
    "10000001000000000000",
    "100000010000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "10000001000",
    "1000000100000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
    "1000000100000000000000000000000",
  },
},
{
  .value = 16777216,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000001000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000",
    "0",
    "00",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000",
    "00",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000",
    "00",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000",
    "000",
    "000000",
    "000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000",
    "0000",
    "00000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000",
    "00000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
    "1111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "10000000000",
    "1000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "1000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000000000000000000000000",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111",
  },
},
{
  .value = 2147483647,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "11111111111111111111111111111111",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = true, .value = INT32_MIN },
  .max = { .set = true, .value = 2147483647 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = INT32_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-edge-generation/edges_int64.cstruct000066400000000000000000013760321521103432300262430ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111",
    "0",
    "01",
    "011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111",
    "01",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111",
    "011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000",
    "1",
    "10",
    "100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000",
    "10",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000",
    "100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000111",
    "0",
    "00",
    "000",
    "0001",
    "00011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000111",
    "00",
    "0001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000111",
    "000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000111",
    "0001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "001000",
    "0",
    "00",
    "001",
    "0010",
    "00100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "001000",
    "00",
    "0010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "001000",
    "001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "001000",
    "0010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000001",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000111",
    "000",
    "000000",
    "000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000111",
    "0000",
    "00000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000111",
    "00000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000111",
    "0000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000001000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000001",
    "000000010",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000001000",
    "00",
    "0000",
    "000000",
    "00000001",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000001000",
    "000",
    "000000",
    "000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000001000",
    "0000",
    "00000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000001000",
    "00000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000001000",
    "0000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000001000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000001",
    "0000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "0000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
    "0000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000000111",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000001",
    "0000000000000000000000000000010",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000001",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "0000000000000000",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "0000000000000000000000000000010",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000000000000000000000000001000",
    "00000000000000000000000000000100",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 7,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000000111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "100000",
    "10",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "100000",
    "100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "100000",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "100001",
    "1",
    "10",
    "100",
    "1000",
    "10000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "100001",
    "10",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "100001",
    "100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "100001",
    "1000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000100000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000001",
    "0000010",
    "00000100",
    "000001000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000100000",
    "00",
    "0000",
    "000001",
    "00000100",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000100000",
    "000",
    "000001",
    "000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000100000",
    "0000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000100000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000100000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000100001",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000001",
    "0000010",
    "00000100",
    "000001000",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000100001",
    "00",
    "0000",
    "000001",
    "00000100",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000100001",
    "000",
    "000001",
    "000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000100001",
    "0000",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000100001",
    "00000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000100001",
    "0000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000100001",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000001",
    "0000000000000000000000000010",
    "00000000000000000000000000100",
    "000000000000000000000000001000",
    "0000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000010",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000001",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "0000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
    "0000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000000000100000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000001",
    "00000000000000000000000000010",
    "000000000000000000000000000100",
    "0000000000000000000000000001000",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000001",
    "000000000000000000000000000100",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000001",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000100",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "0000000000000000",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "0000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000000000000000000000000100001",
    "00000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001",
    "100000000000000000000000000000000000000000000000000000000010",
    "1000000000000000000000000000000000000000000000000000000000100",
    "10000000000000000000000000000000000000000000000000000000001000",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
    "100000000000000000000000000000000000000000000000000000000010000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000000010",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000000001000",
  },
},
{
  .value = 32,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000000000100000",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "01111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "01111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "01111111111",
    "011",
    "011111",
    "011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "01111111111",
    "0111",
    "01111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "01111111111",
    "01111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "01111111111",
    "0111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "01111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "10000000000",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "10000000000",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "10000000000",
    "100",
    "100000",
    "100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "10000000000",
    "1000",
    "10000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "10000000000",
    "10000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "10000000000",
    "1000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "10000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000000000000000000000000001111",
    "10000000000000000000000000000000000000000000000000000011111",
    "100000000000000000000000000000000000000000000000000000111111",
    "1000000000000000000000000000000000000000000000000000001111111",
    "10000000000000000000000000000000000000000000000000000011111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "1000000000000000000000000000000000000000000000000000001111",
    "100000000000000000000000000000000000000000000000000000111111",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111",
    "100000000000000000000000000000000000000000000000000000111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000001",
    "000000000000000000000011",
    "0000000000000000000000111",
    "00000000000000000000001111",
    "000000000000000000000011111",
    "0000000000000000000000111111",
    "00000000000000000000001111111",
    "000000000000000000000011111111",
    "0000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000011",
    "00000000000000000000001111",
    "0000000000000000000000111111",
    "000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000011",
    "000000000000000000000011111",
    "000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000011",
    "0000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "00000000",
    "0000000000000000",
    "000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "0000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
    "0000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000000000000001111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000001",
    "000000000000000000000010",
    "0000000000000000000000100",
    "00000000000000000000001000",
    "000000000000000000000010000",
    "0000000000000000000000100000",
    "00000000000000000000001000000",
    "000000000000000000000010000000",
    "0000000000000000000000100000000",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000010",
    "00000000000000000000001000",
    "0000000000000000000000100000",
    "000000000000000000000010000000",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000010",
    "000000000000000000000010000",
    "000000000000000000000010000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000010",
    "0000000000000000000000100000",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000010",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000010000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "0000000000000000",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "0000000000000000000000100000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000000000000000000010000000000",
    "00000000000000000000001000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000000000000000000000000001111",
    "10000000000000000000000000000000000000000000000000000011111",
    "100000000000000000000000000000000000000000000000000000111111",
    "1000000000000000000000000000000000000000000000000000001111111",
    "10000000000000000000000000000000000000000000000000000011111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "1000000000000000000000000000000000000000000000000000001111",
    "100000000000000000000000000000000000000000000000000000111111",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111",
    "100000000000000000000000000000000000000000000000000000111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000000000000000000000000001111",
    "10000000000000000000000000000000000000000000000000000011111",
    "100000000000000000000000000000000000000000000000000000111111",
    "1000000000000000000000000000000000000000000000000000001111111",
    "10000000000000000000000000000000000000000000000000000011111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "1000000000000000000000000000000000000000000000000000001111",
    "100000000000000000000000000000000000000000000000000000111111",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111",
    "100000000000000000000000000000000000000000000000000000111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000000000000000000000000001111",
    "10000000000000000000000000000000000000000000000000000011111",
    "100000000000000000000000000000000000000000000000000000111111",
    "1000000000000000000000000000000000000000000000000000001111111",
    "10000000000000000000000000000000000000000000000000000011111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "1000000000000000000000000000000000000000000000000000001111",
    "100000000000000000000000000000000000000000000000000000111111",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111",
    "100000000000000000000000000000000000000000000000000000111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111",
    "1000000000000000000000000000000000000000000000000000001111",
    "10000000000000000000000000000000000000000000000000000011111",
    "100000000000000000000000000000000000000000000000000000111111",
    "1000000000000000000000000000000000000000000000000000001111111",
    "10000000000000000000000000000000000000000000000000000011111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "10000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "1000000000000000000000000000000000000000000000000000001111",
    "100000000000000000000000000000000000000000000000000000111111",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "100000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111",
    "100000000000000000000000000000000000000000000000000000111111",
    "100000000000000000000000000000000000000000000000000000111111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000000000111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000000000000000",
    "1000000000000000000000000000000000000000000000000000001",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000000000000000000000000011111111",
  },
},
{
  .value = 1023,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000000000000000000000001111111111",
    "10000000000000000000000000000000",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "0",
    "00",
    "001",
    "0011",
    "00110",
    "001101",
    "0011011",
    "00110110",
    "001101101",
    "0011011011",
    "00110110111",
    "001101101111",
    "0011011011110",
    "00110110111100",
    "001101101111001",
    "0011011011110010",
    "00110110111100100",
    "001101101111001001",
    "0011011011110010010",
    "00110110111100100101",
    "001101101111001001010",
    "0011011011110010010100",
    "00110110111100100101001",
    "001101101111001001010011",
    "0011011011110010010100110",
    "00110110111100100101001100",
    "001101101111001001010011001",
    "0011011011110010010100110011",
    "00110110111100100101001100111",
    "001101101111001001010011001110",
    "0011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "00",
    "0011",
    "001101",
    "00110110",
    "0011011011",
    "001101101111",
    "00110110111100",
    "0011011011110010",
    "001101101111001001",
    "00110110111100100101",
    "0011011011110010010100",
    "001101101111001001010011",
    "00110110111100100101001100",
    "0011011011110010010100110011",
    "001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "001",
    "001101",
    "001101101",
    "001101101111",
    "001101101111001",
    "001101101111001001",
    "001101101111001001010",
    "001101101111001001010011",
    "001101101111001001010011001",
    "001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "0011",
    "00110110",
    "001101101111",
    "0011011011110010",
    "00110110111100100101",
    "001101101111001001010011",
    "0011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "00110110",
    "0011011011110010",
    "001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "0011011011",
    "00110110111100100101",
    "001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "00110110111",
    "0011011011110010010100",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "0011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
    "0011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00110110111100100101001100111011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "0",
    "00",
    "000",
    "0001",
    "00011",
    "000110",
    "0001101",
    "00011011",
    "000110110",
    "0001101101",
    "00011011011",
    "000110110111",
    "0001101101111",
    "00011011011110",
    "000110110111100",
    "0001101101111001",
    "00011011011110010",
    "000110110111100100",
    "0001101101111001001",
    "00011011011110010010",
    "000110110111100100101",
    "0001101101111001001010",
    "00011011011110010010100",
    "000110110111100100101001",
    "0001101101111001001010011",
    "00011011011110010010100110",
    "000110110111100100101001100",
    "0001101101111001001010011001",
    "00011011011110010010100110011",
    "000110110111100100101001100111",
    "0001101101111001001010011001111",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "00",
    "0001",
    "000110",
    "00011011",
    "0001101101",
    "000110110111",
    "00011011011110",
    "0001101101111001",
    "000110110111100100",
    "00011011011110010010",
    "0001101101111001001010",
    "000110110111100100101001",
    "00011011011110010010100110",
    "0001101101111001001010011001",
    "000110110111100100101001100111",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "000",
    "000110",
    "000110110",
    "000110110111",
    "000110110111100",
    "000110110111100100",
    "000110110111100100101",
    "000110110111100100101001",
    "000110110111100100101001100",
    "000110110111100100101001100111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "0001",
    "00011011",
    "000110110111",
    "0001101101111001",
    "00011011011110010010",
    "000110110111100100101001",
    "0001101101111001001010011001",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "00011011",
    "0001101101111001",
    "000110110111100100101001",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "0001101101",
    "00011011011110010010",
    "000110110111100100101001100111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "00011011011",
    "0001101101111001001010",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "0001101101111001",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "0001101101111001001010011001111",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000110110111100100101001100111100",
    "00011011011110010010100110011110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000001",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110",
    "10000000000000000000000000000000001101",
    "100000000000000000000000000000000011011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101",
    "100000000000000000000000000000000011011011",
    "1000000000000000000000000000000000110110111",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110",
    "1000000000000000000000000000000000110110111100",
    "10000000000000000000000000000000001101101111001",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010",
    "100000000000000000000000000000000011011011110010010100",
    "1000000000000000000000000000000000110110111100100101001",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110",
    "1000000000000000000000000000000000110110111100100101001100",
    "10000000000000000000000000000000001101101111001001010011001",
    "100000000000000000000000000000000011011011110010010100110011",
    "1000000000000000000000000000000000110110111100100101001100111",
    "10000000000000000000000000000000001101101111001001010011001110",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "10000000000000000000000000000000001101",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001",
    "1000000000000000000000000000000000110110111100100101",
    "100000000000000000000000000000000011011011110010010100",
    "10000000000000000000000000000000001101101111001001010011",
    "1000000000000000000000000000000000110110111100100101001100",
    "100000000000000000000000000000000011011011110010010100110011",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "100000000000000000000000000000000011011",
    "100000000000000000000000000000000011011011",
    "100000000000000000000000000000000011011011110",
    "100000000000000000000000000000000011011011110010",
    "100000000000000000000000000000000011011011110010010",
    "100000000000000000000000000000000011011011110010010100",
    "100000000000000000000000000000000011011011110010010100110",
    "100000000000000000000000000000000011011011110010010100110011",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100100101001",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000000000000000000000000",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000001",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110",
    "10000000000000000000000000000000001101",
    "100000000000000000000000000000000011011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101",
    "100000000000000000000000000000000011011011",
    "1000000000000000000000000000000000110110111",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110",
    "1000000000000000000000000000000000110110111100",
    "10000000000000000000000000000000001101101111001",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010",
    "100000000000000000000000000000000011011011110010010100",
    "1000000000000000000000000000000000110110111100100101001",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110",
    "1000000000000000000000000000000000110110111100100101001100",
    "10000000000000000000000000000000001101101111001001010011001",
    "100000000000000000000000000000000011011011110010010100110011",
    "1000000000000000000000000000000000110110111100100101001100111",
    "10000000000000000000000000000000001101101111001001010011001110",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "10000000000000000000000000000000001101",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001",
    "1000000000000000000000000000000000110110111100100101",
    "100000000000000000000000000000000011011011110010010100",
    "10000000000000000000000000000000001101101111001001010011",
    "1000000000000000000000000000000000110110111100100101001100",
    "100000000000000000000000000000000011011011110010010100110011",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "100000000000000000000000000000000011011",
    "100000000000000000000000000000000011011011",
    "100000000000000000000000000000000011011011110",
    "100000000000000000000000000000000011011011110010",
    "100000000000000000000000000000000011011011110010010",
    "100000000000000000000000000000000011011011110010010100",
    "100000000000000000000000000000000011011011110010010100110",
    "100000000000000000000000000000000011011011110010010100110011",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100100101001",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000000000000000000000000",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000001",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110",
    "10000000000000000000000000000000001101",
    "100000000000000000000000000000000011011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101",
    "100000000000000000000000000000000011011011",
    "1000000000000000000000000000000000110110111",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110",
    "1000000000000000000000000000000000110110111100",
    "10000000000000000000000000000000001101101111001",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010",
    "100000000000000000000000000000000011011011110010010100",
    "1000000000000000000000000000000000110110111100100101001",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110",
    "1000000000000000000000000000000000110110111100100101001100",
    "10000000000000000000000000000000001101101111001001010011001",
    "100000000000000000000000000000000011011011110010010100110011",
    "1000000000000000000000000000000000110110111100100101001100111",
    "10000000000000000000000000000000001101101111001001010011001110",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "10000000000000000000000000000000001101",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001",
    "1000000000000000000000000000000000110110111100100101",
    "100000000000000000000000000000000011011011110010010100",
    "10000000000000000000000000000000001101101111001001010011",
    "1000000000000000000000000000000000110110111100100101001100",
    "100000000000000000000000000000000011011011110010010100110011",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "100000000000000000000000000000000011011",
    "100000000000000000000000000000000011011011",
    "100000000000000000000000000000000011011011110",
    "100000000000000000000000000000000011011011110010",
    "100000000000000000000000000000000011011011110010010",
    "100000000000000000000000000000000011011011110010010100",
    "100000000000000000000000000000000011011011110010010100110",
    "100000000000000000000000000000000011011011110010010100110011",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100100101001",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000000000000000000000000",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1",
    "10",
    "100",
    "1000",
    "10000",
    "100000",
    "1000000",
    "10000000",
    "100000000",
    "1000000000",
    "10000000000",
    "100000000000",
    "1000000000000",
    "10000000000000",
    "100000000000000",
    "1000000000000000",
    "10000000000000000",
    "100000000000000000",
    "1000000000000000000",
    "10000000000000000000",
    "100000000000000000000",
    "1000000000000000000000",
    "10000000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000",
    "10000000000000000000000000",
    "100000000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "10000000000000000000000000000000001",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110",
    "10000000000000000000000000000000001101",
    "100000000000000000000000000000000011011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101",
    "100000000000000000000000000000000011011011",
    "1000000000000000000000000000000000110110111",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110",
    "1000000000000000000000000000000000110110111100",
    "10000000000000000000000000000000001101101111001",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010",
    "100000000000000000000000000000000011011011110010010100",
    "1000000000000000000000000000000000110110111100100101001",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110",
    "1000000000000000000000000000000000110110111100100101001100",
    "10000000000000000000000000000000001101101111001001010011001",
    "100000000000000000000000000000000011011011110010010100110011",
    "1000000000000000000000000000000000110110111100100101001100111",
    "10000000000000000000000000000000001101101111001001010011001110",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10",
    "1000",
    "100000",
    "10000000",
    "1000000000",
    "100000000000",
    "10000000000000",
    "1000000000000000",
    "100000000000000000",
    "10000000000000000000",
    "1000000000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000",
    "1000000000000000000000000000",
    "100000000000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "10000000000000000000000000000000001101",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001",
    "1000000000000000000000000000000000110110111100100101",
    "100000000000000000000000000000000011011011110010010100",
    "10000000000000000000000000000000001101101111001001010011",
    "1000000000000000000000000000000000110110111100100101001100",
    "100000000000000000000000000000000011011011110010010100110011",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "100",
    "100000",
    "100000000",
    "100000000000",
    "100000000000000",
    "100000000000000000",
    "100000000000000000000",
    "100000000000000000000000",
    "100000000000000000000000000",
    "100000000000000000000000000000",
    "100000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "100000000000000000000000000000000011011",
    "100000000000000000000000000000000011011011",
    "100000000000000000000000000000000011011011110",
    "100000000000000000000000000000000011011011110010",
    "100000000000000000000000000000000011011011110010010",
    "100000000000000000000000000000000011011011110010010100",
    "100000000000000000000000000000000011011011110010010100110",
    "100000000000000000000000000000000011011011110010010100110011",
    "100000000000000000000000000000000011011011110010010100110011101",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000",
    "10000000",
    "100000000000",
    "1000000000000000",
    "10000000000000000000",
    "100000000000000000000000",
    "1000000000000000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111",
    "100000000000000000000000000000000011011011110010",
    "1000000000000000000000000000000000110110111100100101",
    "10000000000000000000000000000000001101101111001001010011",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000",
    "1000000000000000",
    "100000000000000000000000",
    "10000000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "100000000000000000000000000000000011011011110010",
    "10000000000000000000000000000000001101101111001001010011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000",
    "10000000000000000000",
    "100000000000000000000000000000",
    "1000000000000000000000000000000000110110",
    "10000000000000000000000000000000001101101111001001",
    "100000000000000000000000000000000011011011110010010100110011",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000",
    "1000000000000000000000",
    "100000000000000000000000000000000",
    "10000000000000000000000000000000001101101111",
    "1000000000000000000000000000000000110110111100100101001",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000",
    "10000000000000000000000000000000",
    "100000000000000000000000000000000011011011110010",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "1000000000000000000000000000000",
    "10000000000000000000000000000000001101101111001001010011001110",
  },
},
{
  .value = 921850683,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1000000000000000000000000000000000110110111100100101001100111011",
    "10000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1001",
    "10010",
    "100100",
    "1001000",
    "10010000",
    "100100000",
    "1001000000",
    "10010000000",
    "100100000000",
    "1001000000000",
    "10010000000000",
    "100100000000000",
    "1001000000000000",
    "10010000000000000",
    "100100000000000000",
    "1001000000000000000",
    "10010000000000000000",
    "100100000000000000000",
    "1001000000000000000000",
    "10010000000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000",
    "10010000000000000000000000",
    "100100000000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000",
    "1001000000000000000000000000000000",
    "10010000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1001",
    "100100",
    "10010000",
    "1001000000",
    "100100000000",
    "10010000000000",
    "1001000000000000",
    "100100000000000000",
    "10010000000000000000",
    "1001000000000000000000",
    "100100000000000000000000",
    "10010000000000000000000000",
    "1001000000000000000000000000",
    "100100000000000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100100",
    "100100000",
    "100100000000",
    "100100000000000",
    "100100000000000000",
    "100100000000000000000",
    "100100000000000000000000",
    "100100000000000000000000000",
    "100100000000000000000000000000",
    "100100000000000000000000000000000",
    "100100000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001",
    "10010000",
    "100100000000",
    "1001000000000000",
    "10010000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000",
    "1001000000000000",
    "100100000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000",
    "10010000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000",
    "1001000000000000000000",
    "100100000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1001",
    "10010",
    "100100",
    "1001000",
    "10010000",
    "100100000",
    "1001000000",
    "10010000000",
    "100100000000",
    "1001000000000",
    "10010000000000",
    "100100000000000",
    "1001000000000000",
    "10010000000000000",
    "100100000000000000",
    "1001000000000000000",
    "10010000000000000000",
    "100100000000000000000",
    "1001000000000000000000",
    "10010000000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000",
    "10010000000000000000000000",
    "100100000000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000",
    "1001000000000000000000000000000000",
    "10010000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1001",
    "100100",
    "10010000",
    "1001000000",
    "100100000000",
    "10010000000000",
    "1001000000000000",
    "100100000000000000",
    "10010000000000000000",
    "1001000000000000000000",
    "100100000000000000000000",
    "10010000000000000000000000",
    "1001000000000000000000000000",
    "100100000000000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100100",
    "100100000",
    "100100000000",
    "100100000000000",
    "100100000000000000",
    "100100000000000000000",
    "100100000000000000000000",
    "100100000000000000000000000",
    "100100000000000000000000000000",
    "100100000000000000000000000000000",
    "100100000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001",
    "10010000",
    "100100000000",
    "1001000000000000",
    "10010000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000",
    "1001000000000000",
    "100100000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000",
    "10010000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000",
    "1001000000000000000000",
    "100100000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1",
    "10",
    "100",
    "1001",
    "10010",
    "100100",
    "1001000",
    "10010000",
    "100100000",
    "1001000000",
    "10010000000",
    "100100000000",
    "1001000000000",
    "10010000000000",
    "100100000000000",
    "1001000000000000",
    "10010000000000000",
    "100100000000000000",
    "1001000000000000000",
    "10010000000000000000",
    "100100000000000000000",
    "1001000000000000000000",
    "10010000000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000",
    "10010000000000000000000000",
    "100100000000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000",
    "1001000000000000000000000000000000",
    "10010000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10",
    "1001",
    "100100",
    "10010000",
    "1001000000",
    "100100000000",
    "10010000000000",
    "1001000000000000",
    "100100000000000000",
    "10010000000000000000",
    "1001000000000000000000",
    "100100000000000000000000",
    "10010000000000000000000000",
    "1001000000000000000000000000",
    "100100000000000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000",
    "100100000000000000000000000000000000",
    "10010000000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "100",
    "100100",
    "100100000",
    "100100000000",
    "100100000000000",
    "100100000000000000",
    "100100000000000000000",
    "100100000000000000000000",
    "100100000000000000000000000",
    "100100000000000000000000000000",
    "100100000000000000000000000000000",
    "100100000000000000000000000000000000",
    "100100000000000000000000000000000000000",
    "100100000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001",
    "10010000",
    "100100000000",
    "1001000000000000",
    "10010000000000000000",
    "100100000000000000000000",
    "1001000000000000000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000",
    "1001000000000000",
    "100100000000000000000000",
    "10010000000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000",
    "10010000000000000000",
    "100100000000000000000000000000",
    "1001000000000000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000",
    "100100000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000",
    "1001000000000000000000",
    "100100000000000000000000000000000",
    "10010000000000000000000000000000000000000000",
    "1001000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000",
    "10010000000000000000000000000000",
    "100100000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "1001000000000000000000000000000",
    "10010000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = 1152921504606846976,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1001000000000000000000000000000000000000000000000000000000000000",
    "10010000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000",
    "0",
    "00",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000",
    "00",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000",
    "00",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000",
    "000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000",
    "0000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "00000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "00000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "00000000000",
    "000",
    "000000",
    "000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "00000000000",
    "0000",
    "00000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "00000000000",
    "00000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "00000000000",
    "0000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "00000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0",
    "01",
    "011",
    "0111",
    "01111",
    "011111",
    "0111111",
    "01111111",
    "011111111",
    "0111111111",
    "01111111111",
    "011111111111",
    "0111111111111",
    "01111111111111",
    "011111111111111",
    "0111111111111111",
    "01111111111111111",
    "011111111111111111",
    "0111111111111111111",
    "01111111111111111111",
    "011111111111111111111",
    "0111111111111111111111",
    "01111111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111",
    "01111111111111111111111111",
    "011111111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "01111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01",
    "0111",
    "011111",
    "01111111",
    "0111111111",
    "011111111111",
    "01111111111111",
    "0111111111111111",
    "011111111111111111",
    "01111111111111111111",
    "0111111111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111",
    "0111111111111111111111111111",
    "011111111111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "01111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "011",
    "011111",
    "011111111",
    "011111111111",
    "011111111111111",
    "011111111111111111",
    "011111111111111111111",
    "011111111111111111111111",
    "011111111111111111111111111",
    "011111111111111111111111111111",
    "011111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "011111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111",
    "01111111",
    "011111111111",
    "0111111111111111",
    "01111111111111111111",
    "011111111111111111111111",
    "0111111111111111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111",
    "0111111111111111",
    "011111111111111111111111",
    "01111111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111",
    "01111111111111111111",
    "011111111111111111111111111111",
    "0111111111111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111",
    "0111111111111111111111",
    "011111111111111111111111111111111",
    "01111111111111111111111111111111111111111111",
    "0111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111",
    "01111111111111111111111111111111",
    "011111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "0111111111111111111111111111111",
    "01111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = -1,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0111111111111111111111111111111111111111111111111111111111111111",
    "01111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "11111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
    "111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
    "111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "11111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
    "111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
    "111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1",
    "11",
    "111",
    "1111",
    "11111",
    "111111",
    "1111111",
    "11111111",
    "111111111",
    "1111111111",
    "11111111111",
    "111111111111",
    "1111111111111",
    "11111111111111",
    "111111111111111",
    "1111111111111111",
    "11111111111111111",
    "111111111111111111",
    "1111111111111111111",
    "11111111111111111111",
    "111111111111111111111",
    "1111111111111111111111",
    "11111111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111",
    "11111111111111111111111111",
    "111111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "11111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11",
    "1111",
    "111111",
    "11111111",
    "1111111111",
    "111111111111",
    "11111111111111",
    "1111111111111111",
    "111111111111111111",
    "11111111111111111111",
    "1111111111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111",
    "1111111111111111111111111111",
    "111111111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "11111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "111",
    "111111",
    "111111111",
    "111111111111",
    "111111111111111",
    "111111111111111111",
    "111111111111111111111",
    "111111111111111111111111",
    "111111111111111111111111111",
    "111111111111111111111111111111",
    "111111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111",
    "11111111",
    "111111111111",
    "1111111111111111",
    "11111111111111111111",
    "111111111111111111111111",
    "1111111111111111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111",
    "1111111111111111",
    "111111111111111111111111",
    "11111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111",
    "11111111111111111111",
    "111111111111111111111111111111",
    "1111111111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111",
    "1111111111111111111111",
    "111111111111111111111111111111111",
    "11111111111111111111111111111111111111111111",
    "1111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111",
    "11111111111111111111111111111111",
    "111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "1111111111111111111111111111111",
    "11111111111111111111111111111111111111111111111111111111111111",
  },
},
{
  .value = 9223372036854775807,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "1111111111111111111111111111111111111111111111111111111111111111",
    "11111111111111111111111111111111",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "00000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
    "000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 13 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "00000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
    "000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 32 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "00000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
    "000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 1024 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "00000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
    "000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = true, .value = INT64_MIN },
  .max = { .set = true, .value = 4294967295 },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 1,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0",
    "00",
    "000",
    "0000",
    "00000",
    "000000",
    "0000000",
    "00000000",
    "000000000",
    "0000000000",
    "00000000000",
    "000000000000",
    "0000000000000",
    "00000000000000",
    "000000000000000",
    "0000000000000000",
    "00000000000000000",
    "000000000000000000",
    "0000000000000000000",
    "00000000000000000000",
    "000000000000000000000",
    "0000000000000000000000",
    "00000000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000",
    "00000000000000000000000000",
    "000000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "00000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 2,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00",
    "0000",
    "000000",
    "00000000",
    "0000000000",
    "000000000000",
    "00000000000000",
    "0000000000000000",
    "000000000000000000",
    "00000000000000000000",
    "0000000000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000",
    "0000000000000000000000000000",
    "000000000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "00000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 3,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "000",
    "000000",
    "000000000",
    "000000000000",
    "000000000000000",
    "000000000000000000",
    "000000000000000000000",
    "000000000000000000000000",
    "000000000000000000000000000",
    "000000000000000000000000000000",
    "000000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 4,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000",
    "00000000",
    "000000000000",
    "0000000000000000",
    "00000000000000000000",
    "000000000000000000000000",
    "0000000000000000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 8,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000",
    "0000000000000000",
    "000000000000000000000000",
    "00000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 10,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000",
    "00000000000000000000",
    "000000000000000000000000000000",
    "0000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 11,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000",
    "0000000000000000000000",
    "000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 16,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000",
    "00000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 31,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .value = INT64_MIN,
  .min = { .set = false },
  .max = { .set = false },
  .sparsity = 32,
  .expectEdges = {
    "root",
    "0000000000000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/000077500000000000000000000000001521103432300213455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-min-cover/mincover_decimal128.cstruct000066400000000000000000014355641521103432300265330ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = mc_dec128_from_string("-100.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("-100.000000"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000001",
  },
},
{
  .lowerBound = mc_dec128_from_string("-100.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("-100.000000"),
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000001",
  },
},
{
  .lowerBound = mc_dec128_from_string("-100.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("123456789.000000"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000000000110000000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000000000110000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000000000110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000000000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011",
    "010100011100010011110111110010000011011001010011111001010111011101001010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010101",
    "010100011100010011110111110010000011011001010011111001010111011101001011",
    "0101000111000100111101111100100000110110010100111110010101110111010011",
    "01010001110001001111011111001000001101100101001111100101011101110101",
    "0101000111000100111101111100100000110110010100111110010101110111011",
    "01010001110001001111011111001000001101100101001111100101011101111",
    "0101000111000100111101111100100000110110010100111110010101111",
    "010100011100010011110111110010000011011001010011111001011",
    "0101000111000100111101111100100000110110010100111110011",
    "01010001110001001111011111001000001101100101001111101",
    "0101000111000100111101111100100000110110010100111111",
    "0101000111000100111101111100100000110110010101",
    "010100011100010011110111110010000011011001011",
    "0101000111000100111101111100100000110110011",
    "01010001110001001111011111001000001101101",
    "0101000111000100111101111100100000110111",
    "0101000111000100111101111100100000111",
    "0101000111000100111101111100100001",
    "010100011100010011110111110010001",
    "01010001110001001111011111001001",
    "0101000111000100111101111100101",
    "010100011100010011110111110011",
    "0101000111000100111101111101",
    "010100011100010011110111111",
    "010100011100010011111",
    "0101000111000101",
    "010100011100011",
    "0101000111001",
    "010100011101",
    "01010001111",
    "0101001",
    "010101",
    "01011",
    "011",
    "100",
    "10100",
    "101010",
    "1010110",
    "1010111000",
    "10101110010000",
    "101011100100010",
    "10101110010001100",
    "1010111001000110100",
    "10101110010001101010000",
    "1010111001000110101000100000000",
    "101011100100011010100010000000100",
    "1010111001000110101000100000001010",
    "10101110010001101010001000000010110",
    "1010111001000110101000100000001011100",
    "101011100100011010100010000000101110100",
    "1010111001000110101000100000001011101010",
    "101011100100011010100010000000101110101100",
    "1010111001000110101000100000001011101011010",
    "10101110010001101010001000000010111010110110",
    "101011100100011010100010000000101110101101110",
    "101011100100011010100010000000101110101101111000000",
    "1010111001000110101000100000001011101011011110000010",
    "10101110010001101010001000000010111010110111100000110",
    "101011100100011010100010000000101110101101111000001110000",
    "10101110010001101010001000000010111010110111100000111000100000",
    "1010111001000110101000100000001011101011011110000011100010000100",
    "101011100100011010100010000000101110101101111000001110001000010100000",
    "1010111001000110101000100000001011101011011110000011100010000101000010",
    "101011100100011010100010000000101110101101111000001110001000010100001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111111111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111111111001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111111111001111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111111111001111111100",
  },
},
{
  .lowerBound = mc_dec128_from_string("-100.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("123456789.000000"),
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100000111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100001111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100010111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000000000000000001100011111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000000111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001000111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001001111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001010111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000001111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010000111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010001111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010010111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000010111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011000111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011001111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011010111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010010000000000000000011111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000001111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000010111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000011111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000100111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000101111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000000111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001001111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001010111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001011111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001100111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001101111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000001111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010001111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010010111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010011111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010100111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010101111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000010111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011001111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011010111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011011111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011100111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011101111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110110100100100000000011111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001000111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001001111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001010111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001010111101101101001001011111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010010111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011010111111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011000111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011001111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011010111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011011111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011100111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011101111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011110111",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111000",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111001",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111010",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111011",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111100",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111101",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111110",
    "010100011100010011110111110010000011011001010011111001010111011101001010010101111011011011111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011110111111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111000111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111001111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111010111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111011111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111100111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111101111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111110111",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111000",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111001",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111010",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111011",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111100",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111101",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111110",
    "0101000111000100111101111100100000110110010100111110010101110111010010100101011111111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101001111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101010111111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011000111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011001111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011010111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011011111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011100111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011101111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011110111",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111000",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111001",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111010",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111011",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111100",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111101",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111110",
    "01010001110001001111011111001000001101100101001111100101011101110100101011111111",
    "010100011100010011110111110010000011011001010011111001010111011101001011",
    "010100011100010011110111110010000011011001010011111001010111011101001100",
    "010100011100010011110111110010000011011001010011111001010111011101001101",
    "010100011100010011110111110010000011011001010011111001010111011101001110",
    "010100011100010011110111110010000011011001010011111001010111011101001111",
    "010100011100010011110111110010000011011001010011111001010111011101010000",
    "010100011100010011110111110010000011011001010011111001010111011101010001",
    "010100011100010011110111110010000011011001010011111001010111011101010010",
    "010100011100010011110111110010000011011001010011111001010111011101010011",
    "010100011100010011110111110010000011011001010011111001010111011101010100",
    "010100011100010011110111110010000011011001010011111001010111011101010101",
    "010100011100010011110111110010000011011001010011111001010111011101010110",
    "010100011100010011110111110010000011011001010011111001010111011101010111",
    "010100011100010011110111110010000011011001010011111001010111011101011000",
    "010100011100010011110111110010000011011001010011111001010111011101011001",
    "010100011100010011110111110010000011011001010011111001010111011101011010",
    "010100011100010011110111110010000011011001010011111001010111011101011011",
    "010100011100010011110111110010000011011001010011111001010111011101011100",
    "010100011100010011110111110010000011011001010011111001010111011101011101",
    "010100011100010011110111110010000011011001010011111001010111011101011110",
    "010100011100010011110111110010000011011001010011111001010111011101011111",
    "010100011100010011110111110010000011011001010011111001010111011101100000",
    "010100011100010011110111110010000011011001010011111001010111011101100001",
    "010100011100010011110111110010000011011001010011111001010111011101100010",
    "010100011100010011110111110010000011011001010011111001010111011101100011",
    "010100011100010011110111110010000011011001010011111001010111011101100100",
    "010100011100010011110111110010000011011001010011111001010111011101100101",
    "010100011100010011110111110010000011011001010011111001010111011101100110",
    "010100011100010011110111110010000011011001010011111001010111011101100111",
    "010100011100010011110111110010000011011001010011111001010111011101101000",
    "010100011100010011110111110010000011011001010011111001010111011101101001",
    "010100011100010011110111110010000011011001010011111001010111011101101010",
    "010100011100010011110111110010000011011001010011111001010111011101101011",
    "010100011100010011110111110010000011011001010011111001010111011101101100",
    "010100011100010011110111110010000011011001010011111001010111011101101101",
    "010100011100010011110111110010000011011001010011111001010111011101101110",
    "010100011100010011110111110010000011011001010011111001010111011101101111",
    "010100011100010011110111110010000011011001010011111001010111011101110000",
    "010100011100010011110111110010000011011001010011111001010111011101110001",
    "010100011100010011110111110010000011011001010011111001010111011101110010",
    "010100011100010011110111110010000011011001010011111001010111011101110011",
    "010100011100010011110111110010000011011001010011111001010111011101110100",
    "010100011100010011110111110010000011011001010011111001010111011101110101",
    "010100011100010011110111110010000011011001010011111001010111011101110110",
    "010100011100010011110111110010000011011001010011111001010111011101110111",
    "010100011100010011110111110010000011011001010011111001010111011101111000",
    "010100011100010011110111110010000011011001010011111001010111011101111001",
    "010100011100010011110111110010000011011001010011111001010111011101111010",
    "010100011100010011110111110010000011011001010011111001010111011101111011",
    "010100011100010011110111110010000011011001010011111001010111011101111100",
    "010100011100010011110111110010000011011001010011111001010111011101111101",
    "010100011100010011110111110010000011011001010011111001010111011101111110",
    "010100011100010011110111110010000011011001010011111001010111011101111111",
    "010100011100010011110111110010000011011001010011111001010111011110000000",
    "010100011100010011110111110010000011011001010011111001010111011110000001",
    "010100011100010011110111110010000011011001010011111001010111011110000010",
    "010100011100010011110111110010000011011001010011111001010111011110000011",
    "010100011100010011110111110010000011011001010011111001010111011110000100",
    "010100011100010011110111110010000011011001010011111001010111011110000101",
    "010100011100010011110111110010000011011001010011111001010111011110000110",
    "010100011100010011110111110010000011011001010011111001010111011110000111",
    "010100011100010011110111110010000011011001010011111001010111011110001000",
    "010100011100010011110111110010000011011001010011111001010111011110001001",
    "010100011100010011110111110010000011011001010011111001010111011110001010",
    "010100011100010011110111110010000011011001010011111001010111011110001011",
    "010100011100010011110111110010000011011001010011111001010111011110001100",
    "010100011100010011110111110010000011011001010011111001010111011110001101",
    "010100011100010011110111110010000011011001010011111001010111011110001110",
    "010100011100010011110111110010000011011001010011111001010111011110001111",
    "010100011100010011110111110010000011011001010011111001010111011110010000",
    "010100011100010011110111110010000011011001010011111001010111011110010001",
    "010100011100010011110111110010000011011001010011111001010111011110010010",
    "010100011100010011110111110010000011011001010011111001010111011110010011",
    "010100011100010011110111110010000011011001010011111001010111011110010100",
    "010100011100010011110111110010000011011001010011111001010111011110010101",
    "010100011100010011110111110010000011011001010011111001010111011110010110",
    "010100011100010011110111110010000011011001010011111001010111011110010111",
    "010100011100010011110111110010000011011001010011111001010111011110011000",
    "010100011100010011110111110010000011011001010011111001010111011110011001",
    "010100011100010011110111110010000011011001010011111001010111011110011010",
    "010100011100010011110111110010000011011001010011111001010111011110011011",
    "010100011100010011110111110010000011011001010011111001010111011110011100",
    "010100011100010011110111110010000011011001010011111001010111011110011101",
    "010100011100010011110111110010000011011001010011111001010111011110011110",
    "010100011100010011110111110010000011011001010011111001010111011110011111",
    "010100011100010011110111110010000011011001010011111001010111011110100000",
    "010100011100010011110111110010000011011001010011111001010111011110100001",
    "010100011100010011110111110010000011011001010011111001010111011110100010",
    "010100011100010011110111110010000011011001010011111001010111011110100011",
    "010100011100010011110111110010000011011001010011111001010111011110100100",
    "010100011100010011110111110010000011011001010011111001010111011110100101",
    "010100011100010011110111110010000011011001010011111001010111011110100110",
    "010100011100010011110111110010000011011001010011111001010111011110100111",
    "010100011100010011110111110010000011011001010011111001010111011110101000",
    "010100011100010011110111110010000011011001010011111001010111011110101001",
    "010100011100010011110111110010000011011001010011111001010111011110101010",
    "010100011100010011110111110010000011011001010011111001010111011110101011",
    "010100011100010011110111110010000011011001010011111001010111011110101100",
    "010100011100010011110111110010000011011001010011111001010111011110101101",
    "010100011100010011110111110010000011011001010011111001010111011110101110",
    "010100011100010011110111110010000011011001010011111001010111011110101111",
    "010100011100010011110111110010000011011001010011111001010111011110110000",
    "010100011100010011110111110010000011011001010011111001010111011110110001",
    "010100011100010011110111110010000011011001010011111001010111011110110010",
    "010100011100010011110111110010000011011001010011111001010111011110110011",
    "010100011100010011110111110010000011011001010011111001010111011110110100",
    "010100011100010011110111110010000011011001010011111001010111011110110101",
    "010100011100010011110111110010000011011001010011111001010111011110110110",
    "010100011100010011110111110010000011011001010011111001010111011110110111",
    "010100011100010011110111110010000011011001010011111001010111011110111000",
    "010100011100010011110111110010000011011001010011111001010111011110111001",
    "010100011100010011110111110010000011011001010011111001010111011110111010",
    "010100011100010011110111110010000011011001010011111001010111011110111011",
    "010100011100010011110111110010000011011001010011111001010111011110111100",
    "010100011100010011110111110010000011011001010011111001010111011110111101",
    "010100011100010011110111110010000011011001010011111001010111011110111110",
    "010100011100010011110111110010000011011001010011111001010111011110111111",
    "010100011100010011110111110010000011011001010011111001010111011111000000",
    "010100011100010011110111110010000011011001010011111001010111011111000001",
    "010100011100010011110111110010000011011001010011111001010111011111000010",
    "010100011100010011110111110010000011011001010011111001010111011111000011",
    "010100011100010011110111110010000011011001010011111001010111011111000100",
    "010100011100010011110111110010000011011001010011111001010111011111000101",
    "010100011100010011110111110010000011011001010011111001010111011111000110",
    "010100011100010011110111110010000011011001010011111001010111011111000111",
    "010100011100010011110111110010000011011001010011111001010111011111001000",
    "010100011100010011110111110010000011011001010011111001010111011111001001",
    "010100011100010011110111110010000011011001010011111001010111011111001010",
    "010100011100010011110111110010000011011001010011111001010111011111001011",
    "010100011100010011110111110010000011011001010011111001010111011111001100",
    "010100011100010011110111110010000011011001010011111001010111011111001101",
    "010100011100010011110111110010000011011001010011111001010111011111001110",
    "010100011100010011110111110010000011011001010011111001010111011111001111",
    "010100011100010011110111110010000011011001010011111001010111011111010000",
    "010100011100010011110111110010000011011001010011111001010111011111010001",
    "010100011100010011110111110010000011011001010011111001010111011111010010",
    "010100011100010011110111110010000011011001010011111001010111011111010011",
    "010100011100010011110111110010000011011001010011111001010111011111010100",
    "010100011100010011110111110010000011011001010011111001010111011111010101",
    "010100011100010011110111110010000011011001010011111001010111011111010110",
    "010100011100010011110111110010000011011001010011111001010111011111010111",
    "010100011100010011110111110010000011011001010011111001010111011111011000",
    "010100011100010011110111110010000011011001010011111001010111011111011001",
    "010100011100010011110111110010000011011001010011111001010111011111011010",
    "010100011100010011110111110010000011011001010011111001010111011111011011",
    "010100011100010011110111110010000011011001010011111001010111011111011100",
    "010100011100010011110111110010000011011001010011111001010111011111011101",
    "010100011100010011110111110010000011011001010011111001010111011111011110",
    "010100011100010011110111110010000011011001010011111001010111011111011111",
    "010100011100010011110111110010000011011001010011111001010111011111100000",
    "010100011100010011110111110010000011011001010011111001010111011111100001",
    "010100011100010011110111110010000011011001010011111001010111011111100010",
    "010100011100010011110111110010000011011001010011111001010111011111100011",
    "010100011100010011110111110010000011011001010011111001010111011111100100",
    "010100011100010011110111110010000011011001010011111001010111011111100101",
    "010100011100010011110111110010000011011001010011111001010111011111100110",
    "010100011100010011110111110010000011011001010011111001010111011111100111",
    "010100011100010011110111110010000011011001010011111001010111011111101000",
    "010100011100010011110111110010000011011001010011111001010111011111101001",
    "010100011100010011110111110010000011011001010011111001010111011111101010",
    "010100011100010011110111110010000011011001010011111001010111011111101011",
    "010100011100010011110111110010000011011001010011111001010111011111101100",
    "010100011100010011110111110010000011011001010011111001010111011111101101",
    "010100011100010011110111110010000011011001010011111001010111011111101110",
    "010100011100010011110111110010000011011001010011111001010111011111101111",
    "010100011100010011110111110010000011011001010011111001010111011111110000",
    "010100011100010011110111110010000011011001010011111001010111011111110001",
    "010100011100010011110111110010000011011001010011111001010111011111110010",
    "010100011100010011110111110010000011011001010011111001010111011111110011",
    "010100011100010011110111110010000011011001010011111001010111011111110100",
    "010100011100010011110111110010000011011001010011111001010111011111110101",
    "010100011100010011110111110010000011011001010011111001010111011111110110",
    "010100011100010011110111110010000011011001010011111001010111011111110111",
    "010100011100010011110111110010000011011001010011111001010111011111111000",
    "010100011100010011110111110010000011011001010011111001010111011111111001",
    "010100011100010011110111110010000011011001010011111001010111011111111010",
    "010100011100010011110111110010000011011001010011111001010111011111111011",
    "010100011100010011110111110010000011011001010011111001010111011111111100",
    "010100011100010011110111110010000011011001010011111001010111011111111101",
    "010100011100010011110111110010000011011001010011111001010111011111111110",
    "010100011100010011110111110010000011011001010011111001010111011111111111",
    "0101000111000100111101111100100000110110010100111110010101111000",
    "0101000111000100111101111100100000110110010100111110010101111001",
    "0101000111000100111101111100100000110110010100111110010101111010",
    "0101000111000100111101111100100000110110010100111110010101111011",
    "0101000111000100111101111100100000110110010100111110010101111100",
    "0101000111000100111101111100100000110110010100111110010101111101",
    "0101000111000100111101111100100000110110010100111110010101111110",
    "0101000111000100111101111100100000110110010100111110010101111111",
    "0101000111000100111101111100100000110110010100111110010110000000",
    "0101000111000100111101111100100000110110010100111110010110000001",
    "0101000111000100111101111100100000110110010100111110010110000010",
    "0101000111000100111101111100100000110110010100111110010110000011",
    "0101000111000100111101111100100000110110010100111110010110000100",
    "0101000111000100111101111100100000110110010100111110010110000101",
    "0101000111000100111101111100100000110110010100111110010110000110",
    "0101000111000100111101111100100000110110010100111110010110000111",
    "0101000111000100111101111100100000110110010100111110010110001000",
    "0101000111000100111101111100100000110110010100111110010110001001",
    "0101000111000100111101111100100000110110010100111110010110001010",
    "0101000111000100111101111100100000110110010100111110010110001011",
    "0101000111000100111101111100100000110110010100111110010110001100",
    "0101000111000100111101111100100000110110010100111110010110001101",
    "0101000111000100111101111100100000110110010100111110010110001110",
    "0101000111000100111101111100100000110110010100111110010110001111",
    "0101000111000100111101111100100000110110010100111110010110010000",
    "0101000111000100111101111100100000110110010100111110010110010001",
    "0101000111000100111101111100100000110110010100111110010110010010",
    "0101000111000100111101111100100000110110010100111110010110010011",
    "0101000111000100111101111100100000110110010100111110010110010100",
    "0101000111000100111101111100100000110110010100111110010110010101",
    "0101000111000100111101111100100000110110010100111110010110010110",
    "0101000111000100111101111100100000110110010100111110010110010111",
    "0101000111000100111101111100100000110110010100111110010110011000",
    "0101000111000100111101111100100000110110010100111110010110011001",
    "0101000111000100111101111100100000110110010100111110010110011010",
    "0101000111000100111101111100100000110110010100111110010110011011",
    "0101000111000100111101111100100000110110010100111110010110011100",
    "0101000111000100111101111100100000110110010100111110010110011101",
    "0101000111000100111101111100100000110110010100111110010110011110",
    "0101000111000100111101111100100000110110010100111110010110011111",
    "0101000111000100111101111100100000110110010100111110010110100000",
    "0101000111000100111101111100100000110110010100111110010110100001",
    "0101000111000100111101111100100000110110010100111110010110100010",
    "0101000111000100111101111100100000110110010100111110010110100011",
    "0101000111000100111101111100100000110110010100111110010110100100",
    "0101000111000100111101111100100000110110010100111110010110100101",
    "0101000111000100111101111100100000110110010100111110010110100110",
    "0101000111000100111101111100100000110110010100111110010110100111",
    "0101000111000100111101111100100000110110010100111110010110101000",
    "0101000111000100111101111100100000110110010100111110010110101001",
    "0101000111000100111101111100100000110110010100111110010110101010",
    "0101000111000100111101111100100000110110010100111110010110101011",
    "0101000111000100111101111100100000110110010100111110010110101100",
    "0101000111000100111101111100100000110110010100111110010110101101",
    "0101000111000100111101111100100000110110010100111110010110101110",
    "0101000111000100111101111100100000110110010100111110010110101111",
    "0101000111000100111101111100100000110110010100111110010110110000",
    "0101000111000100111101111100100000110110010100111110010110110001",
    "0101000111000100111101111100100000110110010100111110010110110010",
    "0101000111000100111101111100100000110110010100111110010110110011",
    "0101000111000100111101111100100000110110010100111110010110110100",
    "0101000111000100111101111100100000110110010100111110010110110101",
    "0101000111000100111101111100100000110110010100111110010110110110",
    "0101000111000100111101111100100000110110010100111110010110110111",
    "0101000111000100111101111100100000110110010100111110010110111000",
    "0101000111000100111101111100100000110110010100111110010110111001",
    "0101000111000100111101111100100000110110010100111110010110111010",
    "0101000111000100111101111100100000110110010100111110010110111011",
    "0101000111000100111101111100100000110110010100111110010110111100",
    "0101000111000100111101111100100000110110010100111110010110111101",
    "0101000111000100111101111100100000110110010100111110010110111110",
    "0101000111000100111101111100100000110110010100111110010110111111",
    "0101000111000100111101111100100000110110010100111110010111000000",
    "0101000111000100111101111100100000110110010100111110010111000001",
    "0101000111000100111101111100100000110110010100111110010111000010",
    "0101000111000100111101111100100000110110010100111110010111000011",
    "0101000111000100111101111100100000110110010100111110010111000100",
    "0101000111000100111101111100100000110110010100111110010111000101",
    "0101000111000100111101111100100000110110010100111110010111000110",
    "0101000111000100111101111100100000110110010100111110010111000111",
    "0101000111000100111101111100100000110110010100111110010111001000",
    "0101000111000100111101111100100000110110010100111110010111001001",
    "0101000111000100111101111100100000110110010100111110010111001010",
    "0101000111000100111101111100100000110110010100111110010111001011",
    "0101000111000100111101111100100000110110010100111110010111001100",
    "0101000111000100111101111100100000110110010100111110010111001101",
    "0101000111000100111101111100100000110110010100111110010111001110",
    "0101000111000100111101111100100000110110010100111110010111001111",
    "0101000111000100111101111100100000110110010100111110010111010000",
    "0101000111000100111101111100100000110110010100111110010111010001",
    "0101000111000100111101111100100000110110010100111110010111010010",
    "0101000111000100111101111100100000110110010100111110010111010011",
    "0101000111000100111101111100100000110110010100111110010111010100",
    "0101000111000100111101111100100000110110010100111110010111010101",
    "0101000111000100111101111100100000110110010100111110010111010110",
    "0101000111000100111101111100100000110110010100111110010111010111",
    "0101000111000100111101111100100000110110010100111110010111011000",
    "0101000111000100111101111100100000110110010100111110010111011001",
    "0101000111000100111101111100100000110110010100111110010111011010",
    "0101000111000100111101111100100000110110010100111110010111011011",
    "0101000111000100111101111100100000110110010100111110010111011100",
    "0101000111000100111101111100100000110110010100111110010111011101",
    "0101000111000100111101111100100000110110010100111110010111011110",
    "0101000111000100111101111100100000110110010100111110010111011111",
    "0101000111000100111101111100100000110110010100111110010111100000",
    "0101000111000100111101111100100000110110010100111110010111100001",
    "0101000111000100111101111100100000110110010100111110010111100010",
    "0101000111000100111101111100100000110110010100111110010111100011",
    "0101000111000100111101111100100000110110010100111110010111100100",
    "0101000111000100111101111100100000110110010100111110010111100101",
    "0101000111000100111101111100100000110110010100111110010111100110",
    "0101000111000100111101111100100000110110010100111110010111100111",
    "0101000111000100111101111100100000110110010100111110010111101000",
    "0101000111000100111101111100100000110110010100111110010111101001",
    "0101000111000100111101111100100000110110010100111110010111101010",
    "0101000111000100111101111100100000110110010100111110010111101011",
    "0101000111000100111101111100100000110110010100111110010111101100",
    "0101000111000100111101111100100000110110010100111110010111101101",
    "0101000111000100111101111100100000110110010100111110010111101110",
    "0101000111000100111101111100100000110110010100111110010111101111",
    "0101000111000100111101111100100000110110010100111110010111110000",
    "0101000111000100111101111100100000110110010100111110010111110001",
    "0101000111000100111101111100100000110110010100111110010111110010",
    "0101000111000100111101111100100000110110010100111110010111110011",
    "0101000111000100111101111100100000110110010100111110010111110100",
    "0101000111000100111101111100100000110110010100111110010111110101",
    "0101000111000100111101111100100000110110010100111110010111110110",
    "0101000111000100111101111100100000110110010100111110010111110111",
    "0101000111000100111101111100100000110110010100111110010111111000",
    "0101000111000100111101111100100000110110010100111110010111111001",
    "0101000111000100111101111100100000110110010100111110010111111010",
    "0101000111000100111101111100100000110110010100111110010111111011",
    "0101000111000100111101111100100000110110010100111110010111111100",
    "0101000111000100111101111100100000110110010100111110010111111101",
    "0101000111000100111101111100100000110110010100111110010111111110",
    "0101000111000100111101111100100000110110010100111110010111111111",
    "01010001110001001111011111001000001101100101001111100110",
    "01010001110001001111011111001000001101100101001111100111",
    "01010001110001001111011111001000001101100101001111101000",
    "01010001110001001111011111001000001101100101001111101001",
    "01010001110001001111011111001000001101100101001111101010",
    "01010001110001001111011111001000001101100101001111101011",
    "01010001110001001111011111001000001101100101001111101100",
    "01010001110001001111011111001000001101100101001111101101",
    "01010001110001001111011111001000001101100101001111101110",
    "01010001110001001111011111001000001101100101001111101111",
    "01010001110001001111011111001000001101100101001111110000",
    "01010001110001001111011111001000001101100101001111110001",
    "01010001110001001111011111001000001101100101001111110010",
    "01010001110001001111011111001000001101100101001111110011",
    "01010001110001001111011111001000001101100101001111110100",
    "01010001110001001111011111001000001101100101001111110101",
    "01010001110001001111011111001000001101100101001111110110",
    "01010001110001001111011111001000001101100101001111110111",
    "01010001110001001111011111001000001101100101001111111000",
    "01010001110001001111011111001000001101100101001111111001",
    "01010001110001001111011111001000001101100101001111111010",
    "01010001110001001111011111001000001101100101001111111011",
    "01010001110001001111011111001000001101100101001111111100",
    "01010001110001001111011111001000001101100101001111111101",
    "01010001110001001111011111001000001101100101001111111110",
    "01010001110001001111011111001000001101100101001111111111",
    "010100011100010011110111110010000011011001010100",
    "010100011100010011110111110010000011011001010101",
    "010100011100010011110111110010000011011001010110",
    "010100011100010011110111110010000011011001010111",
    "010100011100010011110111110010000011011001011000",
    "010100011100010011110111110010000011011001011001",
    "010100011100010011110111110010000011011001011010",
    "010100011100010011110111110010000011011001011011",
    "010100011100010011110111110010000011011001011100",
    "010100011100010011110111110010000011011001011101",
    "010100011100010011110111110010000011011001011110",
    "010100011100010011110111110010000011011001011111",
    "010100011100010011110111110010000011011001100000",
    "010100011100010011110111110010000011011001100001",
    "010100011100010011110111110010000011011001100010",
    "010100011100010011110111110010000011011001100011",
    "010100011100010011110111110010000011011001100100",
    "010100011100010011110111110010000011011001100101",
    "010100011100010011110111110010000011011001100110",
    "010100011100010011110111110010000011011001100111",
    "010100011100010011110111110010000011011001101000",
    "010100011100010011110111110010000011011001101001",
    "010100011100010011110111110010000011011001101010",
    "010100011100010011110111110010000011011001101011",
    "010100011100010011110111110010000011011001101100",
    "010100011100010011110111110010000011011001101101",
    "010100011100010011110111110010000011011001101110",
    "010100011100010011110111110010000011011001101111",
    "010100011100010011110111110010000011011001110000",
    "010100011100010011110111110010000011011001110001",
    "010100011100010011110111110010000011011001110010",
    "010100011100010011110111110010000011011001110011",
    "010100011100010011110111110010000011011001110100",
    "010100011100010011110111110010000011011001110101",
    "010100011100010011110111110010000011011001110110",
    "010100011100010011110111110010000011011001110111",
    "010100011100010011110111110010000011011001111000",
    "010100011100010011110111110010000011011001111001",
    "010100011100010011110111110010000011011001111010",
    "010100011100010011110111110010000011011001111011",
    "010100011100010011110111110010000011011001111100",
    "010100011100010011110111110010000011011001111101",
    "010100011100010011110111110010000011011001111110",
    "010100011100010011110111110010000011011001111111",
    "010100011100010011110111110010000011011010000000",
    "010100011100010011110111110010000011011010000001",
    "010100011100010011110111110010000011011010000010",
    "010100011100010011110111110010000011011010000011",
    "010100011100010011110111110010000011011010000100",
    "010100011100010011110111110010000011011010000101",
    "010100011100010011110111110010000011011010000110",
    "010100011100010011110111110010000011011010000111",
    "010100011100010011110111110010000011011010001000",
    "010100011100010011110111110010000011011010001001",
    "010100011100010011110111110010000011011010001010",
    "010100011100010011110111110010000011011010001011",
    "010100011100010011110111110010000011011010001100",
    "010100011100010011110111110010000011011010001101",
    "010100011100010011110111110010000011011010001110",
    "010100011100010011110111110010000011011010001111",
    "010100011100010011110111110010000011011010010000",
    "010100011100010011110111110010000011011010010001",
    "010100011100010011110111110010000011011010010010",
    "010100011100010011110111110010000011011010010011",
    "010100011100010011110111110010000011011010010100",
    "010100011100010011110111110010000011011010010101",
    "010100011100010011110111110010000011011010010110",
    "010100011100010011110111110010000011011010010111",
    "010100011100010011110111110010000011011010011000",
    "010100011100010011110111110010000011011010011001",
    "010100011100010011110111110010000011011010011010",
    "010100011100010011110111110010000011011010011011",
    "010100011100010011110111110010000011011010011100",
    "010100011100010011110111110010000011011010011101",
    "010100011100010011110111110010000011011010011110",
    "010100011100010011110111110010000011011010011111",
    "010100011100010011110111110010000011011010100000",
    "010100011100010011110111110010000011011010100001",
    "010100011100010011110111110010000011011010100010",
    "010100011100010011110111110010000011011010100011",
    "010100011100010011110111110010000011011010100100",
    "010100011100010011110111110010000011011010100101",
    "010100011100010011110111110010000011011010100110",
    "010100011100010011110111110010000011011010100111",
    "010100011100010011110111110010000011011010101000",
    "010100011100010011110111110010000011011010101001",
    "010100011100010011110111110010000011011010101010",
    "010100011100010011110111110010000011011010101011",
    "010100011100010011110111110010000011011010101100",
    "010100011100010011110111110010000011011010101101",
    "010100011100010011110111110010000011011010101110",
    "010100011100010011110111110010000011011010101111",
    "010100011100010011110111110010000011011010110000",
    "010100011100010011110111110010000011011010110001",
    "010100011100010011110111110010000011011010110010",
    "010100011100010011110111110010000011011010110011",
    "010100011100010011110111110010000011011010110100",
    "010100011100010011110111110010000011011010110101",
    "010100011100010011110111110010000011011010110110",
    "010100011100010011110111110010000011011010110111",
    "010100011100010011110111110010000011011010111000",
    "010100011100010011110111110010000011011010111001",
    "010100011100010011110111110010000011011010111010",
    "010100011100010011110111110010000011011010111011",
    "010100011100010011110111110010000011011010111100",
    "010100011100010011110111110010000011011010111101",
    "010100011100010011110111110010000011011010111110",
    "010100011100010011110111110010000011011010111111",
    "010100011100010011110111110010000011011011000000",
    "010100011100010011110111110010000011011011000001",
    "010100011100010011110111110010000011011011000010",
    "010100011100010011110111110010000011011011000011",
    "010100011100010011110111110010000011011011000100",
    "010100011100010011110111110010000011011011000101",
    "010100011100010011110111110010000011011011000110",
    "010100011100010011110111110010000011011011000111",
    "010100011100010011110111110010000011011011001000",
    "010100011100010011110111110010000011011011001001",
    "010100011100010011110111110010000011011011001010",
    "010100011100010011110111110010000011011011001011",
    "010100011100010011110111110010000011011011001100",
    "010100011100010011110111110010000011011011001101",
    "010100011100010011110111110010000011011011001110",
    "010100011100010011110111110010000011011011001111",
    "010100011100010011110111110010000011011011010000",
    "010100011100010011110111110010000011011011010001",
    "010100011100010011110111110010000011011011010010",
    "010100011100010011110111110010000011011011010011",
    "010100011100010011110111110010000011011011010100",
    "010100011100010011110111110010000011011011010101",
    "010100011100010011110111110010000011011011010110",
    "010100011100010011110111110010000011011011010111",
    "010100011100010011110111110010000011011011011000",
    "010100011100010011110111110010000011011011011001",
    "010100011100010011110111110010000011011011011010",
    "010100011100010011110111110010000011011011011011",
    "010100011100010011110111110010000011011011011100",
    "010100011100010011110111110010000011011011011101",
    "010100011100010011110111110010000011011011011110",
    "010100011100010011110111110010000011011011011111",
    "010100011100010011110111110010000011011011100000",
    "010100011100010011110111110010000011011011100001",
    "010100011100010011110111110010000011011011100010",
    "010100011100010011110111110010000011011011100011",
    "010100011100010011110111110010000011011011100100",
    "010100011100010011110111110010000011011011100101",
    "010100011100010011110111110010000011011011100110",
    "010100011100010011110111110010000011011011100111",
    "010100011100010011110111110010000011011011101000",
    "010100011100010011110111110010000011011011101001",
    "010100011100010011110111110010000011011011101010",
    "010100011100010011110111110010000011011011101011",
    "010100011100010011110111110010000011011011101100",
    "010100011100010011110111110010000011011011101101",
    "010100011100010011110111110010000011011011101110",
    "010100011100010011110111110010000011011011101111",
    "010100011100010011110111110010000011011011110000",
    "010100011100010011110111110010000011011011110001",
    "010100011100010011110111110010000011011011110010",
    "010100011100010011110111110010000011011011110011",
    "010100011100010011110111110010000011011011110100",
    "010100011100010011110111110010000011011011110101",
    "010100011100010011110111110010000011011011110110",
    "010100011100010011110111110010000011011011110111",
    "010100011100010011110111110010000011011011111000",
    "010100011100010011110111110010000011011011111001",
    "010100011100010011110111110010000011011011111010",
    "010100011100010011110111110010000011011011111011",
    "010100011100010011110111110010000011011011111100",
    "010100011100010011110111110010000011011011111101",
    "010100011100010011110111110010000011011011111110",
    "010100011100010011110111110010000011011011111111",
    "0101000111000100111101111100100000110111",
    "0101000111000100111101111100100000111000",
    "0101000111000100111101111100100000111001",
    "0101000111000100111101111100100000111010",
    "0101000111000100111101111100100000111011",
    "0101000111000100111101111100100000111100",
    "0101000111000100111101111100100000111101",
    "0101000111000100111101111100100000111110",
    "0101000111000100111101111100100000111111",
    "0101000111000100111101111100100001000000",
    "0101000111000100111101111100100001000001",
    "0101000111000100111101111100100001000010",
    "0101000111000100111101111100100001000011",
    "0101000111000100111101111100100001000100",
    "0101000111000100111101111100100001000101",
    "0101000111000100111101111100100001000110",
    "0101000111000100111101111100100001000111",
    "0101000111000100111101111100100001001000",
    "0101000111000100111101111100100001001001",
    "0101000111000100111101111100100001001010",
    "0101000111000100111101111100100001001011",
    "0101000111000100111101111100100001001100",
    "0101000111000100111101111100100001001101",
    "0101000111000100111101111100100001001110",
    "0101000111000100111101111100100001001111",
    "0101000111000100111101111100100001010000",
    "0101000111000100111101111100100001010001",
    "0101000111000100111101111100100001010010",
    "0101000111000100111101111100100001010011",
    "0101000111000100111101111100100001010100",
    "0101000111000100111101111100100001010101",
    "0101000111000100111101111100100001010110",
    "0101000111000100111101111100100001010111",
    "0101000111000100111101111100100001011000",
    "0101000111000100111101111100100001011001",
    "0101000111000100111101111100100001011010",
    "0101000111000100111101111100100001011011",
    "0101000111000100111101111100100001011100",
    "0101000111000100111101111100100001011101",
    "0101000111000100111101111100100001011110",
    "0101000111000100111101111100100001011111",
    "0101000111000100111101111100100001100000",
    "0101000111000100111101111100100001100001",
    "0101000111000100111101111100100001100010",
    "0101000111000100111101111100100001100011",
    "0101000111000100111101111100100001100100",
    "0101000111000100111101111100100001100101",
    "0101000111000100111101111100100001100110",
    "0101000111000100111101111100100001100111",
    "0101000111000100111101111100100001101000",
    "0101000111000100111101111100100001101001",
    "0101000111000100111101111100100001101010",
    "0101000111000100111101111100100001101011",
    "0101000111000100111101111100100001101100",
    "0101000111000100111101111100100001101101",
    "0101000111000100111101111100100001101110",
    "0101000111000100111101111100100001101111",
    "0101000111000100111101111100100001110000",
    "0101000111000100111101111100100001110001",
    "0101000111000100111101111100100001110010",
    "0101000111000100111101111100100001110011",
    "0101000111000100111101111100100001110100",
    "0101000111000100111101111100100001110101",
    "0101000111000100111101111100100001110110",
    "0101000111000100111101111100100001110111",
    "0101000111000100111101111100100001111000",
    "0101000111000100111101111100100001111001",
    "0101000111000100111101111100100001111010",
    "0101000111000100111101111100100001111011",
    "0101000111000100111101111100100001111100",
    "0101000111000100111101111100100001111101",
    "0101000111000100111101111100100001111110",
    "0101000111000100111101111100100001111111",
    "0101000111000100111101111100100010000000",
    "0101000111000100111101111100100010000001",
    "0101000111000100111101111100100010000010",
    "0101000111000100111101111100100010000011",
    "0101000111000100111101111100100010000100",
    "0101000111000100111101111100100010000101",
    "0101000111000100111101111100100010000110",
    "0101000111000100111101111100100010000111",
    "0101000111000100111101111100100010001000",
    "0101000111000100111101111100100010001001",
    "0101000111000100111101111100100010001010",
    "0101000111000100111101111100100010001011",
    "0101000111000100111101111100100010001100",
    "0101000111000100111101111100100010001101",
    "0101000111000100111101111100100010001110",
    "0101000111000100111101111100100010001111",
    "0101000111000100111101111100100010010000",
    "0101000111000100111101111100100010010001",
    "0101000111000100111101111100100010010010",
    "0101000111000100111101111100100010010011",
    "0101000111000100111101111100100010010100",
    "0101000111000100111101111100100010010101",
    "0101000111000100111101111100100010010110",
    "0101000111000100111101111100100010010111",
    "0101000111000100111101111100100010011000",
    "0101000111000100111101111100100010011001",
    "0101000111000100111101111100100010011010",
    "0101000111000100111101111100100010011011",
    "0101000111000100111101111100100010011100",
    "0101000111000100111101111100100010011101",
    "0101000111000100111101111100100010011110",
    "0101000111000100111101111100100010011111",
    "0101000111000100111101111100100010100000",
    "0101000111000100111101111100100010100001",
    "0101000111000100111101111100100010100010",
    "0101000111000100111101111100100010100011",
    "0101000111000100111101111100100010100100",
    "0101000111000100111101111100100010100101",
    "0101000111000100111101111100100010100110",
    "0101000111000100111101111100100010100111",
    "0101000111000100111101111100100010101000",
    "0101000111000100111101111100100010101001",
    "0101000111000100111101111100100010101010",
    "0101000111000100111101111100100010101011",
    "0101000111000100111101111100100010101100",
    "0101000111000100111101111100100010101101",
    "0101000111000100111101111100100010101110",
    "0101000111000100111101111100100010101111",
    "0101000111000100111101111100100010110000",
    "0101000111000100111101111100100010110001",
    "0101000111000100111101111100100010110010",
    "0101000111000100111101111100100010110011",
    "0101000111000100111101111100100010110100",
    "0101000111000100111101111100100010110101",
    "0101000111000100111101111100100010110110",
    "0101000111000100111101111100100010110111",
    "0101000111000100111101111100100010111000",
    "0101000111000100111101111100100010111001",
    "0101000111000100111101111100100010111010",
    "0101000111000100111101111100100010111011",
    "0101000111000100111101111100100010111100",
    "0101000111000100111101111100100010111101",
    "0101000111000100111101111100100010111110",
    "0101000111000100111101111100100010111111",
    "0101000111000100111101111100100011000000",
    "0101000111000100111101111100100011000001",
    "0101000111000100111101111100100011000010",
    "0101000111000100111101111100100011000011",
    "0101000111000100111101111100100011000100",
    "0101000111000100111101111100100011000101",
    "0101000111000100111101111100100011000110",
    "0101000111000100111101111100100011000111",
    "0101000111000100111101111100100011001000",
    "0101000111000100111101111100100011001001",
    "0101000111000100111101111100100011001010",
    "0101000111000100111101111100100011001011",
    "0101000111000100111101111100100011001100",
    "0101000111000100111101111100100011001101",
    "0101000111000100111101111100100011001110",
    "0101000111000100111101111100100011001111",
    "0101000111000100111101111100100011010000",
    "0101000111000100111101111100100011010001",
    "0101000111000100111101111100100011010010",
    "0101000111000100111101111100100011010011",
    "0101000111000100111101111100100011010100",
    "0101000111000100111101111100100011010101",
    "0101000111000100111101111100100011010110",
    "0101000111000100111101111100100011010111",
    "0101000111000100111101111100100011011000",
    "0101000111000100111101111100100011011001",
    "0101000111000100111101111100100011011010",
    "0101000111000100111101111100100011011011",
    "0101000111000100111101111100100011011100",
    "0101000111000100111101111100100011011101",
    "0101000111000100111101111100100011011110",
    "0101000111000100111101111100100011011111",
    "0101000111000100111101111100100011100000",
    "0101000111000100111101111100100011100001",
    "0101000111000100111101111100100011100010",
    "0101000111000100111101111100100011100011",
    "0101000111000100111101111100100011100100",
    "0101000111000100111101111100100011100101",
    "0101000111000100111101111100100011100110",
    "0101000111000100111101111100100011100111",
    "0101000111000100111101111100100011101000",
    "0101000111000100111101111100100011101001",
    "0101000111000100111101111100100011101010",
    "0101000111000100111101111100100011101011",
    "0101000111000100111101111100100011101100",
    "0101000111000100111101111100100011101101",
    "0101000111000100111101111100100011101110",
    "0101000111000100111101111100100011101111",
    "0101000111000100111101111100100011110000",
    "0101000111000100111101111100100011110001",
    "0101000111000100111101111100100011110010",
    "0101000111000100111101111100100011110011",
    "0101000111000100111101111100100011110100",
    "0101000111000100111101111100100011110101",
    "0101000111000100111101111100100011110110",
    "0101000111000100111101111100100011110111",
    "0101000111000100111101111100100011111000",
    "0101000111000100111101111100100011111001",
    "0101000111000100111101111100100011111010",
    "0101000111000100111101111100100011111011",
    "0101000111000100111101111100100011111100",
    "0101000111000100111101111100100011111101",
    "0101000111000100111101111100100011111110",
    "0101000111000100111101111100100011111111",
    "01010001110001001111011111001001",
    "01010001110001001111011111001010",
    "01010001110001001111011111001011",
    "01010001110001001111011111001100",
    "01010001110001001111011111001101",
    "01010001110001001111011111001110",
    "01010001110001001111011111001111",
    "01010001110001001111011111010000",
    "01010001110001001111011111010001",
    "01010001110001001111011111010010",
    "01010001110001001111011111010011",
    "01010001110001001111011111010100",
    "01010001110001001111011111010101",
    "01010001110001001111011111010110",
    "01010001110001001111011111010111",
    "01010001110001001111011111011000",
    "01010001110001001111011111011001",
    "01010001110001001111011111011010",
    "01010001110001001111011111011011",
    "01010001110001001111011111011100",
    "01010001110001001111011111011101",
    "01010001110001001111011111011110",
    "01010001110001001111011111011111",
    "01010001110001001111011111100000",
    "01010001110001001111011111100001",
    "01010001110001001111011111100010",
    "01010001110001001111011111100011",
    "01010001110001001111011111100100",
    "01010001110001001111011111100101",
    "01010001110001001111011111100110",
    "01010001110001001111011111100111",
    "01010001110001001111011111101000",
    "01010001110001001111011111101001",
    "01010001110001001111011111101010",
    "01010001110001001111011111101011",
    "01010001110001001111011111101100",
    "01010001110001001111011111101101",
    "01010001110001001111011111101110",
    "01010001110001001111011111101111",
    "01010001110001001111011111110000",
    "01010001110001001111011111110001",
    "01010001110001001111011111110010",
    "01010001110001001111011111110011",
    "01010001110001001111011111110100",
    "01010001110001001111011111110101",
    "01010001110001001111011111110110",
    "01010001110001001111011111110111",
    "01010001110001001111011111111000",
    "01010001110001001111011111111001",
    "01010001110001001111011111111010",
    "01010001110001001111011111111011",
    "01010001110001001111011111111100",
    "01010001110001001111011111111101",
    "01010001110001001111011111111110",
    "01010001110001001111011111111111",
    "010100011100010011111000",
    "010100011100010011111001",
    "010100011100010011111010",
    "010100011100010011111011",
    "010100011100010011111100",
    "010100011100010011111101",
    "010100011100010011111110",
    "010100011100010011111111",
    "0101000111000101",
    "0101000111000110",
    "0101000111000111",
    "0101000111001000",
    "0101000111001001",
    "0101000111001010",
    "0101000111001011",
    "0101000111001100",
    "0101000111001101",
    "0101000111001110",
    "0101000111001111",
    "0101000111010000",
    "0101000111010001",
    "0101000111010010",
    "0101000111010011",
    "0101000111010100",
    "0101000111010101",
    "0101000111010110",
    "0101000111010111",
    "0101000111011000",
    "0101000111011001",
    "0101000111011010",
    "0101000111011011",
    "0101000111011100",
    "0101000111011101",
    "0101000111011110",
    "0101000111011111",
    "0101000111100000",
    "0101000111100001",
    "0101000111100010",
    "0101000111100011",
    "0101000111100100",
    "0101000111100101",
    "0101000111100110",
    "0101000111100111",
    "0101000111101000",
    "0101000111101001",
    "0101000111101010",
    "0101000111101011",
    "0101000111101100",
    "0101000111101101",
    "0101000111101110",
    "0101000111101111",
    "0101000111110000",
    "0101000111110001",
    "0101000111110010",
    "0101000111110011",
    "0101000111110100",
    "0101000111110101",
    "0101000111110110",
    "0101000111110111",
    "0101000111111000",
    "0101000111111001",
    "0101000111111010",
    "0101000111111011",
    "0101000111111100",
    "0101000111111101",
    "0101000111111110",
    "0101000111111111",
    "01010010",
    "01010011",
    "01010100",
    "01010101",
    "01010110",
    "01010111",
    "01011000",
    "01011001",
    "01011010",
    "01011011",
    "01011100",
    "01011101",
    "01011110",
    "01011111",
    "01100000",
    "01100001",
    "01100010",
    "01100011",
    "01100100",
    "01100101",
    "01100110",
    "01100111",
    "01101000",
    "01101001",
    "01101010",
    "01101011",
    "01101100",
    "01101101",
    "01101110",
    "01101111",
    "01110000",
    "01110001",
    "01110010",
    "01110011",
    "01110100",
    "01110101",
    "01110110",
    "01110111",
    "01111000",
    "01111001",
    "01111010",
    "01111011",
    "01111100",
    "01111101",
    "01111110",
    "01111111",
    "10000000",
    "10000001",
    "10000010",
    "10000011",
    "10000100",
    "10000101",
    "10000110",
    "10000111",
    "10001000",
    "10001001",
    "10001010",
    "10001011",
    "10001100",
    "10001101",
    "10001110",
    "10001111",
    "10010000",
    "10010001",
    "10010010",
    "10010011",
    "10010100",
    "10010101",
    "10010110",
    "10010111",
    "10011000",
    "10011001",
    "10011010",
    "10011011",
    "10011100",
    "10011101",
    "10011110",
    "10011111",
    "10100000",
    "10100001",
    "10100010",
    "10100011",
    "10100100",
    "10100101",
    "10100110",
    "10100111",
    "10101000",
    "10101001",
    "10101010",
    "10101011",
    "10101100",
    "10101101",
    "1010111000000000",
    "1010111000000001",
    "1010111000000010",
    "1010111000000011",
    "1010111000000100",
    "1010111000000101",
    "1010111000000110",
    "1010111000000111",
    "1010111000001000",
    "1010111000001001",
    "1010111000001010",
    "1010111000001011",
    "1010111000001100",
    "1010111000001101",
    "1010111000001110",
    "1010111000001111",
    "1010111000010000",
    "1010111000010001",
    "1010111000010010",
    "1010111000010011",
    "1010111000010100",
    "1010111000010101",
    "1010111000010110",
    "1010111000010111",
    "1010111000011000",
    "1010111000011001",
    "1010111000011010",
    "1010111000011011",
    "1010111000011100",
    "1010111000011101",
    "1010111000011110",
    "1010111000011111",
    "1010111000100000",
    "1010111000100001",
    "1010111000100010",
    "1010111000100011",
    "1010111000100100",
    "1010111000100101",
    "1010111000100110",
    "1010111000100111",
    "1010111000101000",
    "1010111000101001",
    "1010111000101010",
    "1010111000101011",
    "1010111000101100",
    "1010111000101101",
    "1010111000101110",
    "1010111000101111",
    "1010111000110000",
    "1010111000110001",
    "1010111000110010",
    "1010111000110011",
    "1010111000110100",
    "1010111000110101",
    "1010111000110110",
    "1010111000110111",
    "1010111000111000",
    "1010111000111001",
    "1010111000111010",
    "1010111000111011",
    "1010111000111100",
    "1010111000111101",
    "1010111000111110",
    "1010111000111111",
    "1010111001000000",
    "1010111001000001",
    "1010111001000010",
    "1010111001000011",
    "1010111001000100",
    "1010111001000101",
    "101011100100011000000000",
    "101011100100011000000001",
    "101011100100011000000010",
    "101011100100011000000011",
    "101011100100011000000100",
    "101011100100011000000101",
    "101011100100011000000110",
    "101011100100011000000111",
    "101011100100011000001000",
    "101011100100011000001001",
    "101011100100011000001010",
    "101011100100011000001011",
    "101011100100011000001100",
    "101011100100011000001101",
    "101011100100011000001110",
    "101011100100011000001111",
    "101011100100011000010000",
    "101011100100011000010001",
    "101011100100011000010010",
    "101011100100011000010011",
    "101011100100011000010100",
    "101011100100011000010101",
    "101011100100011000010110",
    "101011100100011000010111",
    "101011100100011000011000",
    "101011100100011000011001",
    "101011100100011000011010",
    "101011100100011000011011",
    "101011100100011000011100",
    "101011100100011000011101",
    "101011100100011000011110",
    "101011100100011000011111",
    "101011100100011000100000",
    "101011100100011000100001",
    "101011100100011000100010",
    "101011100100011000100011",
    "101011100100011000100100",
    "101011100100011000100101",
    "101011100100011000100110",
    "101011100100011000100111",
    "101011100100011000101000",
    "101011100100011000101001",
    "101011100100011000101010",
    "101011100100011000101011",
    "101011100100011000101100",
    "101011100100011000101101",
    "101011100100011000101110",
    "101011100100011000101111",
    "101011100100011000110000",
    "101011100100011000110001",
    "101011100100011000110010",
    "101011100100011000110011",
    "101011100100011000110100",
    "101011100100011000110101",
    "101011100100011000110110",
    "101011100100011000110111",
    "101011100100011000111000",
    "101011100100011000111001",
    "101011100100011000111010",
    "101011100100011000111011",
    "101011100100011000111100",
    "101011100100011000111101",
    "101011100100011000111110",
    "101011100100011000111111",
    "101011100100011001000000",
    "101011100100011001000001",
    "101011100100011001000010",
    "101011100100011001000011",
    "101011100100011001000100",
    "101011100100011001000101",
    "101011100100011001000110",
    "101011100100011001000111",
    "101011100100011001001000",
    "101011100100011001001001",
    "101011100100011001001010",
    "101011100100011001001011",
    "101011100100011001001100",
    "101011100100011001001101",
    "101011100100011001001110",
    "101011100100011001001111",
    "101011100100011001010000",
    "101011100100011001010001",
    "101011100100011001010010",
    "101011100100011001010011",
    "101011100100011001010100",
    "101011100100011001010101",
    "101011100100011001010110",
    "101011100100011001010111",
    "101011100100011001011000",
    "101011100100011001011001",
    "101011100100011001011010",
    "101011100100011001011011",
    "101011100100011001011100",
    "101011100100011001011101",
    "101011100100011001011110",
    "101011100100011001011111",
    "101011100100011001100000",
    "101011100100011001100001",
    "101011100100011001100010",
    "101011100100011001100011",
    "101011100100011001100100",
    "101011100100011001100101",
    "101011100100011001100110",
    "101011100100011001100111",
    "101011100100011001101000",
    "101011100100011001101001",
    "101011100100011001101010",
    "101011100100011001101011",
    "101011100100011001101100",
    "101011100100011001101101",
    "101011100100011001101110",
    "101011100100011001101111",
    "101011100100011001110000",
    "101011100100011001110001",
    "101011100100011001110010",
    "101011100100011001110011",
    "101011100100011001110100",
    "101011100100011001110101",
    "101011100100011001110110",
    "101011100100011001110111",
    "101011100100011001111000",
    "101011100100011001111001",
    "101011100100011001111010",
    "101011100100011001111011",
    "101011100100011001111100",
    "101011100100011001111101",
    "101011100100011001111110",
    "101011100100011001111111",
    "101011100100011010000000",
    "101011100100011010000001",
    "101011100100011010000010",
    "101011100100011010000011",
    "101011100100011010000100",
    "101011100100011010000101",
    "101011100100011010000110",
    "101011100100011010000111",
    "101011100100011010001000",
    "101011100100011010001001",
    "101011100100011010001010",
    "101011100100011010001011",
    "101011100100011010001100",
    "101011100100011010001101",
    "101011100100011010001110",
    "101011100100011010001111",
    "101011100100011010010000",
    "101011100100011010010001",
    "101011100100011010010010",
    "101011100100011010010011",
    "101011100100011010010100",
    "101011100100011010010101",
    "101011100100011010010110",
    "101011100100011010010111",
    "101011100100011010011000",
    "101011100100011010011001",
    "101011100100011010011010",
    "101011100100011010011011",
    "101011100100011010011100",
    "101011100100011010011101",
    "101011100100011010011110",
    "101011100100011010011111",
    "101011100100011010100000",
    "101011100100011010100001",
    "10101110010001101010001000000000",
    "10101110010001101010001000000001",
    "1010111001000110101000100000001000000000",
    "1010111001000110101000100000001000000001",
    "1010111001000110101000100000001000000010",
    "1010111001000110101000100000001000000011",
    "1010111001000110101000100000001000000100",
    "1010111001000110101000100000001000000101",
    "1010111001000110101000100000001000000110",
    "1010111001000110101000100000001000000111",
    "1010111001000110101000100000001000001000",
    "1010111001000110101000100000001000001001",
    "1010111001000110101000100000001000001010",
    "1010111001000110101000100000001000001011",
    "1010111001000110101000100000001000001100",
    "1010111001000110101000100000001000001101",
    "1010111001000110101000100000001000001110",
    "1010111001000110101000100000001000001111",
    "1010111001000110101000100000001000010000",
    "1010111001000110101000100000001000010001",
    "1010111001000110101000100000001000010010",
    "1010111001000110101000100000001000010011",
    "1010111001000110101000100000001000010100",
    "1010111001000110101000100000001000010101",
    "1010111001000110101000100000001000010110",
    "1010111001000110101000100000001000010111",
    "1010111001000110101000100000001000011000",
    "1010111001000110101000100000001000011001",
    "1010111001000110101000100000001000011010",
    "1010111001000110101000100000001000011011",
    "1010111001000110101000100000001000011100",
    "1010111001000110101000100000001000011101",
    "1010111001000110101000100000001000011110",
    "1010111001000110101000100000001000011111",
    "1010111001000110101000100000001000100000",
    "1010111001000110101000100000001000100001",
    "1010111001000110101000100000001000100010",
    "1010111001000110101000100000001000100011",
    "1010111001000110101000100000001000100100",
    "1010111001000110101000100000001000100101",
    "1010111001000110101000100000001000100110",
    "1010111001000110101000100000001000100111",
    "1010111001000110101000100000001000101000",
    "1010111001000110101000100000001000101001",
    "1010111001000110101000100000001000101010",
    "1010111001000110101000100000001000101011",
    "1010111001000110101000100000001000101100",
    "1010111001000110101000100000001000101101",
    "1010111001000110101000100000001000101110",
    "1010111001000110101000100000001000101111",
    "1010111001000110101000100000001000110000",
    "1010111001000110101000100000001000110001",
    "1010111001000110101000100000001000110010",
    "1010111001000110101000100000001000110011",
    "1010111001000110101000100000001000110100",
    "1010111001000110101000100000001000110101",
    "1010111001000110101000100000001000110110",
    "1010111001000110101000100000001000110111",
    "1010111001000110101000100000001000111000",
    "1010111001000110101000100000001000111001",
    "1010111001000110101000100000001000111010",
    "1010111001000110101000100000001000111011",
    "1010111001000110101000100000001000111100",
    "1010111001000110101000100000001000111101",
    "1010111001000110101000100000001000111110",
    "1010111001000110101000100000001000111111",
    "1010111001000110101000100000001001000000",
    "1010111001000110101000100000001001000001",
    "1010111001000110101000100000001001000010",
    "1010111001000110101000100000001001000011",
    "1010111001000110101000100000001001000100",
    "1010111001000110101000100000001001000101",
    "1010111001000110101000100000001001000110",
    "1010111001000110101000100000001001000111",
    "1010111001000110101000100000001001001000",
    "1010111001000110101000100000001001001001",
    "1010111001000110101000100000001001001010",
    "1010111001000110101000100000001001001011",
    "1010111001000110101000100000001001001100",
    "1010111001000110101000100000001001001101",
    "1010111001000110101000100000001001001110",
    "1010111001000110101000100000001001001111",
    "1010111001000110101000100000001001010000",
    "1010111001000110101000100000001001010001",
    "1010111001000110101000100000001001010010",
    "1010111001000110101000100000001001010011",
    "1010111001000110101000100000001001010100",
    "1010111001000110101000100000001001010101",
    "1010111001000110101000100000001001010110",
    "1010111001000110101000100000001001010111",
    "1010111001000110101000100000001001011000",
    "1010111001000110101000100000001001011001",
    "1010111001000110101000100000001001011010",
    "1010111001000110101000100000001001011011",
    "1010111001000110101000100000001001011100",
    "1010111001000110101000100000001001011101",
    "1010111001000110101000100000001001011110",
    "1010111001000110101000100000001001011111",
    "1010111001000110101000100000001001100000",
    "1010111001000110101000100000001001100001",
    "1010111001000110101000100000001001100010",
    "1010111001000110101000100000001001100011",
    "1010111001000110101000100000001001100100",
    "1010111001000110101000100000001001100101",
    "1010111001000110101000100000001001100110",
    "1010111001000110101000100000001001100111",
    "1010111001000110101000100000001001101000",
    "1010111001000110101000100000001001101001",
    "1010111001000110101000100000001001101010",
    "1010111001000110101000100000001001101011",
    "1010111001000110101000100000001001101100",
    "1010111001000110101000100000001001101101",
    "1010111001000110101000100000001001101110",
    "1010111001000110101000100000001001101111",
    "1010111001000110101000100000001001110000",
    "1010111001000110101000100000001001110001",
    "1010111001000110101000100000001001110010",
    "1010111001000110101000100000001001110011",
    "1010111001000110101000100000001001110100",
    "1010111001000110101000100000001001110101",
    "1010111001000110101000100000001001110110",
    "1010111001000110101000100000001001110111",
    "1010111001000110101000100000001001111000",
    "1010111001000110101000100000001001111001",
    "1010111001000110101000100000001001111010",
    "1010111001000110101000100000001001111011",
    "1010111001000110101000100000001001111100",
    "1010111001000110101000100000001001111101",
    "1010111001000110101000100000001001111110",
    "1010111001000110101000100000001001111111",
    "1010111001000110101000100000001010000000",
    "1010111001000110101000100000001010000001",
    "1010111001000110101000100000001010000010",
    "1010111001000110101000100000001010000011",
    "1010111001000110101000100000001010000100",
    "1010111001000110101000100000001010000101",
    "1010111001000110101000100000001010000110",
    "1010111001000110101000100000001010000111",
    "1010111001000110101000100000001010001000",
    "1010111001000110101000100000001010001001",
    "1010111001000110101000100000001010001010",
    "1010111001000110101000100000001010001011",
    "1010111001000110101000100000001010001100",
    "1010111001000110101000100000001010001101",
    "1010111001000110101000100000001010001110",
    "1010111001000110101000100000001010001111",
    "1010111001000110101000100000001010010000",
    "1010111001000110101000100000001010010001",
    "1010111001000110101000100000001010010010",
    "1010111001000110101000100000001010010011",
    "1010111001000110101000100000001010010100",
    "1010111001000110101000100000001010010101",
    "1010111001000110101000100000001010010110",
    "1010111001000110101000100000001010010111",
    "1010111001000110101000100000001010011000",
    "1010111001000110101000100000001010011001",
    "1010111001000110101000100000001010011010",
    "1010111001000110101000100000001010011011",
    "1010111001000110101000100000001010011100",
    "1010111001000110101000100000001010011101",
    "1010111001000110101000100000001010011110",
    "1010111001000110101000100000001010011111",
    "1010111001000110101000100000001010100000",
    "1010111001000110101000100000001010100001",
    "1010111001000110101000100000001010100010",
    "1010111001000110101000100000001010100011",
    "1010111001000110101000100000001010100100",
    "1010111001000110101000100000001010100101",
    "1010111001000110101000100000001010100110",
    "1010111001000110101000100000001010100111",
    "1010111001000110101000100000001010101000",
    "1010111001000110101000100000001010101001",
    "1010111001000110101000100000001010101010",
    "1010111001000110101000100000001010101011",
    "1010111001000110101000100000001010101100",
    "1010111001000110101000100000001010101101",
    "1010111001000110101000100000001010101110",
    "1010111001000110101000100000001010101111",
    "1010111001000110101000100000001010110000",
    "1010111001000110101000100000001010110001",
    "1010111001000110101000100000001010110010",
    "1010111001000110101000100000001010110011",
    "1010111001000110101000100000001010110100",
    "1010111001000110101000100000001010110101",
    "1010111001000110101000100000001010110110",
    "1010111001000110101000100000001010110111",
    "1010111001000110101000100000001010111000",
    "1010111001000110101000100000001010111001",
    "1010111001000110101000100000001010111010",
    "1010111001000110101000100000001010111011",
    "1010111001000110101000100000001010111100",
    "1010111001000110101000100000001010111101",
    "1010111001000110101000100000001010111110",
    "1010111001000110101000100000001010111111",
    "1010111001000110101000100000001011000000",
    "1010111001000110101000100000001011000001",
    "1010111001000110101000100000001011000010",
    "1010111001000110101000100000001011000011",
    "1010111001000110101000100000001011000100",
    "1010111001000110101000100000001011000101",
    "1010111001000110101000100000001011000110",
    "1010111001000110101000100000001011000111",
    "1010111001000110101000100000001011001000",
    "1010111001000110101000100000001011001001",
    "1010111001000110101000100000001011001010",
    "1010111001000110101000100000001011001011",
    "1010111001000110101000100000001011001100",
    "1010111001000110101000100000001011001101",
    "1010111001000110101000100000001011001110",
    "1010111001000110101000100000001011001111",
    "1010111001000110101000100000001011010000",
    "1010111001000110101000100000001011010001",
    "1010111001000110101000100000001011010010",
    "1010111001000110101000100000001011010011",
    "1010111001000110101000100000001011010100",
    "1010111001000110101000100000001011010101",
    "1010111001000110101000100000001011010110",
    "1010111001000110101000100000001011010111",
    "1010111001000110101000100000001011011000",
    "1010111001000110101000100000001011011001",
    "1010111001000110101000100000001011011010",
    "1010111001000110101000100000001011011011",
    "1010111001000110101000100000001011011100",
    "1010111001000110101000100000001011011101",
    "1010111001000110101000100000001011011110",
    "1010111001000110101000100000001011011111",
    "1010111001000110101000100000001011100000",
    "1010111001000110101000100000001011100001",
    "1010111001000110101000100000001011100010",
    "1010111001000110101000100000001011100011",
    "1010111001000110101000100000001011100100",
    "1010111001000110101000100000001011100101",
    "1010111001000110101000100000001011100110",
    "1010111001000110101000100000001011100111",
    "1010111001000110101000100000001011101000",
    "1010111001000110101000100000001011101001",
    "1010111001000110101000100000001011101010",
    "101011100100011010100010000000101110101100000000",
    "101011100100011010100010000000101110101100000001",
    "101011100100011010100010000000101110101100000010",
    "101011100100011010100010000000101110101100000011",
    "101011100100011010100010000000101110101100000100",
    "101011100100011010100010000000101110101100000101",
    "101011100100011010100010000000101110101100000110",
    "101011100100011010100010000000101110101100000111",
    "101011100100011010100010000000101110101100001000",
    "101011100100011010100010000000101110101100001001",
    "101011100100011010100010000000101110101100001010",
    "101011100100011010100010000000101110101100001011",
    "101011100100011010100010000000101110101100001100",
    "101011100100011010100010000000101110101100001101",
    "101011100100011010100010000000101110101100001110",
    "101011100100011010100010000000101110101100001111",
    "101011100100011010100010000000101110101100010000",
    "101011100100011010100010000000101110101100010001",
    "101011100100011010100010000000101110101100010010",
    "101011100100011010100010000000101110101100010011",
    "101011100100011010100010000000101110101100010100",
    "101011100100011010100010000000101110101100010101",
    "101011100100011010100010000000101110101100010110",
    "101011100100011010100010000000101110101100010111",
    "101011100100011010100010000000101110101100011000",
    "101011100100011010100010000000101110101100011001",
    "101011100100011010100010000000101110101100011010",
    "101011100100011010100010000000101110101100011011",
    "101011100100011010100010000000101110101100011100",
    "101011100100011010100010000000101110101100011101",
    "101011100100011010100010000000101110101100011110",
    "101011100100011010100010000000101110101100011111",
    "101011100100011010100010000000101110101100100000",
    "101011100100011010100010000000101110101100100001",
    "101011100100011010100010000000101110101100100010",
    "101011100100011010100010000000101110101100100011",
    "101011100100011010100010000000101110101100100100",
    "101011100100011010100010000000101110101100100101",
    "101011100100011010100010000000101110101100100110",
    "101011100100011010100010000000101110101100100111",
    "101011100100011010100010000000101110101100101000",
    "101011100100011010100010000000101110101100101001",
    "101011100100011010100010000000101110101100101010",
    "101011100100011010100010000000101110101100101011",
    "101011100100011010100010000000101110101100101100",
    "101011100100011010100010000000101110101100101101",
    "101011100100011010100010000000101110101100101110",
    "101011100100011010100010000000101110101100101111",
    "101011100100011010100010000000101110101100110000",
    "101011100100011010100010000000101110101100110001",
    "101011100100011010100010000000101110101100110010",
    "101011100100011010100010000000101110101100110011",
    "101011100100011010100010000000101110101100110100",
    "101011100100011010100010000000101110101100110101",
    "101011100100011010100010000000101110101100110110",
    "101011100100011010100010000000101110101100110111",
    "101011100100011010100010000000101110101100111000",
    "101011100100011010100010000000101110101100111001",
    "101011100100011010100010000000101110101100111010",
    "101011100100011010100010000000101110101100111011",
    "101011100100011010100010000000101110101100111100",
    "101011100100011010100010000000101110101100111101",
    "101011100100011010100010000000101110101100111110",
    "101011100100011010100010000000101110101100111111",
    "101011100100011010100010000000101110101101000000",
    "101011100100011010100010000000101110101101000001",
    "101011100100011010100010000000101110101101000010",
    "101011100100011010100010000000101110101101000011",
    "101011100100011010100010000000101110101101000100",
    "101011100100011010100010000000101110101101000101",
    "101011100100011010100010000000101110101101000110",
    "101011100100011010100010000000101110101101000111",
    "101011100100011010100010000000101110101101001000",
    "101011100100011010100010000000101110101101001001",
    "101011100100011010100010000000101110101101001010",
    "101011100100011010100010000000101110101101001011",
    "101011100100011010100010000000101110101101001100",
    "101011100100011010100010000000101110101101001101",
    "101011100100011010100010000000101110101101001110",
    "101011100100011010100010000000101110101101001111",
    "101011100100011010100010000000101110101101010000",
    "101011100100011010100010000000101110101101010001",
    "101011100100011010100010000000101110101101010010",
    "101011100100011010100010000000101110101101010011",
    "101011100100011010100010000000101110101101010100",
    "101011100100011010100010000000101110101101010101",
    "101011100100011010100010000000101110101101010110",
    "101011100100011010100010000000101110101101010111",
    "101011100100011010100010000000101110101101011000",
    "101011100100011010100010000000101110101101011001",
    "101011100100011010100010000000101110101101011010",
    "101011100100011010100010000000101110101101011011",
    "101011100100011010100010000000101110101101011100",
    "101011100100011010100010000000101110101101011101",
    "101011100100011010100010000000101110101101011110",
    "101011100100011010100010000000101110101101011111",
    "101011100100011010100010000000101110101101100000",
    "101011100100011010100010000000101110101101100001",
    "101011100100011010100010000000101110101101100010",
    "101011100100011010100010000000101110101101100011",
    "101011100100011010100010000000101110101101100100",
    "101011100100011010100010000000101110101101100101",
    "101011100100011010100010000000101110101101100110",
    "101011100100011010100010000000101110101101100111",
    "101011100100011010100010000000101110101101101000",
    "101011100100011010100010000000101110101101101001",
    "101011100100011010100010000000101110101101101010",
    "101011100100011010100010000000101110101101101011",
    "101011100100011010100010000000101110101101101100",
    "101011100100011010100010000000101110101101101101",
    "101011100100011010100010000000101110101101101110",
    "101011100100011010100010000000101110101101101111",
    "101011100100011010100010000000101110101101110000",
    "101011100100011010100010000000101110101101110001",
    "101011100100011010100010000000101110101101110010",
    "101011100100011010100010000000101110101101110011",
    "101011100100011010100010000000101110101101110100",
    "101011100100011010100010000000101110101101110101",
    "101011100100011010100010000000101110101101110110",
    "101011100100011010100010000000101110101101110111",
    "10101110010001101010001000000010111010110111100000000000",
    "10101110010001101010001000000010111010110111100000000001",
    "10101110010001101010001000000010111010110111100000000010",
    "10101110010001101010001000000010111010110111100000000011",
    "10101110010001101010001000000010111010110111100000000100",
    "10101110010001101010001000000010111010110111100000000101",
    "10101110010001101010001000000010111010110111100000000110",
    "10101110010001101010001000000010111010110111100000000111",
    "10101110010001101010001000000010111010110111100000001000",
    "10101110010001101010001000000010111010110111100000001001",
    "10101110010001101010001000000010111010110111100000001010",
    "10101110010001101010001000000010111010110111100000001011",
    "10101110010001101010001000000010111010110111100000001100",
    "10101110010001101010001000000010111010110111100000001101",
    "10101110010001101010001000000010111010110111100000001110",
    "10101110010001101010001000000010111010110111100000001111",
    "10101110010001101010001000000010111010110111100000010000",
    "10101110010001101010001000000010111010110111100000010001",
    "10101110010001101010001000000010111010110111100000010010",
    "10101110010001101010001000000010111010110111100000010011",
    "10101110010001101010001000000010111010110111100000010100",
    "10101110010001101010001000000010111010110111100000010101",
    "10101110010001101010001000000010111010110111100000010110",
    "10101110010001101010001000000010111010110111100000010111",
    "10101110010001101010001000000010111010110111100000011000",
    "10101110010001101010001000000010111010110111100000011001",
    "10101110010001101010001000000010111010110111100000011010",
    "10101110010001101010001000000010111010110111100000011011",
    "10101110010001101010001000000010111010110111100000011100",
    "10101110010001101010001000000010111010110111100000011101",
    "10101110010001101010001000000010111010110111100000011110",
    "10101110010001101010001000000010111010110111100000011111",
    "10101110010001101010001000000010111010110111100000100000",
    "10101110010001101010001000000010111010110111100000100001",
    "10101110010001101010001000000010111010110111100000100010",
    "10101110010001101010001000000010111010110111100000100011",
    "10101110010001101010001000000010111010110111100000100100",
    "10101110010001101010001000000010111010110111100000100101",
    "10101110010001101010001000000010111010110111100000100110",
    "10101110010001101010001000000010111010110111100000100111",
    "10101110010001101010001000000010111010110111100000101000",
    "10101110010001101010001000000010111010110111100000101001",
    "10101110010001101010001000000010111010110111100000101010",
    "10101110010001101010001000000010111010110111100000101011",
    "10101110010001101010001000000010111010110111100000101100",
    "10101110010001101010001000000010111010110111100000101101",
    "10101110010001101010001000000010111010110111100000101110",
    "10101110010001101010001000000010111010110111100000101111",
    "10101110010001101010001000000010111010110111100000110000",
    "10101110010001101010001000000010111010110111100000110001",
    "10101110010001101010001000000010111010110111100000110010",
    "10101110010001101010001000000010111010110111100000110011",
    "10101110010001101010001000000010111010110111100000110100",
    "10101110010001101010001000000010111010110111100000110101",
    "10101110010001101010001000000010111010110111100000110110",
    "10101110010001101010001000000010111010110111100000110111",
    "1010111001000110101000100000001011101011011110000011100000000000",
    "1010111001000110101000100000001011101011011110000011100000000001",
    "1010111001000110101000100000001011101011011110000011100000000010",
    "1010111001000110101000100000001011101011011110000011100000000011",
    "1010111001000110101000100000001011101011011110000011100000000100",
    "1010111001000110101000100000001011101011011110000011100000000101",
    "1010111001000110101000100000001011101011011110000011100000000110",
    "1010111001000110101000100000001011101011011110000011100000000111",
    "1010111001000110101000100000001011101011011110000011100000001000",
    "1010111001000110101000100000001011101011011110000011100000001001",
    "1010111001000110101000100000001011101011011110000011100000001010",
    "1010111001000110101000100000001011101011011110000011100000001011",
    "1010111001000110101000100000001011101011011110000011100000001100",
    "1010111001000110101000100000001011101011011110000011100000001101",
    "1010111001000110101000100000001011101011011110000011100000001110",
    "1010111001000110101000100000001011101011011110000011100000001111",
    "1010111001000110101000100000001011101011011110000011100000010000",
    "1010111001000110101000100000001011101011011110000011100000010001",
    "1010111001000110101000100000001011101011011110000011100000010010",
    "1010111001000110101000100000001011101011011110000011100000010011",
    "1010111001000110101000100000001011101011011110000011100000010100",
    "1010111001000110101000100000001011101011011110000011100000010101",
    "1010111001000110101000100000001011101011011110000011100000010110",
    "1010111001000110101000100000001011101011011110000011100000010111",
    "1010111001000110101000100000001011101011011110000011100000011000",
    "1010111001000110101000100000001011101011011110000011100000011001",
    "1010111001000110101000100000001011101011011110000011100000011010",
    "1010111001000110101000100000001011101011011110000011100000011011",
    "1010111001000110101000100000001011101011011110000011100000011100",
    "1010111001000110101000100000001011101011011110000011100000011101",
    "1010111001000110101000100000001011101011011110000011100000011110",
    "1010111001000110101000100000001011101011011110000011100000011111",
    "1010111001000110101000100000001011101011011110000011100000100000",
    "1010111001000110101000100000001011101011011110000011100000100001",
    "1010111001000110101000100000001011101011011110000011100000100010",
    "1010111001000110101000100000001011101011011110000011100000100011",
    "1010111001000110101000100000001011101011011110000011100000100100",
    "1010111001000110101000100000001011101011011110000011100000100101",
    "1010111001000110101000100000001011101011011110000011100000100110",
    "1010111001000110101000100000001011101011011110000011100000100111",
    "1010111001000110101000100000001011101011011110000011100000101000",
    "1010111001000110101000100000001011101011011110000011100000101001",
    "1010111001000110101000100000001011101011011110000011100000101010",
    "1010111001000110101000100000001011101011011110000011100000101011",
    "1010111001000110101000100000001011101011011110000011100000101100",
    "1010111001000110101000100000001011101011011110000011100000101101",
    "1010111001000110101000100000001011101011011110000011100000101110",
    "1010111001000110101000100000001011101011011110000011100000101111",
    "1010111001000110101000100000001011101011011110000011100000110000",
    "1010111001000110101000100000001011101011011110000011100000110001",
    "1010111001000110101000100000001011101011011110000011100000110010",
    "1010111001000110101000100000001011101011011110000011100000110011",
    "1010111001000110101000100000001011101011011110000011100000110100",
    "1010111001000110101000100000001011101011011110000011100000110101",
    "1010111001000110101000100000001011101011011110000011100000110110",
    "1010111001000110101000100000001011101011011110000011100000110111",
    "1010111001000110101000100000001011101011011110000011100000111000",
    "1010111001000110101000100000001011101011011110000011100000111001",
    "1010111001000110101000100000001011101011011110000011100000111010",
    "1010111001000110101000100000001011101011011110000011100000111011",
    "1010111001000110101000100000001011101011011110000011100000111100",
    "1010111001000110101000100000001011101011011110000011100000111101",
    "1010111001000110101000100000001011101011011110000011100000111110",
    "1010111001000110101000100000001011101011011110000011100000111111",
    "1010111001000110101000100000001011101011011110000011100001000000",
    "1010111001000110101000100000001011101011011110000011100001000001",
    "1010111001000110101000100000001011101011011110000011100001000010",
    "1010111001000110101000100000001011101011011110000011100001000011",
    "1010111001000110101000100000001011101011011110000011100001000100",
    "1010111001000110101000100000001011101011011110000011100001000101",
    "1010111001000110101000100000001011101011011110000011100001000110",
    "1010111001000110101000100000001011101011011110000011100001000111",
    "1010111001000110101000100000001011101011011110000011100001001000",
    "1010111001000110101000100000001011101011011110000011100001001001",
    "1010111001000110101000100000001011101011011110000011100001001010",
    "1010111001000110101000100000001011101011011110000011100001001011",
    "1010111001000110101000100000001011101011011110000011100001001100",
    "1010111001000110101000100000001011101011011110000011100001001101",
    "1010111001000110101000100000001011101011011110000011100001001110",
    "1010111001000110101000100000001011101011011110000011100001001111",
    "1010111001000110101000100000001011101011011110000011100001010000",
    "1010111001000110101000100000001011101011011110000011100001010001",
    "1010111001000110101000100000001011101011011110000011100001010010",
    "1010111001000110101000100000001011101011011110000011100001010011",
    "1010111001000110101000100000001011101011011110000011100001010100",
    "1010111001000110101000100000001011101011011110000011100001010101",
    "1010111001000110101000100000001011101011011110000011100001010110",
    "1010111001000110101000100000001011101011011110000011100001010111",
    "1010111001000110101000100000001011101011011110000011100001011000",
    "1010111001000110101000100000001011101011011110000011100001011001",
    "1010111001000110101000100000001011101011011110000011100001011010",
    "1010111001000110101000100000001011101011011110000011100001011011",
    "1010111001000110101000100000001011101011011110000011100001011100",
    "1010111001000110101000100000001011101011011110000011100001011101",
    "1010111001000110101000100000001011101011011110000011100001011110",
    "1010111001000110101000100000001011101011011110000011100001011111",
    "1010111001000110101000100000001011101011011110000011100001100000",
    "1010111001000110101000100000001011101011011110000011100001100001",
    "1010111001000110101000100000001011101011011110000011100001100010",
    "1010111001000110101000100000001011101011011110000011100001100011",
    "1010111001000110101000100000001011101011011110000011100001100100",
    "1010111001000110101000100000001011101011011110000011100001100101",
    "1010111001000110101000100000001011101011011110000011100001100110",
    "1010111001000110101000100000001011101011011110000011100001100111",
    "1010111001000110101000100000001011101011011110000011100001101000",
    "1010111001000110101000100000001011101011011110000011100001101001",
    "1010111001000110101000100000001011101011011110000011100001101010",
    "1010111001000110101000100000001011101011011110000011100001101011",
    "1010111001000110101000100000001011101011011110000011100001101100",
    "1010111001000110101000100000001011101011011110000011100001101101",
    "1010111001000110101000100000001011101011011110000011100001101110",
    "1010111001000110101000100000001011101011011110000011100001101111",
    "1010111001000110101000100000001011101011011110000011100001110000",
    "1010111001000110101000100000001011101011011110000011100001110001",
    "1010111001000110101000100000001011101011011110000011100001110010",
    "1010111001000110101000100000001011101011011110000011100001110011",
    "1010111001000110101000100000001011101011011110000011100001110100",
    "1010111001000110101000100000001011101011011110000011100001110101",
    "1010111001000110101000100000001011101011011110000011100001110110",
    "1010111001000110101000100000001011101011011110000011100001110111",
    "1010111001000110101000100000001011101011011110000011100001111000",
    "1010111001000110101000100000001011101011011110000011100001111001",
    "1010111001000110101000100000001011101011011110000011100001111010",
    "1010111001000110101000100000001011101011011110000011100001111011",
    "1010111001000110101000100000001011101011011110000011100001111100",
    "1010111001000110101000100000001011101011011110000011100001111101",
    "1010111001000110101000100000001011101011011110000011100001111110",
    "1010111001000110101000100000001011101011011110000011100001111111",
    "1010111001000110101000100000001011101011011110000011100010000000",
    "1010111001000110101000100000001011101011011110000011100010000001",
    "1010111001000110101000100000001011101011011110000011100010000010",
    "1010111001000110101000100000001011101011011110000011100010000011",
    "1010111001000110101000100000001011101011011110000011100010000100",
    "101011100100011010100010000000101110101101111000001110001000010100000000",
    "101011100100011010100010000000101110101101111000001110001000010100000001",
    "101011100100011010100010000000101110101101111000001110001000010100000010",
    "101011100100011010100010000000101110101101111000001110001000010100000011",
    "101011100100011010100010000000101110101101111000001110001000010100000100",
    "101011100100011010100010000000101110101101111000001110001000010100000101",
    "101011100100011010100010000000101110101101111000001110001000010100000110",
    "101011100100011010100010000000101110101101111000001110001000010100000111",
    "101011100100011010100010000000101110101101111000001110001000010100001000",
    "101011100100011010100010000000101110101101111000001110001000010100001001",
    "101011100100011010100010000000101110101101111000001110001000010100001010",
    "101011100100011010100010000000101110101101111000001110001000010100001011",
    "101011100100011010100010000000101110101101111000001110001000010100001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111001",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111010",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111011",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111100",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111101",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110100111111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111001",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111010",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111011",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111100",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111101",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110101111111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101100111111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101101111111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000000111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000001111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000010111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000100111111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000101111111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000110111111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111000111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111001111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111010111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111011111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111100111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111101111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110110",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111110111",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111000",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111001",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111010",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111011",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111100",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111101",
    "1010111001000110101000100000001011101011011110000011100010000101000011011001101110010000111011000001000111111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111100111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111101111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110100111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110101111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110110111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111110111111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111000111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111001111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111010111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011110",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111011111",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100000",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100001",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100010",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100011",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100100",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100101",
    "101011100100011010100010000000101110101101111000001110001000010100001101100110111001000011101100000100011111111111100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011100111111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011101111111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011110111111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111000111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111001111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111010111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111011111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111100111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111101111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110001",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110010",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110011",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110100",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110101",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110110",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111110111",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111111000",
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111111001",
  },
},
{
  .lowerBound = mc_dec128_from_string("123456789.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("123456789.000000"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111111001",
  },
},
{
  .lowerBound = mc_dec128_from_string("123456789.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("123456789.000000"),
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = mc_dec128_from_string("-1000000000000000L") },
  .max = { .set = true, .value = MC_DEC128(0x7000000000000000L) },
  .expectMincoverStrings = {
    "10101110010001101010001000000010111010110111100000111000100001010000110110011011100100001110110000010001111111111110011111111001",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/mincover_decimal128_precision.cstruct000066400000000000000000000504651521103432300305760ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = mc_dec128_from_string("0.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("1.638400"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("0.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("6.500000") },
  .precision = { .set = true, .value = 1 },
  .expectMincoverStrings = {
    "000",
    "0010000",
  },
},
{
  .lowerBound = mc_dec128_from_string("0.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("1.638400"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("0.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("6.550000") },
  .precision = { .set = true, .value = 2 },
  .expectMincoverStrings = {
    "000",
    "00100",
    "00101000",
  },
},
{
  .lowerBound = mc_dec128_from_string("0.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("1.638400"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("0.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("6.553000") },
  .precision = { .set = true, .value = 3 },
  .expectMincoverStrings = {
    "000",
    "0010",
    "0011000",
    "00110010",
    "00110011000",
    "001100110010",
    "0011001100110",
  },
},
{
  .lowerBound = mc_dec128_from_string("0.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("1.638400"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("0.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("6.553600") },
  .precision = { .set = true, .value = 4 },
  .expectMincoverStrings = {
    "000",
    "00100000000000000",
  },
},
{
  .lowerBound = mc_dec128_from_string("1000000.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("20000000.000000"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("788545.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("340282366920938463463374607431769000000.000000") },
  .precision = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111110111111111111111111110011111111011",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101000001011111101111111111111111111100111111111",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101000001011111101111111111111111111101",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111110111111111111111111111",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111111",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111010000011",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100001",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111010001",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101001",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110101",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111011",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011111",
    "101011100100001010111100010111101100010001100010001110011000100110010011111",
    "1010111001000010101111000101111011000100011000100011100110001001100101",
    "101011100100001010111100010111101100010001100010001110011000100110011",
    "1010111001000010101111000101111011000100011000100011100110001001101",
    "101011100100001010111100010111101100010001100010001110011000100111",
    "101011100100001010111100010111101100010001100010001110011000101",
    "10101110010000101011110001011110110001000110001000111001100011",
    "101011100100001010111100010111101100010001100010001110011001",
    "10101110010000101011110001011110110001000110001000111001101",
    "1010111001000010101111000101111011000100011000100011100111",
    "1010111001000010101111000101111011000100011000100011101",
    "101011100100001010111100010111101100010001100010001111",
    "10101110010000101011110001011110110001000110001001",
    "1010111001000010101111000101111011000100011000101",
    "101011100100001010111100010111101100010001100011",
    "1010111001000010101111000101111011000100011001",
    "101011100100001010111100010111101100010001101",
    "10101110010000101011110001011110110001000111",
    "10101110010000101011110001011110110001001",
    "1010111001000010101111000101111011000101",
    "101011100100001010111100010111101100011",
    "1010111001000010101111000101111011001",
    "101011100100001010111100010111101101",
    "10101110010000101011110001011110111",
    "10101110010000101011110001011111",
    "101011100100001010111100011",
    "1010111001000010101111001",
    "101011100100001010111101",
    "10101110010000101011111",
    "101011100100001011",
    "1010111001000011",
    "10101110010001000",
    "101011100100010010",
    "10101110010001001100",
    "101011100100010011010",
    "10101110010001001101100",
    "1010111001000100110110100",
    "101011100100010011011010100",
    "1010111001000100110110101010",
    "101011100100010011011010101100",
    "1010111001000100110110101011010",
    "1010111001000100110110101011011000",
    "1010111001000100110110101011011001000",
    "1010111001000100110110101011011001001000",
    "101011100100010011011010101101100100100100",
    "10101110010001001101101010110110010010010100",
    "1010111001000100110110101011011001001001010100",
    "10101110010001001101101010110110010010010101010000",
    "10101110010001001101101010110110010010010101010001000",
    "101011100100010011011010101101100100100101010100010010",
    "1010111001000100110110101011011001001001010101000100110",
    "101011100100010011011010101101100100100101010100010011100",
    "1010111001000100110110101011011001001001010101000100111010",
    "101011100100010011011010101101100100100101010100010011101100",
    "1010111001000100110110101011011001001001010101000100111011010",
    "10101110010001001101101010110110010010010101010001001110110110",
    "1010111001000100110110101011011001001001010101000100111011011100",
    "1010111001000100110110101011011001001001010101000100111011011101000000",
    "101011100100010011011010101101100100100101010100010011101101110100000100000",
    "10101110010001001101101010110110010010010101010001001110110111010000010000100",
    "101011100100010011011010101101100100100101010100010011101101110100000100001010",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101100",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101010",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110000",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110100",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101010",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111000",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111111100110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111111100111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001111111100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011111111010",
  },
},
{
  .lowerBound = mc_dec128_from_string("1000000.000000"),
  .includeLowerBound = true,
  .upperBound = mc_dec128_from_string("20000000.000000"),
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = mc_dec128_from_string("788544.000000") },
  .max = { .set = true, .value = mc_dec128_from_string("340282366920938463463374607431769000000.000000") },
  .precision = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111110111111111111111111110011111111011",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101000001011111101111111111111111111100111111111",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101000001011111101111111111111111111101",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111110111111111111111111111",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100000101111111",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111010000011",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110100001",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111010001",
    "101011100100001010111100010111101100010001100010001110011000100110010011110111101001",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011110101",
    "1010111001000010101111000101111011000100011000100011100110001001100100111101111011",
    "10101110010000101011110001011110110001000110001000111001100010011001001111011111",
    "101011100100001010111100010111101100010001100010001110011000100110010011111",
    "1010111001000010101111000101111011000100011000100011100110001001100101",
    "101011100100001010111100010111101100010001100010001110011000100110011",
    "1010111001000010101111000101111011000100011000100011100110001001101",
    "101011100100001010111100010111101100010001100010001110011000100111",
    "101011100100001010111100010111101100010001100010001110011000101",
    "10101110010000101011110001011110110001000110001000111001100011",
    "101011100100001010111100010111101100010001100010001110011001",
    "10101110010000101011110001011110110001000110001000111001101",
    "1010111001000010101111000101111011000100011000100011100111",
    "1010111001000010101111000101111011000100011000100011101",
    "101011100100001010111100010111101100010001100010001111",
    "10101110010000101011110001011110110001000110001001",
    "1010111001000010101111000101111011000100011000101",
    "101011100100001010111100010111101100010001100011",
    "1010111001000010101111000101111011000100011001",
    "101011100100001010111100010111101100010001101",
    "10101110010000101011110001011110110001000111",
    "10101110010000101011110001011110110001001",
    "1010111001000010101111000101111011000101",
    "101011100100001010111100010111101100011",
    "1010111001000010101111000101111011001",
    "101011100100001010111100010111101101",
    "10101110010000101011110001011110111",
    "10101110010000101011110001011111",
    "101011100100001010111100011",
    "1010111001000010101111001",
    "101011100100001010111101",
    "10101110010000101011111",
    "101011100100001011",
    "1010111001000011",
    "10101110010001000",
    "101011100100010010",
    "10101110010001001100",
    "101011100100010011010",
    "10101110010001001101100",
    "1010111001000100110110100",
    "101011100100010011011010100",
    "1010111001000100110110101010",
    "101011100100010011011010101100",
    "1010111001000100110110101011010",
    "1010111001000100110110101011011000",
    "1010111001000100110110101011011001000",
    "1010111001000100110110101011011001001000",
    "101011100100010011011010101101100100100100",
    "10101110010001001101101010110110010010010100",
    "1010111001000100110110101011011001001001010100",
    "10101110010001001101101010110110010010010101010000",
    "10101110010001001101101010110110010010010101010001000",
    "101011100100010011011010101101100100100101010100010010",
    "1010111001000100110110101011011001001001010101000100110",
    "101011100100010011011010101101100100100101010100010011100",
    "1010111001000100110110101011011001001001010101000100111010",
    "101011100100010011011010101101100100100101010100010011101100",
    "1010111001000100110110101011011001001001010101000100111011010",
    "10101110010001001101101010110110010010010101010001001110110110",
    "1010111001000100110110101011011001001001010101000100111011011100",
    "1010111001000100110110101011011001001001010101000100111011011101000000",
    "101011100100010011011010101101100100100101010100010011101101110100000100000",
    "10101110010001001101101010110110010010010101010001001110110111010000010000100",
    "101011100100010011011010101101100100100101010100010011101101110100000100001010",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101100",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101010",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110000",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110100",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101010",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111000",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110010",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111111100110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011110",
    "101011100100010011011010101101100100100101010100010011101101110100000100001011010110110001101011111111111111111111100111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001111110",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011111110",
    "1010111001000100110110101011011001001001010101000100111011011101000001000010110101101100011010111111111111111111111001111111100",
    "10101110010001001101101010110110010010010101010001001110110111010000010000101101011011000110101111111111111111111110011111111010",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/mincover_double.cstruct000066400000000000000000002440111521103432300261340ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -100.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -100.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -100.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -100.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "010000000000",
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "010000000000",
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "010000000000",
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "110000000000",
    "1100000000010",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "110000000000",
    "11000000000100",
    "11000000000101",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "1100000000010000",
    "1100000000010001",
    "1100000000010010",
    "1100000000010011",
    "1100000000010100",
    "1100000000010101",
    "1100000000010110",
    "1100000000010111",
    "1100000000011000",
    "1100000000011001",
    "1100000000011010",
    "1100000000011011",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "110000000001",
    "110000000010",
    "110000000011",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "110000000",
    "1100000010000",
    "11000000100010",
    "110000001000110",
    "1100000010001110",
    "11000000100011110",
    "110000001000111110",
    "1100000010001111110",
    "11000000100011111110",
    "110000001000111111110",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "1100000000",
    "1100000001",
    "11000000100000",
    "11000000100001",
    "11000000100010",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "110000001000111100",
    "110000001000111101",
    "110000001000111110",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "1100000010001111111100",
    "1100000010001111111101",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "110000000",
    "11000000100",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "1100000000",
    "1100000001",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "110000000001",
    "110000000010",
    "110000000011",
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "110000000",
    "1100000010",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "1100000000",
    "1100000001",
    "1100000010",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "11000000",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "00111111101010",
    "00111111101011",
    "001111111011",
    "0011111111",
    "01",
    "10",
    "11000000",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -100.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0011111110100111",
    "0011111110101000",
    "0011111110101001",
    "0011111110101010",
    "0011111110101011",
    "0011111110101100",
    "0011111110101101",
    "0011111110101110",
    "0011111110101111",
    "001111111011",
    "001111111100",
    "001111111101",
    "001111111110",
    "001111111111",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "11000000",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = -1.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "0100000000010000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "010000000100",
    "010000000101",
    "010000000110",
    "010000000111",
    "010000001000",
    "010000001001",
    "010000001010",
    "010000001011",
    "010000001100",
    "010000001101",
    "010000001110",
    "010000001111",
    "01000001",
    "01000010",
    "01000011",
    "01000100",
    "01000101",
    "01000110",
    "01000111",
    "01001000",
    "01001001",
    "01001010",
    "01001011",
    "01001100",
    "01001101",
    "01001110",
    "01001111",
    "0101",
    "0110",
    "0111",
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "110000000000",
    "1100000000010",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "110000000000",
    "11000000000100",
    "11000000000101",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "010000000100",
    "010000000101",
    "010000000110",
    "010000000111",
    "010000001000",
    "010000001001",
    "010000001010",
    "010000001011",
    "010000001100",
    "010000001101",
    "010000001110",
    "010000001111",
    "01000001",
    "01000010",
    "01000011",
    "01000100",
    "01000101",
    "01000110",
    "01000111",
    "01001000",
    "01001001",
    "01001010",
    "01001011",
    "01001100",
    "01001101",
    "01001110",
    "01001111",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "110000000001",
    "110000000010",
    "110000000011",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "110000000",
    "1100000010000",
    "11000000100010",
    "110000001000110",
    "1100000010001110",
    "11000000100011110",
    "110000001000111110",
    "1100000010001111110",
    "11000000100011111110",
    "110000001000111111110",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "1100000000",
    "1100000001",
    "11000000100000",
    "11000000100001",
    "11000000100010",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "110000001000111100",
    "110000001000111101",
    "110000001000111110",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "1100000010001111111100",
    "1100000010001111111101",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "110000000",
    "11000000100",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "1100000000",
    "1100000001",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "110000000",
    "1100000010",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "1100000000",
    "1100000001",
    "1100000010",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "01000000001",
    "0100000001",
    "010000001",
    "01000001",
    "0100001",
    "010001",
    "01001",
    "0101",
    "011",
    "10",
    "11000000",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "010000000001",
    "010000000010",
    "010000000011",
    "0100000001",
    "0100000010",
    "0100000011",
    "01000001",
    "01000010",
    "01000011",
    "010001",
    "010010",
    "010011",
    "0101",
    "0110",
    "0111",
    "10",
    "11000000",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 0.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "110000000000",
    "1100000000010",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "110000000000",
    "11000000000100",
    "11000000000101",
    "11000000000110",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "1100000000010000",
    "1100000000010001",
    "1100000000010010",
    "1100000000010011",
    "1100000000010100",
    "1100000000010101",
    "1100000000010110",
    "1100000000010111",
    "1100000000011000",
    "1100000000011001",
    "1100000000011010",
    "1100000000011011",
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "1100000000",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "110000000001",
    "110000000010",
    "110000000011",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "110000000",
    "1100000010000",
    "11000000100010",
    "110000001000110",
    "1100000010001110",
    "11000000100011110",
    "110000001000111110",
    "1100000010001111110",
    "11000000100011111110",
    "110000001000111111110",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "1100000000",
    "1100000001",
    "11000000100000",
    "11000000100001",
    "11000000100010",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "110000001000111100",
    "110000001000111101",
    "110000001000111110",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "1100000010001111111100",
    "1100000010001111111101",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "110000000",
    "11000000100",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "1100000000",
    "1100000001",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000",
    "1001",
    "1010",
    "1011",
    "110000000000",
    "110000000001",
    "110000000010",
    "110000000011",
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "110000000",
    "1100000010",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "1100000000",
    "1100000001",
    "1100000010",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "11000000",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "10",
    "11000000",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1000",
    "1001",
    "1010",
    "1011",
    "11000000",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 7.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "11000000001",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "110000000010",
    "110000000011",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100",
    "1100000000011101",
    "1100000000011110",
    "1100000000011111",
    "110000000010",
    "110000000011",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100",
    "1100000000011101",
    "1100000000011110",
    "1100000000011111",
    "1100000000100000",
    "1100000000100001",
    "1100000000100010",
    "1100000000100011",
    "1100000000100100",
    "1100000000100101",
    "1100000000100110",
    "1100000000100111",
    "1100000000101000",
    "1100000000101001",
    "1100000000101010",
    "1100000000101011",
    "1100000000101100",
    "1100000000101101",
    "1100000000101110",
    "1100000000101111",
    "1100000000110000",
    "1100000000110001",
    "1100000000110010",
    "1100000000110011",
    "1100000000110100",
    "1100000000110101",
    "1100000000110110",
    "1100000000110111",
    "1100000000111000",
    "1100000000111001",
    "1100000000111010",
    "1100000000111011",
    "1100000000111100",
    "1100000000111101",
    "1100000000111110",
    "1100000000111111",
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "11000000001",
    "1100000001",
    "1100000010000",
    "11000000100010",
    "110000001000110",
    "1100000010001110",
    "11000000100011110",
    "110000001000111110",
    "1100000010001111110",
    "11000000100011111110",
    "110000001000111111110",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "110000000010",
    "110000000011",
    "1100000001",
    "11000000100000",
    "11000000100001",
    "11000000100010",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "110000001000111100",
    "110000001000111101",
    "110000001000111110",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "1100000010001111111100",
    "1100000010001111111101",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100",
    "1100000000011101",
    "1100000000011110",
    "1100000000011111",
    "110000000010",
    "110000000011",
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "1100000010000000",
    "1100000010000001",
    "1100000010000010",
    "1100000010000011",
    "1100000010000100",
    "1100000010000101",
    "1100000010000110",
    "1100000010000111",
    "1100000010001000",
    "1100000010001001",
    "1100000010001010",
    "1100000010001011",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "11000000100011110000",
    "11000000100011110001",
    "11000000100011110010",
    "11000000100011110011",
    "11000000100011110100",
    "11000000100011110101",
    "11000000100011110110",
    "11000000100011110111",
    "11000000100011111000",
    "11000000100011111001",
    "11000000100011111010",
    "11000000100011111011",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "110000001000111111110000",
    "110000001000111111110001",
    "110000001000111111110010",
    "110000001000111111110011",
    "110000001000111111110100",
    "110000001000111111110101",
    "110000001000111111110110",
    "110000001000111111110111",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "11000000001",
    "1100000001",
    "11000000100",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "110000000010",
    "110000000011",
    "1100000001",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100",
    "1100000000011101",
    "1100000000011110",
    "1100000000011111",
    "110000000010",
    "110000000011",
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "11000000001",
    "1100000001",
    "1100000010",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "110000000010",
    "110000000011",
    "1100000001",
    "1100000010",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "11000000001",
    "1100000001",
    "110000001",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "11000000000111",
    "110000000010",
    "110000000011",
    "1100000001",
    "1100000010",
    "1100000011",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 7.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000000011100",
    "1100000000011101",
    "1100000000011110",
    "1100000000011111",
    "110000000010",
    "110000000011",
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "110000001011",
    "110000001100",
    "110000001101",
    "110000001110",
    "110000001111",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 32.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "1100000010000",
    "11000000100010",
    "110000001000110",
    "1100000010001110",
    "11000000100011110",
    "110000001000111110",
    "1100000010001111110",
    "11000000100011111110",
    "110000001000111111110",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "11000000100000",
    "11000000100001",
    "11000000100010",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "110000001000111100",
    "110000001000111101",
    "110000001000111110",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "1100000010001111111100",
    "1100000010001111111101",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "1100000010000000",
    "1100000010000001",
    "1100000010000010",
    "1100000010000011",
    "1100000010000100",
    "1100000010000101",
    "1100000010000110",
    "1100000010000111",
    "1100000010001000",
    "1100000010001001",
    "1100000010001010",
    "1100000010001011",
    "1100000010001100",
    "1100000010001101",
    "1100000010001110",
    "11000000100011110000",
    "11000000100011110001",
    "11000000100011110010",
    "11000000100011110011",
    "11000000100011110100",
    "11000000100011110101",
    "11000000100011110110",
    "11000000100011110111",
    "11000000100011111000",
    "11000000100011111001",
    "11000000100011111010",
    "11000000100011111011",
    "11000000100011111100",
    "11000000100011111101",
    "11000000100011111110",
    "110000001000111111110000",
    "110000001000111111110001",
    "110000001000111111110010",
    "110000001000111111110011",
    "110000001000111111110100",
    "110000001000111111110101",
    "110000001000111111110110",
    "110000001000111111110111",
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "11000000100",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "1100000010",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "1100000010",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "110000001",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000001",
    "1100000010",
    "1100000011",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 32.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000000100",
    "110000000101",
    "110000000110",
    "110000000111",
    "110000001000",
    "110000001001",
    "110000001010",
    "110000001011",
    "110000001100",
    "110000001101",
    "110000001110",
    "110000001111",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1023.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001000111111111",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110",
    "1100000010001111111111",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001000111111111000",
    "110000001000111111111001",
    "110000001000111111111010",
    "110000001000111111111011",
    "110000001000111111111100",
    "110000001000111111111101",
    "110000001000111111111110",
    "110000001000111111111111",
    "110000001001",
    "110000001010",
    "1100000010110000",
    "11000000101100010000",
    "110000001011000100010000",
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001000111111111",
    "110000001001",
    "11000000101",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110",
    "1100000010001111111111",
    "110000001001",
    "110000001010",
    "110000001011",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001000111111111",
    "110000001001",
    "11000000101",
    "1100000011",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010001111111110",
    "1100000010001111111111",
    "110000001001",
    "110000001010",
    "110000001011",
    "1100000011",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1023.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001000111111111000",
    "110000001000111111111001",
    "110000001000111111111010",
    "110000001000111111111011",
    "110000001000111111111100",
    "110000001000111111111101",
    "110000001000111111111110",
    "110000001000111111111111",
    "110000001001",
    "110000001010",
    "110000001011",
    "110000001100",
    "110000001101",
    "110000001110",
    "110000001111",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 4369.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000010110001000100010000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001011000100010001",
    "11000000101100010001001",
    "1100000010110001000101",
    "110000001011000100011",
    "1100000010110001001",
    "110000001011000101",
    "11000000101100011",
    "110000001011001",
    "11000000101101",
    "1100000010111",
    "11000000110",
    "1100000011100",
    "11000000111010",
    "110000001110110",
    "1100000011101110",
    "11000000111011110",
    "110000001110111110",
    "1100000011101111110",
    "11000000111011111110",
    "110000001110111111110",
    "1100000011101111111110",
    "11000000111011111111110",
    "110000001110111111111110",
    "1100000011101111111111110",
    "11000000111011111111111110",
    "110000001110111111111111110",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001011000100010001",
    "110000001011000100010010",
    "110000001011000100010011",
    "1100000010110001000101",
    "1100000010110001000110",
    "1100000010110001000111",
    "11000000101100010010",
    "11000000101100010011",
    "110000001011000101",
    "110000001011000110",
    "110000001011000111",
    "1100000010110010",
    "1100000010110011",
    "11000000101101",
    "11000000101110",
    "11000000101111",
    "110000001100",
    "110000001101",
    "11000000111000",
    "11000000111001",
    "11000000111010",
    "1100000011101100",
    "1100000011101101",
    "1100000011101110",
    "110000001110111100",
    "110000001110111101",
    "110000001110111110",
    "11000000111011111100",
    "11000000111011111101",
    "11000000111011111110",
    "1100000011101111111100",
    "1100000011101111111101",
    "1100000011101111111110",
    "110000001110111111111100",
    "110000001110111111111101",
    "110000001110111111111110",
    "11000000111011111111111100",
    "11000000111011111111111101",
    "11000000111011111111111110",
    "1100000011101111111111111100",
    "1100000011101111111111111101",
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001011000100010001",
    "11000000101100010001001",
    "1100000010110001000101",
    "110000001011000100011",
    "1100000010110001001",
    "110000001011000101",
    "11000000101100011",
    "110000001011001",
    "11000000101101",
    "1100000010111",
    "1100000011",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 4369.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001011000100010001",
    "110000001011000100010010",
    "110000001011000100010011",
    "1100000010110001000101",
    "1100000010110001000110",
    "1100000010110001000111",
    "11000000101100010010",
    "11000000101100010011",
    "110000001011000101",
    "110000001011000110",
    "110000001011000111",
    "1100000010110010",
    "1100000010110011",
    "11000000101101",
    "11000000101110",
    "11000000101111",
    "1100000011",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 65535.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "110000001110111111111111111",
    "110000001111",
    "110000010",
    "1100000110",
    "110000011100",
    "1100000111010",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110",
    "1100000011101111111111111111",
    "110000001111",
    "1100000100",
    "1100000101",
    "1100000110",
    "110000011100",
    "11000001110100",
    "11000001110101",
    "11000001110110",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 65535.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000011101111111111111110",
    "1100000011101111111111111111",
    "110000001111",
    "110000010000",
    "110000010001",
    "110000010010",
    "110000010011",
    "110000010100",
    "110000010101",
    "110000010110",
    "110000010111",
    "110000011000",
    "110000011001",
    "110000011010",
    "110000011011",
    "110000011100",
    "1100000111010000",
    "1100000111010001",
    "1100000111010010",
    "1100000111010011",
    "1100000111010100",
    "1100000111010101",
    "1100000111010110",
    "1100000111010111",
    "1100000111011000",
    "1100000111011001",
    "1100000111011010",
    "1100000111011011",
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192.000000,
  .includeLowerBound = true,
  .upperBound = 1879048192.000000,
  .includeUpperBound = true,
  .sparsity = 8,
  .min = { .set = true, .value = -1000000000000000L },
  .max = { .set = true, .value = 0x7000000000000000L },
  .expectMincoverStrings = {
    "1100000111011100000000000000000000000000000000000000000000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/mincover_double_precision.cstruct000066400000000000000000000205721521103432300302130ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1.638400,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0.000000 },
  .max = { .set = true, .value = 6.500000 },
  .precision = { .set = true, .value = 1 },
  .expectMincoverStrings = {
    "000",
    "0010000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1.638400,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0.000000 },
  .max = { .set = true, .value = 6.550000 },
  .precision = { .set = true, .value = 2 },
  .expectMincoverStrings = {
    "000",
    "00100",
    "00101000",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1.638400,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0.000000 },
  .max = { .set = true, .value = 6.553000 },
  .precision = { .set = true, .value = 3 },
  .expectMincoverStrings = {
    "000",
    "0010",
    "0011000",
    "00110010",
    "00110011000",
    "001100110010",
    "0011001100110",
  },
},
{
  .lowerBound = 0.000000,
  .includeLowerBound = true,
  .upperBound = 1.638400,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0.000000 },
  .max = { .set = true, .value = 6.553600 },
  .precision = { .set = true, .value = 4 },
  .expectMincoverStrings = {
    "000",
    "00100000000000000",
  },
},
{
  .lowerBound = 10000.000000,
  .includeLowerBound = true,
  .upperBound = 20000.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 383.000000 },
  .max = { .set = true, .value = 18446744073709552000.000000 },
  .precision = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "110000001100001110001",
    "11000000110000111001",
    "1100000011000011101",
    "110000001100001111",
    "11000000110001",
    "1100000011001",
    "110000001101000",
    "1100000011010010",
    "11000000110100110",
    "110000001101001110000",
    "1100000011010011100010000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 10000.000000,
  .includeLowerBound = true,
  .upperBound = 20000.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 384.000000 },
  .max = { .set = true, .value = 18446744073709552000.000000 },
  .precision = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "110000001100001110001",
    "11000000110000111001",
    "1100000011000011101",
    "110000001100001111",
    "11000000110001",
    "1100000011001",
    "110000001101000",
    "1100000011010010",
    "11000000110100110",
    "110000001101001110000",
    "1100000011010011100010000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.000000 },
  .max = { .set = true, .value = 314.000000 },
  .precision = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "000001011",
    "0000011",
    "00001",
    "0001",
    "001",
    "0100",
    "01010",
    "010110",
    "0101110",
    "01011110",
    "010111110",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.700000 },
  .max = { .set = true, .value = 314.100000 },
  .precision = { .set = true, .value = 1 },
  .expectMincoverStrings = {
    "000001100111",
    "000001101",
    "00000111",
    "00001",
    "0001",
    "001",
    "010",
    "0110",
    "011100",
    "0111010",
    "0111011000",
    "01110110010",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.710000 },
  .max = { .set = true, .value = 314.150000 },
  .precision = { .set = true, .value = 2 },
  .expectMincoverStrings = {
    "000010000000101",
    "00001000000011",
    "000010000001",
    "00001000001",
    "0000100001",
    "000010001",
    "00001001",
    "0000101",
    "000011",
    "0001",
    "001",
    "01",
    "1000",
    "1001000",
    "10010010",
    "100100110",
    "1001001110",
    "10010011110",
    "10010011111000",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.718000 },
  .max = { .set = true, .value = 314.159000 },
  .precision = { .set = true, .value = 3 },
  .expectMincoverStrings = {
    "000001010000010101",
    "00000101000001011",
    "000001010000011",
    "0000010100001",
    "000001010001",
    "00000101001",
    "0000010101",
    "000001011",
    "0000011",
    "00001",
    "0001",
    "001",
    "0100",
    "01010",
    "010110",
    "0101110000",
    "01011100010",
    "0101110001100",
    "01011100011010",
    "010111000110110000",
    "0101110001101100010",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.718200 },
  .max = { .set = true, .value = 314.159200 },
  .precision = { .set = true, .value = 4 },
  .expectMincoverStrings = {
    "000001100100011010001",
    "00000110010001101001",
    "0000011001000110101",
    "000001100100011011",
    "0000011001000111",
    "0000011001001",
    "000001100101",
    "00000110011",
    "000001101",
    "00000111",
    "00001",
    "0001",
    "001",
    "010",
    "0110",
    "0111000",
    "01110010",
    "011100110",
    "01110011100000",
    "011100111000010",
    "0111001110000110",
    "011100111000011100",
    "011100111000011101000",
    "0111001110000111010010",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.718280 },
  .max = { .set = true, .value = 314.159260 },
  .precision = { .set = true, .value = 5 },
  .expectMincoverStrings = {
    "00000111110110000010011",
    "000001111101100000101",
    "00000111110110000011",
    "000001111101100001",
    "00000111110110001",
    "0000011111011001",
    "000001111101101",
    "00000111110111",
    "00000111111",
    "00001",
    "0001",
    "001",
    "01",
    "1000",
    "1001000000",
    "10010000010",
    "1001000001100",
    "1001000001101000",
    "10010000011010010000",
    "1001000001101001000100",
    "10010000011010010001010",
    "1001000001101001000101100",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.718281 },
  .max = { .set = true, .value = 314.159265 },
  .precision = { .set = true, .value = 6 },
  .expectMincoverStrings = {
    "00000100111001110001011110111",
    "00000100111001110001011111",
    "000001001110011100011",
    "0000010011100111001",
    "000001001110011101",
    "00000100111001111",
    "0000010011101",
    "000001001111",
    "00000101",
    "0000011",
    "00001",
    "0001",
    "001",
    "0100",
    "01010",
    "0101100",
    "0101101000",
    "0101101001000000",
    "01011010010000010",
    "0101101001000001100",
    "010110100100000110100",
    "0101101001000001101010",
    "010110100100000110101100",
    "0101101001000001101011010",
    "01011010010000011010110110",
  },
},
{
  .lowerBound = 13.000000,
  .includeLowerBound = true,
  .upperBound = 192.000000,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 2.718282 },
  .max = { .set = true, .value = 314.159265 },
  .precision = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "000001100010000011011101100111",
    "000001100010000011011101101",
    "00000110001000001101110111",
    "00000110001000001101111",
    "0000011000100000111",
    "0000011000100001",
    "000001100010001",
    "00000110001001",
    "0000011000101",
    "000001100011",
    "0000011001",
    "000001101",
    "00000111",
    "00001",
    "0001",
    "001",
    "010",
    "0110",
    "011100000",
    "0111000010",
    "011100001100",
    "011100001101000",
    "01110000110100100000",
    "011100001101001000010",
    "011100001101001000011000",
    "0111000011010010000110010000",
    "01110000110100100001100100010",
    "011100001101001000011001000110",
    "01110000110100100001100100011100",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/mincover_int32.cstruct000066400000000000000000003144571521103432300256350ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011000011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110000111",
    "00000001001100010010110001",
    "0000000100110001001011001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101100001110",
    "000000010011000100101100001111",
    "00000001001100010010110001",
    "00000001001100010010110010",
    "00000001001100010010110011",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101100001110",
    "000000010011000100101100001111",
    "000000010011000100101100010",
    "000000010011000100101100011",
    "000000010011000100101100100",
    "000000010011000100101100101",
    "000000010011000100101100110",
    "000000010011000100101100111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110000111",
    "00000001001100010010110001",
    "0000000100110001001011001",
    "0000000100110001001011010000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110000111",
    "00000001001100010010110001",
    "0000000100110001001011001",
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101100001110",
    "000000010011000100101100001111",
    "00000001001100010010110001",
    "00000001001100010010110010",
    "00000001001100010010110011",
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101100001110",
    "000000010011000100101100001111",
    "000000010011000100101100010",
    "000000010011000100101100011",
    "000000010011000100101100100",
    "000000010011000100101100101",
    "000000010011000100101100110",
    "000000010011000100101100111",
    "000000010011000100101101000000",
    "000000010011000100101101000001",
    "000000010011000100101101000010",
    "000000010011000100101101000011",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "1001100010010110001",
    "100110001001011001",
    "1001100010010110100",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110000111",
    "10011000100101100010",
    "10011000100101100011",
    "100110001001011001",
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "100110001001011000100",
    "100110001001011000101",
    "100110001001011000110",
    "100110001001011000111",
    "100110001001011001",
    "100110001001011010000",
    "100110001001011010001",
    "100110001001011010010",
    "100110001001011010011",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011000011100",
    "100110001001011000011101",
    "100110001001011000011110",
    "100110001001011000011111",
    "10011000100101100010",
    "10011000100101100011",
    "10011000100101100100",
    "10011000100101100101",
    "10011000100101100110",
    "10011000100101100111",
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110000111",
    "00000001001100010010110001",
    "0000000100110001001011001",
    "00000001001100010010110100",
    "0000000100110001001011010100000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110000111",
    "00000001001100010010110001",
    "0000000100110001001011001",
    "000000010011000100101101",
    "00000001001100010010111",
    "00000001001100010011",
    "000000010011000101",
    "00000001001100011",
    "000000010011001",
    "00000001001101",
    "0000000100111",
    "0000000101",
    "000000011",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "11100000",
    "11100001000",
    "111000010010",
    "1110000100110000",
    "1110000100110001000",
    "111000010011000100100",
    "1110000100110001001010",
    "111000010011000100101100",
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011001111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "0",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011001111111",
    "0000000100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
    "0001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
    "0001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "00000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
    "000001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
    "000001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011001111111",
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0",
    "1000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "00",
    "01",
    "1000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0000",
    "0001",
    "0010",
    "0011",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
    "001000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0000",
    "0001",
    "001000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
    "001000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000",
    "0000000000000000000000000001000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "1001100010010110100",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "100110001001011010000",
    "100110001001011010001",
    "100110001001011010010",
    "100110001001011010011",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011001111111",
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011001111111",
    "00000001001100010010110100",
    "0000000100110001001011010100000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0",
    "10000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "00",
    "01",
    "100000",
    "100001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
    "001",
    "010",
    "011",
    "100000",
    "100001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000000000000000000000000",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000000000000000000000000",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000",
    "000000000000000000000000001",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011001111111",
    "000000010011000100101101",
    "00000001001100010010111",
    "00000001001100010011",
    "000000010011000101",
    "00000001001100011",
    "000000010011001",
    "00000001001101",
    "0000000100111",
    "0000000101",
    "000000011",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "11100000",
    "11100001000",
    "111000010010",
    "1110000100110000",
    "1110000100110001000",
    "111000010011000100100",
    "1110000100110001001010",
    "111000010011000100101100",
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0",
    "10",
    "110",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00",
    "01",
    "10",
    "1100",
    "1101",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000",
    "001",
    "010",
    "011",
    "100",
    "101",
    "110",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110100000",
    "1001100010010110100001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000000",
    "100110001001011010000001",
    "100110001001011010000010",
    "100110001001011010000011",
    "100110001001011010000100",
    "100110001001011010000101",
    "100110001001011010000110",
    "100110001001011010000111",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101101000000",
    "000000010011000100101101000001",
    "000000010011000100101101000010",
    "000000010011000100101101000011",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
    "001",
    "01",
    "1000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
    "0010",
    "0011",
    "01",
    "1000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "0001",
    "0010",
    "0011",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "00001",
    "0001",
    "001000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "000010",
    "000011",
    "0001",
    "001000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "000010",
    "000011",
    "000100",
    "000101",
    "000110",
    "000111",
    "001000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000001",
    "000000000000000000000000000001",
    "00000000000000000000000000001",
    "0000000000000000000000000001000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0000",
    "0001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0000",
    "0001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000000",
    "000000000000000000000000000001",
    "000000000000000000000000000010",
    "000000000000000000000000000011",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "1001100010010110100",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000",
    "100110001001011010001",
    "100110001001011010010",
    "100110001001011010011",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "10011000100101101000",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110100",
    "0000000100110001001011010100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "000010",
    "000011",
    "0001",
    "0010",
    "0011",
    "01",
    "100000",
    "100001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000001",
    "000010",
    "000011",
    "000100",
    "000101",
    "000110",
    "000111",
    "001",
    "010",
    "011",
    "100000",
    "100001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000001",
    "000000000000000000000000000001",
    "00000000000000000000000000001",
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0",
    "100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "00",
    "01",
    "100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000",
    "001",
    "010",
    "011",
    "100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000000000000000000000000",
    "0000000000000000000000000100000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000010011000100101101",
    "00000001001100010010111",
    "00000001001100010011",
    "000000010011000101",
    "00000001001100011",
    "000000010011001",
    "00000001001101",
    "0000000100111",
    "0000000101",
    "000000011",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "11100000",
    "11100001000",
    "111000010010",
    "1110000100110000",
    "1110000100110001000",
    "111000010011000100100",
    "1110000100110001001010",
    "111000010011000100101100",
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000001",
    "000000000000000000000000000001",
    "00000000000000000000000000001",
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "00000000000000000000000001",
    "0000000000000000000000001",
    "000000000000000000000001",
    "00000000000000000000001",
    "0000000000000000000001",
    "000000000000000000001",
    "00000000000000000001",
    "0000000000000000001",
    "000000000000000001",
    "00000000000000001",
    "0000000000000001",
    "000000000000001",
    "00000000000001",
    "0000000000001",
    "000000000001",
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0",
    "10",
    "110",
    "1110000000000000000000000000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "1000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "001000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "001000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "001000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000001000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 7 },
  .expectMincoverStrings = {
    "111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000111",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "00000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 7,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
    "100110001001011010001",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
    "1001100010010110100010",
    "1001100010010110100011",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
    "100110001001011010001",
    "100110001001011010010",
    "100110001001011010011",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010000111",
    "100110001001011010001000",
    "100110001001011010001001",
    "100110001001011010001010",
    "100110001001011010001011",
    "100110001001011010001100",
    "100110001001011010001101",
    "100110001001011010001110",
    "100110001001011010001111",
    "10011000100101101001",
    "100110001001011010100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000111",
    "0000000100110001001011010001",
    "000000010011000100101101001",
    "0000000100110001001011010100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "001",
    "01",
    "10000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0010",
    "0011",
    "01",
    "100000",
    "100001",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "001",
    "010",
    "011",
    "100000",
    "100001",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000001",
    "0000000000000000000000000010",
    "0000000000000000000000000011",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000100",
    "000000000000000000000000000101",
    "000000000000000000000000000110",
    "000000000000000000000000000111",
    "000000000000000000000000001",
    "000000000000000000000000010000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
    "001",
    "01",
    "100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
    "0010",
    "0011",
    "01",
    "100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "000111",
    "001",
    "010",
    "011",
    "100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000111",
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "0000000000000000000000000100000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0",
    "10",
    "1100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "00",
    "01",
    "10",
    "1100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "0000",
    "0001",
    "0010",
    "0011",
    "0100",
    "0101",
    "0110",
    "0111",
    "1000",
    "1001",
    "1010",
    "1011",
    "1100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000",
    "0000000000000000000000000010",
    "000000000000000000000000001100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000",
    "0000000000000000000000000001",
    "0000000000000000000000000010",
    "000000000000000000000000001100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "000000000000000000000000000",
    "000000000000000000000000001000",
    "000000000000000000000000001001",
    "000000000000000000000000001010",
    "000000000000000000000000001011",
    "000000000000000000000000001100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010000111",
    "0000000100110001001011010001",
    "000000010011000100101101001",
    "00000001001100010010110101",
    "0000000100110001001011011",
    "00000001001100010010111",
    "00000001001100010011",
    "000000010011000101",
    "00000001001100011",
    "000000010011001",
    "00000001001101",
    "0000000100111",
    "0000000101",
    "000000011",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "11100000",
    "11100001000",
    "111000010010",
    "1110000100110000",
    "1110000100110001000",
    "111000010011000100100",
    "1110000100110001001010",
    "111000010011000100101100",
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "00000000000000000000000001",
    "0000000000000000000000001",
    "000000000000000000000001",
    "00000000000000000000001",
    "0000000000000000000001",
    "000000000000000000001",
    "00000000000000000001",
    "0000000000000000001",
    "000000000000000001",
    "00000000000000001",
    "0000000000000001",
    "000000000000001",
    "00000000000001",
    "0000000000001",
    "000000000001",
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000001",
    "0000000000000000000000000010",
    "0000000000000000000000000011",
    "00000000000000000000000001",
    "00000000000000000000000010",
    "00000000000000000000000011",
    "000000000000000000000001",
    "000000000000000000000010",
    "000000000000000000000011",
    "0000000000000000000001",
    "0000000000000000000010",
    "0000000000000000000011",
    "00000000000000000001",
    "00000000000000000010",
    "00000000000000000011",
    "000000000000000001",
    "000000000000000010",
    "000000000000000011",
    "0000000000000001",
    "0000000000000010",
    "0000000000000011",
    "00000000000001",
    "00000000000010",
    "00000000000011",
    "000000000001",
    "000000000010",
    "000000000011",
    "0000000001",
    "0000000010",
    "0000000011",
    "00000001",
    "00000010",
    "00000011",
    "000001",
    "000010",
    "000011",
    "0001",
    "0010",
    "0011",
    "01",
    "10",
    "1100",
    "1101",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000111",
    "0000000000000000000000000001",
    "000000000000000000000000001",
    "00000000000000000000000001",
    "0000000000000000000000001",
    "000000000000000000000001",
    "00000000000000000000001",
    "0000000000000000000001",
    "000000000000000000001",
    "00000000000000000001",
    "0000000000000000001",
    "000000000000000001",
    "00000000000000001",
    "0000000000000001",
    "000000000000001",
    "00000000000001",
    "0000000000001",
    "000000000001",
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "1110000000000000000000000000000",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0",
    "10",
    "1100",
    "11010",
    "110110",
    "1101110",
    "11011110",
    "110111110",
    "1101111110",
    "11011111110",
    "110111111110",
    "1101111111110",
    "11011111111110",
    "110111111111110",
    "1101111111111110",
    "11011111111111110",
    "110111111111111110",
    "1101111111111111110",
    "11011111111111111110",
    "110111111111111111110",
    "1101111111111111111110",
    "11011111111111111111110",
    "110111111111111111111110",
    "1101111111111111111111110",
    "11011111111111111111111110",
    "110111111111111111111111110",
    "1101111111111111111111111110",
    "110111111111111111111111111100",
  },
},
{
  .lowerBound = 7,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00",
    "01",
    "10",
    "1100",
    "110100",
    "110101",
    "110110",
    "11011100",
    "11011101",
    "11011110",
    "1101111100",
    "1101111101",
    "1101111110",
    "110111111100",
    "110111111101",
    "110111111110",
    "11011111111100",
    "11011111111101",
    "11011111111110",
    "1101111111111100",
    "1101111111111101",
    "1101111111111110",
    "110111111111111100",
    "110111111111111101",
    "110111111111111110",
    "11011111111111111100",
    "11011111111111111101",
    "11011111111111111110",
    "1101111111111111111100",
    "1101111111111111111101",
    "1101111111111111111110",
    "110111111111111111111100",
    "110111111111111111111101",
    "110111111111111111111110",
    "11011111111111111111111100",
    "11011111111111111111111101",
    "11011111111111111111111110",
    "1101111111111111111111111100",
    "1101111111111111111111111101",
    "1101111111111111111111111110",
    "110111111111111111111111111100",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100110001001011010100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000100110001001011010100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000100001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000100000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 32 },
  .expectMincoverStrings = {
    "11001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000011001",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 32,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 32 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000000000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000001001100010010110101",
    "0000000100110001001011011",
    "00000001001100010010111",
    "00000001001100010011",
    "000000010011000101",
    "00000001001100011",
    "000000010011001",
    "00000001001101",
    "0000000100111",
    "0000000101",
    "000000011",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "11100000",
    "11100001000",
    "111000010010",
    "1110000100110000",
    "1110000100110001000",
    "111000010011000100100",
    "1110000100110001001010",
    "111000010011000100101100",
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0000000000000000000000000100001",
    "000000000000000000000000010001",
    "00000000000000000000000001001",
    "0000000000000000000000000101",
    "000000000000000000000000011",
    "0000000000000000000000001",
    "000000000000000000000001",
    "00000000000000000000001",
    "0000000000000000000001",
    "000000000000000000001",
    "00000000000000000001",
    "0000000000000000001",
    "000000000000000001",
    "00000000000000001",
    "0000000000000001",
    "000000000000001",
    "00000000000001",
    "0000000000001",
    "000000000001",
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "111000000000000000000000000000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "00000000000000000000000001",
    "0000000000000000000000001",
    "000000000000000000000001",
    "00000000000000000000001",
    "0000000000000000000001",
    "000000000000000000001",
    "00000000000000000001",
    "0000000000000000001",
    "000000000000000001",
    "00000000000000001",
    "0000000000000001",
    "000000000000001",
    "00000000000001",
    "0000000000001",
    "000000000001",
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "10",
    "110",
    "1110000000000000000000000000000",
  },
},
{
  .lowerBound = 32,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 32 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "0",
    "10",
    "1100",
    "11010",
    "110110",
    "1101110",
    "11011110",
    "110111110",
    "1101111110",
    "11011111110",
    "110111111110",
    "1101111111110",
    "11011111111110",
    "110111111111110",
    "1101111111111110",
    "11011111111111110",
    "110111111111111110",
    "1101111111111111110",
    "11011111111111111110",
    "110111111111111111110",
    "1101111111111111111110",
    "11011111111111111111110",
    "110111111111111111111110",
    "1101111111111111111111110",
    "11011111111111111111111110",
    "1101111111111111111111111100000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -10000000 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "1110000100110001001011010000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "1110000000000000000000000000001",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "1110000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 7 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "1101111111111111111111111111001",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 32 },
  .max = { .set = true, .value = 1879048192 },
  .expectMincoverStrings = {
    "1101111111111111111111111100000",
  },
},
libmongocrypt-1.19.0/test/data/range-min-cover/mincover_int64.cstruct000066400000000000000000003516111521103432300256330ustar00rootroot00000000000000// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`
// clang-format off
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -100,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "111000110101111110101001001100011001111111110",
    "111000110101111110101001001100011001111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101000",
    "111000110101111110101001001100011001111111101001",
    "111000110101111110101001001100011001111111101010",
    "111000110101111110101001001100011001111111101011",
    "111000110101111110101001001100011001111111101100",
    "111000110101111110101001001100011001111111101101",
    "111000110101111110101001001100011001111111101110",
    "111000110101111110101001001100011001111111101111",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "111000110101111110101001001100011001111111110",
    "111000110101111110101001001100011001111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101000",
    "111000110101111110101001001100011001111111101001",
    "111000110101111110101001001100011001111111101010",
    "111000110101111110101001001100011001111111101011",
    "111000110101111110101001001100011001111111101100",
    "111000110101111110101001001100011001111111101101",
    "111000110101111110101001001100011001111111101110",
    "111000110101111110101001001100011001111111101111",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "111000110101111110101001001100011001111111110",
    "111000110101111110101001001100011001111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101000",
    "111000110101111110101001001100011001111111101001",
    "111000110101111110101001001100011001111111101010",
    "111000110101111110101001001100011001111111101011",
    "111000110101111110101001001100011001111111101100",
    "111000110101111110101001001100011001111111101101",
    "111000110101111110101001001100011001111111101110",
    "111000110101111110101001001100011001111111101111",
    "11100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000111000110101111110101001001100011001111111100111",
    "0000000000000111000110101111110101001001100011001111111101",
    "000000000000011100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000001110001101011111101010010011000110011111111001110",
    "00000000000001110001101011111101010010011000110011111111001111",
    "0000000000000111000110101111110101001001100011001111111101",
    "0000000000000111000110101111110101001001100011001111111110",
    "0000000000000111000110101111110101001001100011001111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
    "000000000000011100011010111111010100100110001100111111110011101",
    "000000000000011100011010111111010100100110001100111111110011110",
    "000000000000011100011010111111010100100110001100111111110011111",
    "000000000000011100011010111111010100100110001100111111110100",
    "000000000000011100011010111111010100100110001100111111110101",
    "000000000000011100011010111111010100100110001100111111110110",
    "000000000000011100011010111111010100100110001100111111110111",
    "000000000000011100011010111111010100100110001100111111111",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000111000110101111110101001001100011001111111100111",
    "0000000000000111000110101111110101001001100011001111111101",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
    "000000000000011100011010111111010100100110001100111111110011101",
    "000000000000011100011010111111010100100110001100111111110011110",
    "000000000000011100011010111111010100100110001100111111110011111",
    "000000000000011100011010111111010100100110001100111111110100",
    "000000000000011100011010111111010100100110001100111111110101",
    "000000000000011100011010111111010100100110001100111111110110",
    "000000000000011100011010111111010100100110001100111111110111",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000",
    "111000110101111110101001001100011010000010",
    "111000110101111110101001001100011010000011000",
    "1110001101011111101010010011000110100000110010",
    "11100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
    "111000110101111110101001001100011010000000",
    "111000110101111110101001001100011010000001",
    "111000110101111110101001001100011010000010",
    "1110001101011111101010010011000110100000110000",
    "1110001101011111101010010011000110100000110001",
    "1110001101011111101010010011000110100000110010",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "111000110101111110101001001100011001111111110",
    "111000110101111110101001001100011001111111111",
    "111000110101111110101001001100011010000000",
    "111000110101111110101001001100011010000001",
    "111000110101111110101001001100011010000010",
    "111000110101111110101001001100011010000011000",
    "111000110101111110101001001100011010000011001000",
    "111000110101111110101001001100011010000011001001",
    "111000110101111110101001001100011010000011001010",
    "111000110101111110101001001100011010000011001011",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101000",
    "111000110101111110101001001100011001111111101001",
    "111000110101111110101001001100011001111111101010",
    "111000110101111110101001001100011001111111101011",
    "111000110101111110101001001100011001111111101100",
    "111000110101111110101001001100011001111111101101",
    "111000110101111110101001001100011001111111101110",
    "111000110101111110101001001100011001111111101111",
    "11100011010111111010100100110001100111111111",
    "11100011010111111010100100110001101000000000",
    "11100011010111111010100100110001101000000001",
    "11100011010111111010100100110001101000000010",
    "11100011010111111010100100110001101000000011",
    "11100011010111111010100100110001101000000100",
    "11100011010111111010100100110001101000000101",
    "11100011010111111010100100110001101000000110",
    "11100011010111111010100100110001101000000111",
    "11100011010111111010100100110001101000001000",
    "11100011010111111010100100110001101000001001",
    "11100011010111111010100100110001101000001010",
    "11100011010111111010100100110001101000001011",
    "111000110101111110101001001100011010000011000000",
    "111000110101111110101001001100011010000011000001",
    "111000110101111110101001001100011010000011000010",
    "111000110101111110101001001100011010000011000011",
    "111000110101111110101001001100011010000011000100",
    "111000110101111110101001001100011010000011000101",
    "111000110101111110101001001100011010000011000110",
    "111000110101111110101001001100011010000011000111",
    "111000110101111110101001001100011010000011001000",
    "111000110101111110101001001100011010000011001001",
    "111000110101111110101001001100011010000011001010",
    "111000110101111110101001001100011010000011001011",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000111000110101111110101001001100011001111111100111",
    "0000000000000111000110101111110101001001100011001111111101",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101000000",
    "0000000000000111000110101111110101001001100011010000010",
    "0000000000000111000110101111110101001001100011010000011000",
    "00000000000001110001101011111101010010011000110100000110010",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000001110001101011111101010010011000110011111111001110",
    "00000000000001110001101011111101010010011000110011111111001111",
    "0000000000000111000110101111110101001001100011001111111101",
    "0000000000000111000110101111110101001001100011001111111110",
    "0000000000000111000110101111110101001001100011001111111111",
    "000000000000011100011010111111010100100110001101000000",
    "00000000000001110001101011111101010010011000110100000100",
    "00000000000001110001101011111101010010011000110100000101",
    "0000000000000111000110101111110101001001100011010000011000",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
    "000000000000011100011010111111010100100110001100111111110011101",
    "000000000000011100011010111111010100100110001100111111110011110",
    "000000000000011100011010111111010100100110001100111111110011111",
    "000000000000011100011010111111010100100110001100111111110100",
    "000000000000011100011010111111010100100110001100111111110101",
    "000000000000011100011010111111010100100110001100111111110110",
    "000000000000011100011010111111010100100110001100111111110111",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001000",
    "000000000000011100011010111111010100100110001101000001001",
    "000000000000011100011010111111010100100110001101000001010",
    "000000000000011100011010111111010100100110001101000001011",
    "000000000000011100011010111111010100100110001101000001100000",
    "000000000000011100011010111111010100100110001101000001100001",
    "000000000000011100011010111111010100100110001101000001100010",
    "000000000000011100011010111111010100100110001101000001100011",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "111000110101111110101001001100011001111111101",
    "11100011010111111010100100110001100111111111",
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011001111111100111",
    "1110001101011111101010010011000110011111111010",
    "1110001101011111101010010011000110011111111011",
    "11100011010111111010100100110001100111111111",
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000111000110101111110101001001100011001111111100111",
    "0000000000000111000110101111110101001001100011001111111101",
    "000000000000011100011010111111010100100110001100111111111",
    "00000000000001110001101011111101010010011000110100000",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
    "000000000000011100011010111111010100100110001100111111110011101",
    "000000000000011100011010111111010100100110001100111111110011110",
    "000000000000011100011010111111010100100110001100111111110011111",
    "000000000000011100011010111111010100100110001100111111110100",
    "000000000000011100011010111111010100100110001100111111110101",
    "000000000000011100011010111111010100100110001100111111110110",
    "000000000000011100011010111111010100100110001100111111110111",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000111000110101111110101001001100011001111111100111",
    "0000000000000111000110101111110101001001100011001111111101",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101",
    "00000000000001110001101011111101010010011000111",
    "00000000000001110001101011111101010010011001",
    "0000000000000111000110101111110101001001101",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "00000000000001110001101011111101010011",
    "000000000000011100011010111111010101",
    "00000000000001110001101011111101011",
    "000000000000011100011010111111011",
    "00000000000001110001101011111110000",
    "0000000000000111000110101111111000100",
    "0000000000000111000110101111111000101000",
    "00000000000001110001101011111110001010010",
    "000000000000011100011010111111100010100110000",
    "0000000000000111000110101111111000101001100010",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = -100,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111110011100",
    "000000000000011100011010111111010100100110001100111111110011101",
    "000000000000011100011010111111010100100110001100111111110011110",
    "000000000000011100011010111111010100100110001100111111110011111",
    "000000000000011100011010111111010100100110001100111111110100",
    "000000000000011100011010111111010100100110001100111111110101",
    "000000000000011100011010111111010100100110001100111111110110",
    "000000000000011100011010111111010100100110001100111111110111",
    "000000000000011100011010111111010100100110001100111111111",
    "000000000000011100011010111111010100100110001101",
    "000000000000011100011010111111010100100110001110",
    "000000000000011100011010111111010100100110001111",
    "000000000000011100011010111111010100100110010",
    "000000000000011100011010111111010100100110011",
    "000000000000011100011010111111010100100110100",
    "000000000000011100011010111111010100100110101",
    "000000000000011100011010111111010100100110110",
    "000000000000011100011010111111010100100110111",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "000000000000011100011010111111010100110",
    "000000000000011100011010111111010100111",
    "000000000000011100011010111111010101",
    "000000000000011100011010111111010110",
    "000000000000011100011010111111010111",
    "000000000000011100011010111111011",
    "000000000000011100011010111111100000",
    "000000000000011100011010111111100001",
    "000000000000011100011010111111100010000",
    "000000000000011100011010111111100010001",
    "000000000000011100011010111111100010010",
    "000000000000011100011010111111100010011",
    "000000000000011100011010111111100010100000",
    "000000000000011100011010111111100010100001",
    "000000000000011100011010111111100010100010",
    "000000000000011100011010111111100010100011",
    "000000000000011100011010111111100010100100",
    "000000000000011100011010111111100010100101",
    "000000000000011100011010111111100010100110000",
    "000000000000011100011010111111100010100110001000",
    "000000000000011100011010111111100010100110001001",
    "000000000000011100011010111111100010100110001010",
    "000000000000011100011010111111100010100110001011",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = -1 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "0",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = -1,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "root",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "0000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "0000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "11100011010111111010100100110001101000000",
    "111000110101111110101001001100011010000010",
    "111000110101111110101001001100011010000011000",
    "1110001101011111101010010011000110100000110010",
    "11100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "111000110101111110101001001100011010000000",
    "111000110101111110101001001100011010000001",
    "111000110101111110101001001100011010000010",
    "1110001101011111101010010011000110100000110000",
    "1110001101011111101010010011000110100000110001",
    "1110001101011111101010010011000110100000110010",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101000000",
    "0000000000000111000110101111110101001001100011010000010",
    "0000000000000111000110101111110101001001100011010000011000",
    "00000000000001110001101011111101010010011000110100000110010",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001000",
    "000000000000011100011010111111010100100110001101000001001",
    "000000000000011100011010111111010100100110001101000001010",
    "000000000000011100011010111111010100100110001101000001011",
    "000000000000011100011010111111010100100110001101000001100000",
    "000000000000011100011010111111010100100110001101000001100001",
    "000000000000011100011010111111010100100110001101000001100010",
    "000000000000011100011010111111010100100110001101000001100011",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00",
    "010",
    "011000",
    "0110010",
    "01100110",
    "01100111000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000000000000000000011000",
    "00000000000000000000000000000000000000000000000000000110010",
    "000000000000000000000000000000000000000000000000000001100110",
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000001000",
    "000000000000000000000000000000000000000000000000000001001",
    "000000000000000000000000000000000000000000000000000001010",
    "000000000000000000000000000000000000000000000000000001011",
    "000000000000000000000000000000000000000000000000000001100000",
    "000000000000000000000000000000000000000000000000000001100001",
    "000000000000000000000000000000000000000000000000000001100010",
    "000000000000000000000000000000000000000000000000000001100011",
    "000000000000000000000000000000000000000000000000000001100100",
    "000000000000000000000000000000000000000000000000000001100101",
    "000000000000000000000000000000000000000000000000000001100110",
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001100111111111111111",
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "00000000000001110001101011111101010010011000110100000",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "0",
    "1000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00",
    "01",
    "1000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000001000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000001000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000010000000000",
    "000000000000000000000000000000000000000000000000000010000000001",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101",
    "00000000000001110001101011111101010010011000111",
    "00000000000001110001101011111101010010011001",
    "0000000000000111000110101111110101001001101",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "00000000000001110001101011111101010011",
    "000000000000011100011010111111010101",
    "00000000000001110001101011111101011",
    "000000000000011100011010111111011",
    "00000000000001110001101011111110000",
    "0000000000000111000110101111111000100",
    "0000000000000111000110101111111000101000",
    "00000000000001110001101011111110001010010",
    "000000000000011100011010111111100010100110000",
    "0000000000000111000110101111111000101001100010",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001100111111111111111",
    "000000000000011100011010111111010100100110001101",
    "000000000000011100011010111111010100100110001110",
    "000000000000011100011010111111010100100110001111",
    "000000000000011100011010111111010100100110010",
    "000000000000011100011010111111010100100110011",
    "000000000000011100011010111111010100100110100",
    "000000000000011100011010111111010100100110101",
    "000000000000011100011010111111010100100110110",
    "000000000000011100011010111111010100100110111",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "000000000000011100011010111111010100110",
    "000000000000011100011010111111010100111",
    "000000000000011100011010111111010101",
    "000000000000011100011010111111010110",
    "000000000000011100011010111111010111",
    "000000000000011100011010111111011",
    "000000000000011100011010111111100000",
    "000000000000011100011010111111100001",
    "000000000000011100011010111111100010000",
    "000000000000011100011010111111100010001",
    "000000000000011100011010111111100010010",
    "000000000000011100011010111111100010011",
    "000000000000011100011010111111100010100000",
    "000000000000011100011010111111100010100001",
    "000000000000011100011010111111100010100010",
    "000000000000011100011010111111100010100011",
    "000000000000011100011010111111100010100100",
    "000000000000011100011010111111100010100101",
    "000000000000011100011010111111100010100110000",
    "000000000000011100011010111111100010100110001000",
    "000000000000011100011010111111100010100110001001",
    "000000000000011100011010111111100010100110001010",
    "000000000000011100011010111111100010100110001011",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "0000000000000000000000000000000000",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "000000000000000000000000000000001100",
    "000000000000000000000000000000001101",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = -1,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "000000000000000000000000000000001000",
    "000000000000000000000000000000001001",
    "000000000000000000000000000000001010",
    "000000000000000000000000000000001011",
    "000000000000000000000000000000001100",
    "000000000000000000000000000000001101",
    "000000000000000000000000000000001110000000000000000000000000000",
    "000000000000000000000000000000001110000000000000000000000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 0 },
  .expectMincoverStrings = {
    "1",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 0,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000",
    "111000110101111110101001001100011010000010",
    "111000110101111110101001001100011010000011000",
    "1110001101011111101010010011000110100000110010",
    "11100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011010000000",
    "111000110101111110101001001100011010000001",
    "111000110101111110101001001100011010000010",
    "1110001101011111101010010011000110100000110000",
    "1110001101011111101010010011000110100000110001",
    "1110001101011111101010010011000110100000110010",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "111000110101111110101001001100011010000000",
    "111000110101111110101001001100011010000001",
    "111000110101111110101001001100011010000010",
    "111000110101111110101001001100011010000011000",
    "111000110101111110101001001100011010000011001000",
    "111000110101111110101001001100011010000011001001",
    "111000110101111110101001001100011010000011001010",
    "111000110101111110101001001100011010000011001011",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000000000",
    "11100011010111111010100100110001101000000001",
    "11100011010111111010100100110001101000000010",
    "11100011010111111010100100110001101000000011",
    "11100011010111111010100100110001101000000100",
    "11100011010111111010100100110001101000000101",
    "11100011010111111010100100110001101000000110",
    "11100011010111111010100100110001101000000111",
    "11100011010111111010100100110001101000001000",
    "11100011010111111010100100110001101000001001",
    "11100011010111111010100100110001101000001010",
    "11100011010111111010100100110001101000001011",
    "111000110101111110101001001100011010000011000000",
    "111000110101111110101001001100011010000011000001",
    "111000110101111110101001001100011010000011000010",
    "111000110101111110101001001100011010000011000011",
    "111000110101111110101001001100011010000011000100",
    "111000110101111110101001001100011010000011000101",
    "111000110101111110101001001100011010000011000110",
    "111000110101111110101001001100011010000011000111",
    "111000110101111110101001001100011010000011001000",
    "111000110101111110101001001100011010000011001001",
    "111000110101111110101001001100011010000011001010",
    "111000110101111110101001001100011010000011001011",
    "111000110101111110101001001100011010000011001100",
    "111000110101111110101001001100011010000011001101",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000",
    "0000000000000111000110101111110101001001100011010000010",
    "0000000000000111000110101111110101001001100011010000011000",
    "00000000000001110001101011111101010010011000110100000110010",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000",
    "00000000000001110001101011111101010010011000110100000100",
    "00000000000001110001101011111101010010011000110100000101",
    "0000000000000111000110101111110101001001100011010000011000",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001000",
    "000000000000011100011010111111010100100110001101000001001",
    "000000000000011100011010111111010100100110001101000001010",
    "000000000000011100011010111111010100100110001101000001011",
    "000000000000011100011010111111010100100110001101000001100000",
    "000000000000011100011010111111010100100110001101000001100001",
    "000000000000011100011010111111010100100110001101000001100010",
    "000000000000011100011010111111010100100110001101000001100011",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000001110001101011111101010010011000110100000000",
    "00000000000001110001101011111101010010011000110100000001",
    "00000000000001110001101011111101010010011000110100000010",
    "00000000000001110001101011111101010010011000110100000011",
    "00000000000001110001101011111101010010011000110100000100",
    "00000000000001110001101011111101010010011000110100000101",
    "000000000000011100011010111111010100100110001101000001100000",
    "000000000000011100011010111111010100100110001101000001100001",
    "000000000000011100011010111111010100100110001101000001100010",
    "000000000000011100011010111111010100100110001101000001100011",
    "000000000000011100011010111111010100100110001101000001100100",
    "000000000000011100011010111111010100100110001101000001100101",
    "000000000000011100011010111111010100100110001101000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "010",
    "011000",
    "0110010",
    "01100110",
    "01100111000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000000000000000000011000",
    "00000000000000000000000000000000000000000000000000000110010",
    "000000000000000000000000000000000000000000000000000001100110",
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000001000",
    "000000000000000000000000000000000000000000000000000001001",
    "000000000000000000000000000000000000000000000000000001010",
    "000000000000000000000000000000000000000000000000000001011",
    "000000000000000000000000000000000000000000000000000001100000",
    "000000000000000000000000000000000000000000000000000001100001",
    "000000000000000000000000000000000000000000000000000001100010",
    "000000000000000000000000000000000000000000000000000001100011",
    "000000000000000000000000000000000000000000000000000001100100",
    "000000000000000000000000000000000000000000000000000001100101",
    "000000000000000000000000000000000000000000000000000001100110",
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00",
    "010",
    "011000",
    "0110010",
    "01100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00",
    "0100",
    "0101",
    "011000",
    "01100100",
    "01100101",
    "01100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "000",
    "001",
    "010",
    "011000",
    "011001000",
    "011001001",
    "011001010",
    "011001011",
    "011001100",
    "011001101",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "0000",
    "0001",
    "0010",
    "0011",
    "0100",
    "0101",
    "01100000",
    "01100001",
    "01100010",
    "01100011",
    "01100100",
    "01100101",
    "01100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "0000000000000000000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000000000000000000011000",
    "00000000000000000000000000000000000000000000000000000110010",
    "000000000000000000000000000000000000000000000000000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000100",
    "00000000000000000000000000000000000000000000000000000101",
    "0000000000000000000000000000000000000000000000000000011000",
    "000000000000000000000000000000000000000000000000000001100100",
    "000000000000000000000000000000000000000000000000000001100101",
    "000000000000000000000000000000000000000000000000000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000001000",
    "000000000000000000000000000000000000000000000000000001001",
    "000000000000000000000000000000000000000000000000000001010",
    "000000000000000000000000000000000000000000000000000001011",
    "000000000000000000000000000000000000000000000000000001100000",
    "000000000000000000000000000000000000000000000000000001100001",
    "000000000000000000000000000000000000000000000000000001100010",
    "000000000000000000000000000000000000000000000000000001100011",
    "000000000000000000000000000000000000000000000000000001100100",
    "000000000000000000000000000000000000000000000000000001100101",
    "000000000000000000000000000000000000000000000000000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 4,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000000000",
    "00000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000010",
    "00000000000000000000000000000000000000000000000000000011",
    "00000000000000000000000000000000000000000000000000000100",
    "00000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000001100000",
    "000000000000000000000000000000000000000000000000000001100001",
    "000000000000000000000000000000000000000000000000000001100010",
    "000000000000000000000000000000000000000000000000000001100011",
    "000000000000000000000000000000000000000000000000000001100100",
    "000000000000000000000000000000000000000000000000000001100101",
    "000000000000000000000000000000000000000000000000000001100110",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "1110001101011111101010010011000110100000",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000001110001101011111101010010011000110100000",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000000",
    "000000000000011100011010111111010100100110001101000001",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "00000000001",
    "0000000001",
    "000000001",
    "00000001",
    "0000001",
    "000001",
    "00001",
    "0001",
    "001",
    "01",
    "1000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000001000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000000000000100",
    "000000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000000110",
    "000000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000010000000000",
    "000000000000000000000000000000000000000000000000000010000000001",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "0",
    "10000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000",
    "000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101",
    "00000000000001110001101011111101010010011000111",
    "00000000000001110001101011111101010010011001",
    "0000000000000111000110101111110101001001101",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "00000000000001110001101011111101010011",
    "000000000000011100011010111111010101",
    "00000000000001110001101011111101011",
    "000000000000011100011010111111011",
    "00000000000001110001101011111110000",
    "0000000000000111000110101111111000100",
    "0000000000000111000110101111111000101000",
    "00000000000001110001101011111110001010010",
    "000000000000011100011010111111100010100110000",
    "0000000000000111000110101111111000101001100010",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101",
    "000000000000011100011010111111010100100110001110",
    "000000000000011100011010111111010100100110001111",
    "000000000000011100011010111111010100100110010",
    "000000000000011100011010111111010100100110011",
    "000000000000011100011010111111010100100110100",
    "000000000000011100011010111111010100100110101",
    "000000000000011100011010111111010100100110110",
    "000000000000011100011010111111010100100110111",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "000000000000011100011010111111010100110",
    "000000000000011100011010111111010100111",
    "000000000000011100011010111111010101",
    "000000000000011100011010111111010110",
    "000000000000011100011010111111010111",
    "000000000000011100011010111111011",
    "000000000000011100011010111111100000",
    "000000000000011100011010111111100001",
    "000000000000011100011010111111100010000",
    "000000000000011100011010111111100010001",
    "000000000000011100011010111111100010010",
    "000000000000011100011010111111100010011",
    "000000000000011100011010111111100010100000",
    "000000000000011100011010111111100010100001",
    "000000000000011100011010111111100010100010",
    "000000000000011100011010111111100010100011",
    "000000000000011100011010111111100010100100",
    "000000000000011100011010111111100010100101",
    "000000000000011100011010111111100010100110000",
    "000000000000011100011010111111100010100110001000",
    "000000000000011100011010111111100010100110001001",
    "000000000000011100011010111111100010100110001010",
    "000000000000011100011010111111100010100110001011",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000001",
    "000000000000000000000000000000000000001",
    "00000000000000000000000000000000000001",
    "0000000000000000000000000000000000001",
    "000000000000000000000000000000000001",
    "00000000000000000000000000000000001",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 0,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "000000000000000000000000000000001000",
    "000000000000000000000000000000001001",
    "000000000000000000000000000000001010",
    "000000000000000000000000000000001011",
    "000000000000000000000000000000001100",
    "000000000000000000000000000000001101",
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "01100111000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "01100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 823,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100110111",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000001100110111",
    "11100011010111111010100100110001101000001100111",
    "11100011010111111010100100110001101000001101",
    "1110001101011111101010010011000110100000111",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000001100110111",
    "111000110101111110101001001100011010000011001110",
    "111000110101111110101001001100011010000011001111",
    "11100011010111111010100100110001101000001101",
    "11100011010111111010100100110001101000001110",
    "11100011010111111010100100110001101000001111",
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000001100110111",
    "000000000000011100011010111111010100100110001101000001100111",
    "000000000000011100011010111111010100100110001101000001101",
    "00000000000001110001101011111101010010011000110100000111",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000001100110111",
    "000000000000011100011010111111010100100110001101000001100111",
    "000000000000011100011010111111010100100110001101000001101",
    "000000000000011100011010111111010100100110001101000001110",
    "000000000000011100011010111111010100100110001101000001111",
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "01100111",
    "01101",
    "0111",
    "1000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "01100111",
    "011010",
    "011011",
    "0111",
    "1000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "00000000000000000000000000000000000000000000000000000111",
    "00000000000000000000000000000000000000000000000000001000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111",
    "0000000000000000000000000000000000000000000000000000011010",
    "0000000000000000000000000000000000000000000000000000011011",
    "00000000000000000000000000000000000000000000000000000111",
    "00000000000000000000000000000000000000000000000000001000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "000000000000000000000000000000000000000000000000000001110",
    "000000000000000000000000000000000000000000000000000001111",
    "000000000000000000000000000000000000000000000000000010000000000",
    "000000000000000000000000000000000000000000000000000010000000001",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "01100110111",
    "01100111",
    "01101",
    "0111",
    "10000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100110111",
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "00000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100110111",
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "000000000000000000000000000000000000000000000000000001110",
    "000000000000000000000000000000000000000000000000000001111",
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000001100110111",
    "000000000000011100011010111111010100100110001101000001100111",
    "000000000000011100011010111111010100100110001101000001101",
    "00000000000001110001101011111101010010011000110100000111",
    "00000000000001110001101011111101010010011000110100001",
    "0000000000000111000110101111110101001001100011010001",
    "000000000000011100011010111111010100100110001101001",
    "00000000000001110001101011111101010010011000110101",
    "0000000000000111000110101111110101001001100011011",
    "00000000000001110001101011111101010010011000111",
    "00000000000001110001101011111101010010011001",
    "0000000000000111000110101111110101001001101",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "00000000000001110001101011111101010011",
    "000000000000011100011010111111010101",
    "00000000000001110001101011111101011",
    "000000000000011100011010111111011",
    "00000000000001110001101011111110000",
    "0000000000000111000110101111111000100",
    "0000000000000111000110101111111000101000",
    "00000000000001110001101011111110001010010",
    "000000000000011100011010111111100010100110000",
    "0000000000000111000110101111111000101001100010",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "00000000000000000000000000000000000000000000000000000111",
    "00000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000001",
    "000000000000000000000000000000000000001",
    "00000000000000000000000000000000000001",
    "0000000000000000000000000000000000001",
    "000000000000000000000000000000000001",
    "00000000000000000000000000000000001",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100111",
    "0000000000000000000000000000000000000000000000000000011010",
    "0000000000000000000000000000000000000000000000000000011011",
    "00000000000000000000000000000000000000000000000000000111",
    "000000000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000000000011",
    "0000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000000000000000011",
    "00000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000010",
    "00000000000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000000000011",
    "0000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000000000011",
    "00000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000010",
    "00000000000000000000000000000000000000000011",
    "000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000010",
    "000000000000000000000000000000000000000011",
    "0000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000010",
    "0000000000000000000000000000000000000011",
    "00000000000000000000000000000000000001",
    "00000000000000000000000000000000000010",
    "00000000000000000000000000000000000011",
    "000000000000000000000000000000000001",
    "000000000000000000000000000000000010",
    "000000000000000000000000000000000011",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "000000000000000000000000000000001100",
    "000000000000000000000000000000001101",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = 823,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000001100110111",
    "000000000000000000000000000000000000000000000000000001100111",
    "000000000000000000000000000000000000000000000000000001101",
    "00000000000000000000000000000000000000000000000000000111",
    "00000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000001",
    "000000000000000000000000000000000000001",
    "00000000000000000000000000000000000001",
    "0000000000000000000000000000000000001",
    "000000000000000000000000000000000001",
    "00000000000000000000000000000000001",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 2,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "11100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "10000000001",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000010000000001",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000010000000001",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 1024 },
  .expectMincoverStrings = {
    "10000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000010000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1024,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000001110001101011111101010010011000110100001",
    "0000000000000111000110101111110101001001100011010001",
    "000000000000011100011010111111010100100110001101001",
    "00000000000001110001101011111101010010011000110101",
    "0000000000000111000110101111110101001001100011011",
    "00000000000001110001101011111101010010011000111",
    "00000000000001110001101011111101010010011001",
    "0000000000000111000110101111110101001001101",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "00000000000001110001101011111101010011",
    "000000000000011100011010111111010101",
    "00000000000001110001101011111101011",
    "000000000000011100011010111111011",
    "00000000000001110001101011111110000",
    "0000000000000111000110101111111000100",
    "0000000000000111000110101111111000101000",
    "00000000000001110001101011111110001010010",
    "000000000000011100011010111111100010100110000",
    "0000000000000111000110101111111000101001100010",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111010100100110001101000010",
    "000000000000011100011010111111010100100110001101000011",
    "000000000000011100011010111111010100100110001101000100",
    "000000000000011100011010111111010100100110001101000101",
    "000000000000011100011010111111010100100110001101000110",
    "000000000000011100011010111111010100100110001101000111",
    "000000000000011100011010111111010100100110001101001",
    "000000000000011100011010111111010100100110001101010",
    "000000000000011100011010111111010100100110001101011",
    "000000000000011100011010111111010100100110001101100",
    "000000000000011100011010111111010100100110001101101",
    "000000000000011100011010111111010100100110001101110",
    "000000000000011100011010111111010100100110001101111",
    "000000000000011100011010111111010100100110001110",
    "000000000000011100011010111111010100100110001111",
    "000000000000011100011010111111010100100110010",
    "000000000000011100011010111111010100100110011",
    "000000000000011100011010111111010100100110100",
    "000000000000011100011010111111010100100110101",
    "000000000000011100011010111111010100100110110",
    "000000000000011100011010111111010100100110111",
    "000000000000011100011010111111010100100111",
    "000000000000011100011010111111010100101",
    "000000000000011100011010111111010100110",
    "000000000000011100011010111111010100111",
    "000000000000011100011010111111010101",
    "000000000000011100011010111111010110",
    "000000000000011100011010111111010111",
    "000000000000011100011010111111011",
    "000000000000011100011010111111100000",
    "000000000000011100011010111111100001",
    "000000000000011100011010111111100010000",
    "000000000000011100011010111111100010001",
    "000000000000011100011010111111100010010",
    "000000000000011100011010111111100010011",
    "000000000000011100011010111111100010100000",
    "000000000000011100011010111111100010100001",
    "000000000000011100011010111111100010100010",
    "000000000000011100011010111111100010100011",
    "000000000000011100011010111111100010100100",
    "000000000000011100011010111111100010100101",
    "000000000000011100011010111111100010100110000",
    "000000000000011100011010111111100010100110001000",
    "000000000000011100011010111111100010100110001001",
    "000000000000011100011010111111100010100110001010",
    "000000000000011100011010111111100010100110001011",
    "000000000000011100011010111111100010100110001100",
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000000000000000000000010000000001",
    "00000000000000000000000000000000000000000000000000001000000001",
    "0000000000000000000000000000000000000000000000000000100000001",
    "000000000000000000000000000000000000000000000000000010000001",
    "00000000000000000000000000000000000000000000000000001000001",
    "0000000000000000000000000000000000000000000000000000100001",
    "000000000000000000000000000000000000000000000000000010001",
    "00000000000000000000000000000000000000000000000000001001",
    "0000000000000000000000000000000000000000000000000000101",
    "000000000000000000000000000000000000000000000000000011",
    "0000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000001",
    "000000000000000000000000000000000000001",
    "00000000000000000000000000000000000001",
    "0000000000000000000000000000000000001",
    "000000000000000000000000000000000001",
    "00000000000000000000000000000000001",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "00000000000000000000000000000000111000000000000000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "00000000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000000001",
    "000000000000000000000000000000000000000001",
    "00000000000000000000000000000000000000001",
    "0000000000000000000000000000000000000001",
    "000000000000000000000000000000000000001",
    "00000000000000000000000000000000000001",
    "0000000000000000000000000000000000001",
    "000000000000000000000000000000000001",
    "00000000000000000000000000000000001",
    "0000000000000000000000000000000001",
    "0000000000000000000000000000000010",
    "00000000000000000000000000000000110",
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "0000000000000000000000000000000010",
    "000000000000000000000000000000001100",
    "0000000000000000000000000000000011010",
    "00000000000000000000000000000000110110",
    "000000000000000000000000000000001101110",
    "0000000000000000000000000000000011011110",
    "00000000000000000000000000000000110111110",
    "000000000000000000000000000000001101111110",
    "0000000000000000000000000000000011011111110",
    "00000000000000000000000000000000110111111110",
    "000000000000000000000000000000001101111111110",
    "0000000000000000000000000000000011011111111110",
    "00000000000000000000000000000000110111111111110",
    "000000000000000000000000000000001101111111111110",
    "0000000000000000000000000000000011011111111111110",
    "00000000000000000000000000000000110111111111111110",
    "000000000000000000000000000000001101111111111111110",
    "0000000000000000000000000000000011011111111111111110",
    "00000000000000000000000000000000110111111111111111110",
    "000000000000000000000000000000001101111111111111111110000000000",
  },
},
{
  .lowerBound = 1024,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000000",
    "000000000000000000000000000000001000",
    "000000000000000000000000000000001001",
    "000000000000000000000000000000001010",
    "000000000000000000000000000000001011",
    "000000000000000000000000000000001100",
    "000000000000000000000000000000001101000",
    "000000000000000000000000000000001101001",
    "000000000000000000000000000000001101010",
    "000000000000000000000000000000001101011",
    "000000000000000000000000000000001101100",
    "000000000000000000000000000000001101101",
    "000000000000000000000000000000001101110",
    "000000000000000000000000000000001101111000",
    "000000000000000000000000000000001101111001",
    "000000000000000000000000000000001101111010",
    "000000000000000000000000000000001101111011",
    "000000000000000000000000000000001101111100",
    "000000000000000000000000000000001101111101",
    "000000000000000000000000000000001101111110",
    "000000000000000000000000000000001101111111000",
    "000000000000000000000000000000001101111111001",
    "000000000000000000000000000000001101111111010",
    "000000000000000000000000000000001101111111011",
    "000000000000000000000000000000001101111111100",
    "000000000000000000000000000000001101111111101",
    "000000000000000000000000000000001101111111110",
    "000000000000000000000000000000001101111111111000",
    "000000000000000000000000000000001101111111111001",
    "000000000000000000000000000000001101111111111010",
    "000000000000000000000000000000001101111111111011",
    "000000000000000000000000000000001101111111111100",
    "000000000000000000000000000000001101111111111101",
    "000000000000000000000000000000001101111111111110",
    "000000000000000000000000000000001101111111111111000",
    "000000000000000000000000000000001101111111111111001",
    "000000000000000000000000000000001101111111111111010",
    "000000000000000000000000000000001101111111111111011",
    "000000000000000000000000000000001101111111111111100",
    "000000000000000000000000000000001101111111111111101",
    "000000000000000000000000000000001101111111111111110",
    "000000000000000000000000000000001101111111111111111000",
    "000000000000000000000000000000001101111111111111111001",
    "000000000000000000000000000000001101111111111111111010",
    "000000000000000000000000000000001101111111111111111011",
    "000000000000000000000000000000001101111111111111111100",
    "000000000000000000000000000000001101111111111111111101",
    "000000000000000000000000000000001101111111111111111110000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1000000000000000 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000011100011010111111100010100110001101000000000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001110000000000000000000000000001",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = -1 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001110000000000000000000000000001",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 0 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001110000000000000000000000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 1,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001101111111111111111110000000000",
  },
},
{
  .lowerBound = 1879048192,
  .includeLowerBound = true,
  .upperBound = 1879048192,
  .includeUpperBound = true,
  .sparsity = 3,
  .min = { .set = true, .value = 1024 },
  .max = { .set = true, .value = 8070450532247928832 },
  .expectMincoverStrings = {
    "000000000000000000000000000000001101111111111111111110000000000",
  },
},
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/000077500000000000000000000000001521103432300232445ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-find-int32/000077500000000000000000000000001521103432300260675ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-find-int32/cmd.json000066400000000000000000000001621521103432300275240ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "encrypted": {
            "$numberInt": "123456"
        }
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-find-int32/encrypted-field-map.json000066400000000000000000000006011521103432300326100ustar00rootroot00000000000000{
   "db.test": {
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range"
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-find-int32/encrypted-payload.json000066400000000000000000000035161521103432300324130ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "$and": [
            {
                "encrypted": {
                    "$gte": {
                        "$binary": {
                            "base64": "DQEBAAADcGF5bG9hZACZAAAABGcAhQAAAAMwAH0AAAAFZAAgAAAAABVoe9wkcCyHpbOGCU+k9auwnEcS2CtNpRqiecg98ONpBXMAIAAAAABvqHHsxgHdi12FXVttMVYYwNK4R2zYUjeXQHAURNBvzwVsACAAAAAAfmp4QWZF7LiYXH6RYjf1kc6OpgRnCadlHki6cRrMilsAABJjbQAIAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAgAAAAAAAAAQdGYABgAAABBtbgAAAACAEG14AP///38A",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "encrypted": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    },
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range"
                        }
                    }
                ],
                "escCollection": "enxcol_.test.esc",
                "ecocCollection": "enxcol_.test.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-find-int32/mongocryptd-reply.json000066400000000000000000000043171521103432300324650ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "find": "test",
        "filter": {
            "$and": [
                {
                    "encrypted": {
                        "$gte": {
                            "$binary": {
                                "base64": "AxABAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAsQAAAANlZGdlc0luZm8AawAAABBsb3dlckJvdW5kAEDiAQAIbGJJbmNsdWRlZAABEHVwcGVyQm91bmQAQOIBAAh1YkluY2x1ZGVkAAEQdHJpbUZhY3RvcgAGAAAAEGluZGV4TWluAAAAAIAQaW5kZXhNYXgA////fwAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAAEmNtAAgAAAAAAAAAEnMAAgAAAAAAAAAA",
                                "subType": "06"
                            }
                        }
                    }
                },
                {
                    "encrypted": {
                        "$lte": {
                            "$binary": {
                                "base64": "A5oAAAAQdAACAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAOwAAABBwYXlsb2FkSWQAAAAAABBmaXJzdE9wZXJhdG9yAAIAAAAQc2Vjb25kT3BlcmF0b3IABAAAAAASY20ACAAAAAAAAAAScwACAAAAAAAAAAA=",
                                "subType": "06"
                            }
                        }
                    }
                }
            ]
        },
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range"
                            }
                        }
                    ]
                }
            }
        }
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-insert-int32/000077500000000000000000000000001521103432300264535ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-insert-int32/cmd.json000066400000000000000000000002721521103432300301120ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$numberInt": "123456"
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-insert-int32/encrypted-field-map.json000066400000000000000000000006011521103432300331740ustar00rootroot00000000000000{
   "db.test": {
      "fields": [
         {
            "keyId": {
               "$binary": {
                  "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                  "subType": "04"
               }
            },
            "path": "encrypted",
            "bsonType": "int",
            "queries": {
               "queryType": "range"
            }
         }
      ]
   }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-insert-int32/encrypted-payload.json000066400000000000000000000117131521103432300327750ustar00rootroot00000000000000{
    "insert": "test",
    "documents": [
        {
            "plainText": "sample",
            "encrypted": {
                "$binary": {
                    "base64": "C64LAAAFZAAgAAAAAFX/OCQuK7sgFC5OgvIzRF0iZF5yZhqqgVEw+BDk04D/BXMAIAAAAADf8ZGTnPqqur9tusELGqTbSNtoJgj8i071DXm7kQpcSgVwADEAAAAAAwECAwECAwECAwECAwECAz+VM8kdZjiqXJ2Kga/NSZuITHP/iMRHFXZ17XdXy4FMWwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAABI0VngSNJh2EjQSNFZ4kBIBAgMBAgMBAgMBAgMBAgMB978eymjOG8LzTgudRZqpbK4Tm1RK3tB9QMB9lTWKiy6Wqp7twzRA/ZbjQ69RKj60BWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawAGAAAAAAAAAARnACcKAAADMAC2AAAABWQAIAAAAABx4c6MPMR3Yyxzi0dTMjQ7UdRNgI8uP2LTi1vhGYnLMAVzACAAAAAAon13gVyXE18K3ylqp/ovYStGulVBaekS2mKVRFq7RQgFbAAgAAAAAH5qeEFmRey4mFx+kWI39ZHOjqYEZwmnZR5IunEazIpbBXAAMQAAAAACAwECAwECAwECAwECAwECy273HayEz3hgoMYG56e0sxacPePKwPiqCCS575wlTLMxAAMxALYAAAAFZAAgAAAAALhKoC9+IzmWByMV7+lxHqBz30ckyd4wjXTJIaxvSHGQBXMAIAAAAADOFYCxSi5cl0dHBBpG2MuoCMK4VW7bnHkT4W0b8/S9QAVsACAAAAAA2ed4R4wYD6DT0P+N6o3gDJPE0DjljbRAv5vme3jb42sFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgMucSLry7LOh6S3NFriDyboyFWjjO7jUCKQmfnXNTVgRlsAAzIAtgAAAAVkACAAAAAA6d+xdtGaB4rPWNOM/dfLBsjgszgJ3nZIu6NMY7PGleMFcwAgAAAAAOoLJCMXdUXT5CP7ddF7NkbkHzTWmAJdEQnP3xG/VufdBWwAIAAAAADH9XHyaSTZUZNt8Z6XnKi7viLmxyO8TBuoRmwhAOkIogVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAViMN5HI9n2E2jIfq+NIBjTZq9V1WKXzuz6coSb44HleywADMwC2AAAABWQAIAAAAABHc/GW0GvNFnIAzns/1yI0xGl/Ir5cH5BLQckH+kV3fAVzACAAAAAAQmiJLIWjabGJeih4GboT6t/cn3SFOjMUgUpJrmQ8XUEFbAAgAAAAAOZbKzIqF/xRxwrHvwiWIdNZqj2l78DCYswNswCdUkc9BXAAMQAAAAACAwECAwECAwECAwECAwECK3sJsHWwtZbjBccUWeeIOOIGGMIOkyKsUwxlBaKiVPowAAM0ALYAAAAFZAAgAAAAAOW+ccgZnspe3Zk0B15sNoxYs+++vkIhLYDssUWrpf+ZBXMAIAAAAAC13nmuhE1cSoIdkJleu6iHCYZARpCxvagwSrdjvgaRUgVsACAAAAAAIrk7klUCamSAp05f9qK6PqR041yRVCaJ7dctpSUm5/gFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgNVutv0BdHOWmHtoNn6bEXHyRFbnxCJcfOzMiOveMdMVFsAAzUAtgAAAAVkACAAAAAAHDzIz2zmH3ABRBNEt4lMKBvbFr/Isvn120UyOZX2fO0FcwAgAAAAAC38Cjx6SPJmI+8cIdItT/DAqdCxJaLNeqYyxzGMte4GBWwAIAAAAAB2mov70YYtzeH4imEKJhjQUoXg3o3I54oyLFTZgru74QVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAZ97GY6ly8oxHf74/+Aef4L9HTES5QVj0JFhuQbLA3CFywADNgC2AAAABWQAIAAAAAD0ugfk8nq6aFeln4+QLIhFyOkLafGcozN8MvybVLzaRgVzACAAAAAAC1ORUt0477aATkfBAN86Ky6TzUN4zYnGFdZw5VLJ3hcFbAAgAAAAAAs9x1SnVpMfNv5Bm1aXGwHmbbI9keWa9HRD35XuCBK5BXAAMQAAAAACAwECAwECAwECAwECAwECYkARzi0rM5HqMaitQIKh+RNJSvXzZJh+x5BcTpRX16wwAAM3ALYAAAAFZAAgAAAAANCNn0wJp9IH48jKhaXrMNEZxL+8bFLBDK4cdcRKisXwBXMAIAAAAAAwYLl1BiMdwdX0D4kOg6Ny/vAShkyLLpYLOj//l8nv8QVsACAAAAAADxH2z9GufFM6RT5+pgWycCbSSY0peGvRXeT/U6u25NEFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgPQBBsvh7+P0TYEP8mqVE4yPmcJX8yz4s2IQqszUQgy91sAAzgAtgAAAAVkACAAAAAApUDgJrXbR7rYptc8rNKDAMTJJgpWx3wJ5Ei6ExI4cKsFcwAgAAAAAFYLdRWwhup6c51dwxz7JtWZ9hiWTI45RNME1HAiNLTlBWwAIAAAAAAK6ZFHvQOghjlhXsLbZmhDLlW+nCV3NrZD9Ue7y3/0ngVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAeSMZqdvBdItTYy5HS7IFqekQvk1jCmX7uRXqkdlgipmywADOQC2AAAABWQAIAAAAACnNRIgrtUTrL9bvDzNfdzpGsdqBElHVWVpYyPXwSwrvgVzACAAAAAAjmBoFQHWGwST1BE0yaBiekIcYVsoUf/BSrLwO9UhtEsFbAAgAAAAAEe9aNvZUOhoa4IYjG0TUFY0Lbx6KYY//F0HqCDbN/u7BXAAMQAAAAACAwECAwECAwECAwECAwEC53PoifHFxyP5q/5Yif35qH/G5u2j+O55mPTckBO/vfAwAAMxMAC2AAAABWQAIAAAAADO9uLhaBzFY2pp5k2huL0y7Ia9bbX9Td+8jbnze8vEoAVzACAAAAAAqE4YrbpDF0KstI1C6t+5gafrNKpWWc0gzHNa1VbxPjgFbAAgAAAAABkNIn+3njU/Zxu3Jz1yp3hxtx8KveIfzSQx6uLAsQLYBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDSCq69zvfhVJPRL0CTghUwWd8L3PWYQF7TwvOGZAw4z5bAAMxMQC2AAAABWQAIAAAAABugPtcm/HV5MD8yyuw4bpcdO1+K8CkSbE/dhzK93fyJAVzACAAAAAAuKJP+IQVI2kYA02OeeHFQEmvuVVPFYtpD7nAcl4U8rYFbAAgAAAAAJRkWtjoKRlC5wNyXtyBUyPeH8hFa0fcZTw8R8alLt7QBXAAMQAAAAABAgMBAgMBAgMBAgMBAgMBCiVcSluWGz4mEqlQS9L1MnQbWPaPsiXDOOq+RRmibDXLAAMxMgC2AAAABWQAIAAAAAC6uNqCo6p+ohuqlioUo7bhj6L+0kJpFl3yLYfYhqbnrAVzACAAAAAAH6WDk481/wTTL/Y6t765GCYPH0niPXWzamTeZ6lEQkcFbAAgAAAAABhXLMOXrnco598ejjUHAkNGZR8eNVnDMWrAuQ16j2O/BXAAMQAAAAACAwECAwECAwECAwECAwECdrYDD38mIyO5UBlW9+MiyhvVmP9plGQLuCLyzG/aS/wwAAMxMwC2AAAABWQAIAAAAABn8GOJ0BUhqxsCMujadpEuMjQH/JqjdSe+d7ck4BJpBgVzACAAAAAA/+5+gxu0s/JHvMroY0hgg4/pNDl6+FbjkVVL4pp0wKAFbAAgAAAAAIZjTmkgXeFszdl7b+WoFTth6LSUyRYvg6eh5JiOaoBYBXAAMQAAAAADAQIDAQIDAQIDAQIDAQIDH4rc2ZooIeKkTPqox5+Nw09+L+D6wJq4Ei3fLly1HaZbAAASc3AAAgAAAAAAAAAQdGYABgAAABBtbgAAAACAEG14AP///38A",
                    "subType": "06"
                }
            }
        }
    ],
    "encryptionInformation": {
        "type": {
            "$numberInt": "1"
        },
        "schema": {
            "db.test": {
                "fields": [
                    {
                        "keyId": {
                            "$binary": {
                                "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                "subType": "04"
                            }
                        },
                        "path": "encrypted",
                        "bsonType": "int",
                        "queries": {
                            "queryType": "range"
                        }
                    }
                ],
                "escCollection": "enxcol_.test.esc",
                "ecocCollection": "enxcol_.test.ecoc"
            }
        }
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/auto-insert-int32/mongocryptd-reply.json000066400000000000000000000026301521103432300330450ustar00rootroot00000000000000{
    "hasEncryptionPlaceholders": true,
    "schemaRequiresEncryption": true,
    "result": {
        "insert": "test",
        "encryptionInformation": {
            "type": {
                "$numberInt": "1"
            },
            "schema": {
                "db.test": {
                    "fields": [
                        {
                            "keyId": {
                                "$binary": {
                                    "base64": "EjRWeBI0mHYSNBI0VniQEg==",
                                    "subType": "04"
                                }
                            },
                            "path": "encrypted",
                            "bsonType": "int",
                            "queries": {
                                "queryType": "range"
                            }
                        }
                    ]
                }
            }
        },
        "documents": [
            {
                "plainText": "sample",
                "encrypted": {
                    "$binary": {
                        "base64": "A30AAAAQdAABAAAAEGEAAwAAAAVraQAQAAAABBI0VngSNJh2EjQSNFZ4kBIFa3UAEAAAAAQSNFZ4EjSYdhI0EjRWeJASA3YAHgAAABB2AEDiAQAQbWluAAAAAIAQbWF4AP///38AEmNtAAgAAAAAAAAAEnMAAgAAAAAAAAAA",
                        "subType": "06"
                    }
                }
            }
        ]
    },
    "ok": {
        "$numberDouble": "1.0"
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32-defaults/000077500000000000000000000000001521103432300305455ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32-defaults/expected.json000066400000000000000000000027721521103432300332510ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DYECAAADcGF5bG9hZAAZAgAABGcABQIAAAMwAH0AAAAFZAAgAAAAANBGT3k4GTmoze5hCRukb6u82SvYtFyGp+AHjlySsjWOBXMAIAAAAAB9cQ+iwhaAuhOnOn/BFalNNBn3Tx1mHngGdjzKvNPfsgVsACAAAAAA5ZNdJZh+4XZcHqJMhqWcLUo4YkxCxPhhRU7UFru8pk8AAzEAfQAAAAVkACAAAAAAzrlE+7ZYgnH71G+SpzjCefAEj4qNkUQOfn0FUFuKWycFcwAgAAAAAKZDfaea7ZYGynYzTtP38WQAewOs6QD3NH4RO9XwuJG5BWwAIAAAAACpq6FrRDHryiZeM1B50z/fz/J5yK3fEqAlFj4E6KCdSwADMgB9AAAABWQAIAAAAABTX/+x7fmCvi5gAWgsy5xZowLqeEc/z1qE7Eh9QkpMmQVzACAAAAAAjOLpo8K1z0hnG5U/e+p5IVCfR23vd98ZDgqy00xqJuwFbAAgAAAAAFqIYZydW8nwOUai0Cbz6IKBHpkuCbTP/VsBNDG9j1wUAAMzAH0AAAAFZAAgAAAAADgmrmBDpEvWF++05l1otn4vG6Q+GoYpGgdV2EIGSYXaBXMAIAAAAAD2b6bl3/wn2zPllR9z8Kl96rDn8OwdHZuVhUC9sYjFngVsACAAAAAARtUrQP0hYeNlaA1/I+sENuShjFgWyxY3Jw9URdcLiJAAABJjbQABAAAAAAAAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAASc3AAAgAAAAAAAAAQdGYABgAAABBtbgAAAAAAEG14AIfWEgAA",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32-defaults/to-encrypt.json000066400000000000000000000005761521103432300335540ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberInt": "23"
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$numberInt": "35"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32/000077500000000000000000000000001521103432300267405ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32/expected.json000066400000000000000000000035221521103432300314360ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$binary": {
                            "base64": "DYEDAAADcGF5bG9hZAAZAwAABGcABQMAAAMwAH0AAAAFZAAgAAAAANBGT3k4GTmoze5hCRukb6u82SvYtFyGp+AHjlySsjWOBXMAIAAAAAB9cQ+iwhaAuhOnOn/BFalNNBn3Tx1mHngGdjzKvNPfsgVsACAAAAAA5ZNdJZh+4XZcHqJMhqWcLUo4YkxCxPhhRU7UFru8pk8AAzEAfQAAAAVkACAAAAAAzrlE+7ZYgnH71G+SpzjCefAEj4qNkUQOfn0FUFuKWycFcwAgAAAAAKZDfaea7ZYGynYzTtP38WQAewOs6QD3NH4RO9XwuJG5BWwAIAAAAACpq6FrRDHryiZeM1B50z/fz/J5yK3fEqAlFj4E6KCdSwADMgB9AAAABWQAIAAAAAA9l5+QldlpI2N4eSiV+2l4tS9xZM+Y8kwcP+KhiEA8owVzACAAAAAAmdQflu77krN/4fFaB4la/ZWystLLh6nNv6xE9ErNZmMFbAAgAAAAACJbpdQ2++FVjMz+SdScn4lx3wsi1Y9gBcJozBfHuC6dAAMzAH0AAAAFZAAgAAAAAAhh9K4YcA+N8ACgj7B4smCyJmmJImd5oOUxjgM3T208BXMAIAAAAAB8LMl/6O3elk8pcE6o/WWd6HQKpQgr22l1tt2CpA+V7QVsACAAAAAAIMPlQ/IXn7oI3v3ltx2/1ql4oP/HRQPRjFyh/qTXANoAAzQAfQAAAAVkACAAAAAAXvLR5mSdJiSJ1DFwqIs6GYKvY+1bd0llpUdegS3y6tEFcwAgAAAAABAbG+tZXZ/z9VokQYAH23Kd7R4B9hjN0M3kWn/KBj8CBWwAIAAAAAACdaE9Ls6mFpsHzPBdVxPze4wHSHyPOW9xjCKEW3nYKgADNQB9AAAABWQAIAAAAAALlsQ6TaYfVYomaBlpbXtQcSyRgpJWDQVMh9YGaoJKwwVzACAAAAAA2OMU7sh4xR5O2482Idkiluu2zVJ2/OytctuyOxrgBWsFbAAgAAAAAA8vwTEAaqZNyh/H/6rELrEWzB/Q+JsF/NJ3zszdMjy+AAASY20AAQAAAAAAAAAAEHBheWxvYWRJZAAAAAAAEGZpcnN0T3BlcmF0b3IAAgAAABBzZWNvbmRPcGVyYXRvcgAEAAAAEnNwAAMAAAAAAAAAEHRmAAQAAAAQbW4AAAAAABBteACH1hIAAA==",
                            "subType": "06"
                        }
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$binary": {
                            "base64": "DTsAAAAQcGF5bG9hZElkAAAAAAAQZmlyc3RPcGVyYXRvcgACAAAAEHNlY29uZE9wZXJhdG9yAAQAAAAA",
                            "subType": "06"
                        }
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-find-int32/to-encrypt.json000066400000000000000000000005761521103432300317470ustar00rootroot00000000000000{
    "v": {
        "$and": [
            {
                "age": {
                    "$gte": {
                        "$numberInt": "23"
                    }
                }
            },
            {
                "age": {
                    "$lte": {
                        "$numberInt": "35"
                    }
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-double/000077500000000000000000000000001521103432300276375ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-double/expected.json000066400000000000000000000051061521103432300323350ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C2QHAAAFZAAgAAAAALhaV7AbX3sF2A4xhA3wIdk4qL/eFe7aZpAa1DnM2kSTBXMAIAAAAACOMuCmd823x6f7jsONJvKeL8wOiR68hhvu0dlvYoxS4QVwADEAAAAAAwECAwECAwECAwECAwECA25WQvz2USXXRAu+gynxH97vWxVQnoRKQG2pTaOkTY/nWwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0AAEAAAAFdgBQAAAAABI0VngSNJh2EjQSNFZ4kBIBAgMBAgMBAgMBAgMBAgMBwLKhtxuduoYSToLyr19j8+j+DHvJPS3XBVIHUMBjyS23HbcAYCgSw2kR1oBL6wTbBWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAiWNW98E+po41ct7A9qFXR6tOJ+C+TiNsYjrf6YBFdfoSawABAAAAAAAAAARnAM0FAAADMAC2AAAABWQAIAAAAAAQ4DndUIxNVcQbeCUb4fjmceM6kT/THUMUu7yx/7fbRQVzACAAAAAAtol+lj6hLJuanENCnlMN6Rmgp04cveQw9LviWaTz8sgFbAAgAAAAAAB5IMUmXk1/yctyMDORpFDfUeqesGwHjXgfKfd67hMUBXAAMQAAAAACAwECAwECAwECAwECAwEC35r+Cs6y8Lzw46wu3g6WOyR6IPiXFPWIJv3O8mJt+3MxAAMxALYAAAAFZAAgAAAAAFK5izd0ZDT135CYAGOt9lk4ifpTzhBe/8CumApPOXNkBXMAIAAAAACxQUZGVGEmG3BUwnAfRO5Mhttn1Tst3JMCZCR8JjXr3AVsACAAAAAAA4hleX/0UT5MKUq2TBhzQUmq5sMqlfkmFyO90Fvz5p8FcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgNRJeQc1f20C5Ok8jC7kwMMRkx8DLsVEMiBHLCw4PQ22lsAAzIAtgAAAAVkACAAAAAA7+qR6azxFnahvjFVFRZ5DtLNKgJfX0XOGCrReRAqWz4FcwAgAAAAACMYa0yh6yIIe5ZGxBOrCymO5XJU0mL5WY+Ra0cCZYPgBWwAIAAAAACDq4A9lSlrKYJqyoMst0vEkbTVzmG2MZOMOwVM9apfoAVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAZGfeP5+aBpfRYeiGiGYO1uzUZP3EsVX87jCFXBF0x1jywADMwC2AAAABWQAIAAAAAAkX6RCK3jBegSxwLBf5UXNvgDJVAueuSdKEhIvmPOS1QVzACAAAAAA8nErlZj5TxlcSxhyTglmvilPVSOdj4Fte4Ch3h8wBB4FbAAgAAAAAMiSVZlV86lAL4NPTAsp7+2y1GuICLjXV0vdY0/abTH7BXAAMQAAAAACAwECAwECAwECAwECAwECm2KrCWjqkz42NPceDlT9bBSV0pUWJpDVqcaNddmuDaUwAAM0ALYAAAAFZAAgAAAAAGZmOclRRXANFpclF+VP8e/onm/zQPS7QkoE3ptUtZRCBXMAIAAAAAALaMsYYNTs3MprY7WRchKAIdGDULAHlJbFjHlMJfxbtAVsACAAAAAAuLSbGn8yWQatCfxFq63aSG81xhL4VdNrq1S61AbihSMFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgPrDGlC4Uh+zCmbU/U1pf/A4UaYiTA/WM1G9O2A4z2GslsAAzUAtgAAAAVkACAAAAAAjbLuXNZXd0/yom0v+4ESfI8HOVcgQD3wCp6GBsMt6lQFcwAgAAAAAC58vjDehb7mDU0jUjS1qmyOB4jwx5Pq39dO5shO+rysBWwAIAAAAAA3YMxxqO2vRJlAMcjML86nucUGcnrd9npxzz7wrExzgwVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAZz7rYIBBoaxM1zHjAaGmh6zs2lTBzREdeAdmP8JTCIvywADNgC2AAAABWQAIAAAAABb2oc8Q6Q0skHDGDDtjx7bCZq0bUIi+ltESICucx0UCAVzACAAAAAAHh2HU8T2s/Tb+7MUMnpLjvFE/h1TUF/CdIIPjnOiKXIFbAAgAAAAAIszvCwG/imDGHPdZzXeiNqRYv4GRV8G5rvQYXWUkJRcBXAAMQAAAAACAwECAwECAwECAwECAwECdw4HzzTlb9OxhFx4cifQXMyeeavY+U56psQjJbU8IMkwAAM3ALYAAAAFZAAgAAAAABzHcTf5Q7tCDmM2iWj5hn8YE6CgYG5UbdZZZB74+42vBXMAIAAAAAAJKy5sUK1se93nLyJCo9D/vd2zwv5fbKeMI4B6RW047QVsACAAAAAA+KkgAsZn/tg1wsxNM0JTQouYEf9NDVjSBzbP+MkddVAFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgPpT4w20TH+az4XH2LmdD2/fUqoG35noPwPWxS2g6zl61sAABJzcAADAAAAAAAAABBwbgACAAAAEHRmAAQAAAABbW4AAAAAAAAAAAABbXgAAAAAAIfWMkEA",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-int32-defaults/000077500000000000000000000000001521103432300311315ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-int32-defaults/expected.json000066400000000000000000000054521521103432300336330ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "Cw0IAAAFZAAgAAAAALzbu+LfseFckQJg1Ylo/9nBhe7hxjFLXcZoirJJ4PlxBXMAIAAAAACOQJsWGHdNI2AXICcsv3q3rYFe0S/kvPGbUBQmmn5naQVwADEAAAAAAwECAwECAwECAwECAwECA24kOUyZ698zg+cQZ4hol/dtFkUIr9xwqhgogOpcv7pvWwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAABI0VngSNJh2EjQSNFZ4kBIBAgMBAgMBAgMBAgMBAgMB978eymjOG8LzTgudRZqpbK4Tm1RK3tB9QMB9lTWKiy6Wqp7twzRA/ZbjQ69RKj60BWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawABAAAAAAAAAARnAIYGAAADMAC2AAAABWQAIAAAAADDDSwgzG3b3hAnx/QM/8RGNV7qvbq+fAFpPF7RSN6W/AVzACAAAAAAjW2YlJV8aul4reu19mYd5nynwgS5giAc6AW7rTsXfEkFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAAACAwECAwECAwECAwECAwEC5H4YCGVvts4S0gTZtjuGNEF9RbIyKzGkOkOXBv2JdfIxAAMxALYAAAAFZAAgAAAAAEMbeAokq9+EI0VAu9kzSqGXKkgWTt7fcnJgp40qUzxhBXMAIAAAAADWfY3LqVm2iPeXvaZrJnHCNTSo7wfb7xEkPY8YN4MdEQVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgM2GS+RKMUkmBRnjebP8ZyC9aOzNofjI0qnRRvU8ULAF1sAAzIAtgAAAAVkACAAAAAAkJnPJd1pQ1WTaAnx7PevE42PCOA3+5GLN+PLOr84qYYFcwAgAAAAANfCPZjI5EvdhScmqaqtEf2Brjlo1gVe/xMEOPWfdB87BWwAIAAAAAA5zR9OnG49Xrn3grU7zBDon+oj+/1FUIOptLvwesWUvQVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAWVFLioXZ3OKuzbCd5ieIY+8GtjLFqLwVSRXRsLYwoG4ywADMwC2AAAABWQAIAAAAAD+Ea3yOx7sqUO7kUA8MvDlp0KbcR9S0CZn6yQqmZFRLwVzACAAAAAAIxyzUPpOb4HsMQRnbN3FH9sqGIjHsmKEdA0jTBCIjokFbAAgAAAAAIFEk9LC+wpdnt+iy3OTuzvc/W/4y7GWyhLN8YIFWpBZBXAAMQAAAAACAwECAwECAwECAwECAwECSg8zzApds6aGTusLLIBezebwnz5MG3M8pksP59YWhzIwAAM0ALYAAAAFZAAgAAAAAG8oo7+6uTa9nxjpXDpMofmlbRMxKSIGXsYgL5OGRE3ABXMAIAAAAAC81iL904byU1ACGkbBkxemVDQkxAyvPH+YBFmeYu5XCAVsACAAAAAABcm2+tbs0VSGFn9rDvWyvhuIRVDepRdoIvNa6TRv3zkFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgNcsoCnUhpgQ7PyKgZlRPrmlKM/HYyX8CQbfM1SpC+KDlsAAzUAtgAAAAVkACAAAAAAN1yutwxk1no+d0K7uwq121gaj6/APpV81NvMBSbh9QsFcwAgAAAAADotY5M6YW1+5/baxwt+IdliMsVZwd5Igr0coiqGiXNFBWwAIAAAAACLCFou5eC94e2pRXNjJ8oPkENRCDGdw9eZP7Kvgb2mCAVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAYiqcCHl4lUp2ec+GTlNEatfhiT6AXnmKIpP3B3BP+3GywADNgC2AAAABWQAIAAAAACxK8LFOEPUtM0uhqsrNd2VxlEAAboEfCcme14stervuwVzACAAAAAAF83o3sy5t9k6FW0br92D/gK0GtnqWYcTLkKDCZJJ/RwFbAAgAAAAAI+sI8AzBUAhDs2DSTh7StO5rgbgse0idO29VwOikD69BXAAMQAAAAACAwECAwECAwECAwECAwECft5oQjyqa/5QaoJ374AYLD9unW9h8Jar/ASvolTX9KcwAAM3ALYAAAAFZAAgAAAAACERygJgbMzz/kAGWZ7VGvByGUqPK9Ofs8jhN5zaldOFBXMAIAAAAACbP0SY0GDEKmYqCYatldSkTuzCS2xMCAJzfeRzqWyTswVsACAAAAAAx3b30GiSDqGKGibsVkC0wPEtLdUn5oieC/mR0h0PfooFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgN7W+bCUfxWOoXaOcYJQjnkjnvZkux0xFnwBXC/b61OtVsAAzgAtgAAAAVkACAAAAAAfuQBgBMx9gTdzh8VtYmaXdOUzVw8eIzZEk36kyGQZ8YFcwAgAAAAAOQaG1G8sbv0RuwezsEIxWfAXhOwpNnWxPrOueCWX9z5BWwAIAAAAACEBB9msU96lZNyT2Ory8KBNcfwHUZew4nuAMFzpU0kTwVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAVadCONjMoOjeP36EPM79RX96vITZH54bs2dx9fR6UJ6ywAAEnNwAAIAAAAAAAAAEHRmAAYAAAAQbW4AAAAAABBteACH1hIAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-int32/000077500000000000000000000000001521103432300273245ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/range-sends-cryptoParams/explicit-insert-int32/expected.json000066400000000000000000000041061521103432300320210ustar00rootroot00000000000000{
    "v": {
        "$binary": {
            "base64": "C+IFAAAFZAAgAAAAALzbu+LfseFckQJg1Ylo/9nBhe7hxjFLXcZoirJJ4PlxBXMAIAAAAACOQJsWGHdNI2AXICcsv3q3rYFe0S/kvPGbUBQmmn5naQVwADEAAAAAAwECAwECAwECAwECAwECA24kOUyZ698zg+cQZ4hol/dtFkUIr9xwqhgogOpcv7pvWwV1ABAAAAAEEjRWeBI0mHYSNBI0VniQEhB0ABAAAAAFdgBQAAAAABI0VngSNJh2EjQSNFZ4kBIBAgMBAgMBAgMBAgMBAgMB978eymjOG8LzTgudRZqpbK4Tm1RK3tB9QMB9lTWKiy6Wqp7twzRA/ZbjQ69RKj60BWUAIAAAAADrmnP3kS2GpCl+gdL2da90KHTkBX46iQ/sZRoj7uPz7AVsACAAAAAAlO36MaVLVRDFW6xrI+0UTkyQdFfSCEBPkZg8sFzuTJYSawABAAAAAAAAAARnAFsEAAADMAC2AAAABWQAIAAAAADDDSwgzG3b3hAnx/QM/8RGNV7qvbq+fAFpPF7RSN6W/AVzACAAAAAAjW2YlJV8aul4reu19mYd5nynwgS5giAc6AW7rTsXfEkFbAAgAAAAAA6eOZwxxhUyuExW2kjHcKJmNCrEPFQ9vS9YbrzP67TgBXAAMQAAAAACAwECAwECAwECAwECAwEC5H4YCGVvts4S0gTZtjuGNEF9RbIyKzGkOkOXBv2JdfIxAAMxALYAAAAFZAAgAAAAAEMbeAokq9+EI0VAu9kzSqGXKkgWTt7fcnJgp40qUzxhBXMAIAAAAADWfY3LqVm2iPeXvaZrJnHCNTSo7wfb7xEkPY8YN4MdEQVsACAAAAAAl7RsOFQfWxmV2cMZafTExrUJC43pyC/+L/peqek9Lf0FcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgM2GS+RKMUkmBRnjebP8ZyC9aOzNofjI0qnRRvU8ULAF1sAAzIAtgAAAAVkACAAAAAASubnYKt2+pegwuJwp7dxPEyRP9GvmJgFinGHU0f9CpIFcwAgAAAAAB/mpuAOeTGXLOVFgdx0eXJL51Zh2D5L2vZxwAgghlI+BWwAIAAAAAAQL6NCvBkhQ9ulCrwaNULOgv7Uhfp3ZhsiTHSOF5GySgVwADEAAAAAAQIDAQIDAQIDAQIDAQIDAa1htVLR+gnAEvShX+5HSQB2U7fCGJnlcMEivj9nMMy9ywADMwC2AAAABWQAIAAAAABvKKO/urk2vZ8Y6Vw6TKH5pW0TMSkiBl7GIC+ThkRNwAVzACAAAAAAvNYi/dOG8lNQAhpGwZMXplQ0JMQMrzx/mARZnmLuVwgFbAAgAAAAAAXJtvrW7NFUhhZ/aw71sr4biEVQ3qUXaCLzWuk0b985BXAAMQAAAAACAwECAwECAwECAwECAwEC1cWiYSOVLnQ6ffUqgc6MdGnuo3KHBi3HSkJ1NaRwXrMwAAM0ALYAAAAFZAAgAAAAAIMIjVhHf3tXJuPJ8mfbyeyXEutLfChd4KW3CK5y8gVqBXMAIAAAAABqPndgeb1Hei5AUcmBUdeO2gyoqqfq/2mf2oQEYClk3wVsACAAAAAAmeoE0x+Ci2vQGzzdW6c6SVIMBWeg7wUACzaaNQt+g/YFcAAxAAAAAAMBAgMBAgMBAgMBAgMBAgOKWtU6+CHVas2wYYklhjrOGpuzcyfSMzIcohDIpui52VsAAzUAtgAAAAVkACAAAAAAIRHKAmBszPP+QAZZntUa8HIZSo8r05+zyOE3nNqV04UFcwAgAAAAAJs/RJjQYMQqZioJhq2V1KRO7MJLbEwIAnN95HOpbJOzBWwAIAAAAADHdvfQaJIOoYoaJuxWQLTA8S0t1SfmiJ4L+ZHSHQ9+igVwADEAAAAAAQIDAQIDAQIDAQIDAQIDASm4VyoP4/x9WDvtWJ+m5NZzWCPorOumqEQumkTu2g0wywAAEnNwAAMAAAAAAAAAEHRmAAQAAAAQbW4AAAAAABBteACH1hIAAA==",
            "subType": "06"
        }
    }
}
libmongocrypt-1.19.0/test/data/rmd/000077500000000000000000000000001521103432300171365ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/rmd/key-document-a.json000066400000000000000000000020461521103432300226550ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
            "subType": "04"
        }
    },
    "keyAltNames": [
        "aaaaaaaaaaaaaaaa"
    ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1641016800000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1641016860000"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws"
    }
}
libmongocrypt-1.19.0/test/data/rmd/key-document-b.json000066400000000000000000000021461521103432300226570ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "YmJiYmJiYmJiYmJiYmJiYg==",
            "subType": "04"
        }
    },
    "keyAltNames": [
        "bbbbbbbbbbbbbbbb",
        "additionalAltName"
    ],
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEGkNTybTc7Eyif0f+qqE0lAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDB2j78AeuIQxcRh8cQIBEIB7vj9buHEaT7XHFIsKBJiyzZRmNnjvqMK5LSdzonKdx97jlqauvPvTDXSsdQDcspUs5oLrGmAXpbFResscxmbwZoKgUtWiuIOpeAcYuszCiMKt15s1WIMLDXUhYtfCmhRhekvgHnRAaK4HJMlGE+lKJXYI84E0b86Cd/g+",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1641020400000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1641020460000"
        }
    },
    "status": {
        "$numberInt": "1"
    },
    "masterKey": {
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
        "region": "us-east-1",
        "provider": "aws",
        "endpoint": "example.com"
    }
}
libmongocrypt-1.19.0/test/data/rmd/key-document-local.json000066400000000000000000000013741521103432300235320ustar00rootroot00000000000000{
    "_id": {
        "$binary": {
            "base64": "bG9jYWxrZXlsb2NhbGtleQ==",
            "subType": "04"
        }
    },
    "keyMaterial": {
        "$binary": {
            "base64": "+AiT2AHPKPBgmrcL8yHZ8IW5IwmLWOAbkUtino6yGc3x9PwIKQYHMN1+t4H9ZWU4YiHqbrChhpN/lq5uaeagWTTENYpb3iXaDAMPYWjmbRehDPv08+dQyh0DzkGVyskA/vfdFRqSPUKtUpMn8ilH/3KR8Yw77eYZKNwoHOApw1SLmDMr6vickWvtj32/2hTc1pZyMGlL28Ismizp5GWx8g==",
            "subType": "00"
        }
    },
    "creationDate": {
        "$date": {
            "$numberLong": "1641024000000"
        }
    },
    "updateDate": {
        "$date": {
            "$numberLong": "1641024060000"
        }
    },
    "status": {
        "$numberInt": "0"
    },
    "masterKey": {
        "provider": "local"
    }
}
libmongocrypt-1.19.0/test/data/rmd/kms-decrypt-reply-429.txt000066400000000000000000000005561521103432300236140ustar00rootroot00000000000000HTTP/1.1 429 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 233

{"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "Plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/test/data/rmd/kms-decrypt-reply-a.txt000066400000000000000000000006261521103432300235140ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 274

{"EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0","Plaintext":"YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"}
libmongocrypt-1.19.0/test/data/rmd/kms-decrypt-reply-b.txt000066400000000000000000000006261521103432300235150ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 274

{"EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0","Plaintext":"YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi"}
libmongocrypt-1.19.0/test/data/rmd/kms-encrypt-reply-a.txt000066400000000000000000000011751521103432300235260ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: 29f5ed0e-6362-408f-8b80-7400a6c6fdab
Content-Type: application/x-amz-json-1.1
Content-Length: 487
Connection: close

{"CiphertextBlob":"AQICAHjEc7oVE9nX494vQ37Y6VBvgh0L7Bjm5QzbH0Tf04D6bwEfH1MXhs2uh+eRM5zK/CA8AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDEv7o1A3VumZBUj7VgIBEIB7TKZcxPSasdzzAVme1Rn7DYTee+cCmhiUVQCQS4/Ae9Z870S+MhyiX8Ud1v9TAUbIGkeKahZoqq7UXpXsL7pvIghQPRxppkSNfKmT9kJhDy+u4pOOVmmvkCKueTW4FlBu2jik8vTO8/wp7tlRSA5BUcaqdEv5KWXAjgdt","EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d"}
libmongocrypt-1.19.0/test/data/rmd/kms-encrypt-reply-b.txt000066400000000000000000000011751521103432300235270ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: 9ff3f25e-5ae5-49c9-93eb-152ead31b5a7
Content-Type: application/x-amz-json-1.1
Content-Length: 487
Connection: close

{"CiphertextBlob":"AQICAHjEc7oVE9nX494vQ37Y6VBvgh0L7Bjm5QzbH0Tf04D6bwGbFDkmaxct2y/JL65t8FGKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDIhcdknWoFRHzSiAAwIBEIB7ubV6iGGWNjd/MeQT6QYTaJBZcmMUBLf/pvnjQFN/UzDZBFKNWpfSlHo4P5tGw/PoZPr4+wLMMoF2VvQCr6xWccngkd7mS7txQ4gbCx6rqI/UlRZ94IreLxJyb2kwrN62/jmlcnD1ERGItwmagzaVU42bzyocqlXt9c2r","EncryptionAlgorithm":"SYMMETRIC_DEFAULT","KeyId":"arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d"}
libmongocrypt-1.19.0/test/data/roundtrip/000077500000000000000000000000001521103432300204025ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/roundtrip/README.md000066400000000000000000000023741521103432300216670ustar00rootroot00000000000000# Roundtrip crypto test cases

Each file is a JSON array containing a set of documents with the following fields:

* `name` (required): This is a symbolic field used for identifying a failing test case in logs.
* `algo` (required): The name of the cipher and hmac to use during crypto operations. One of:
   * `AES-256-CBC/SHA-512-256` aka `FLE1`
   * `AES-256-CTR/SHA-256` aka `FLE2` with AEAD
   * `AES-256-CTR/NONE` aka `FLE2` with no MAC
* `key` (required): Either 32 or 96 octets of key material expressed as hexits
* `iv` (required): 16 octets of inintialization vector expressed as hexits
* `aad` (optional if MAC is NONE, otherwise required): Associated Data as hexits
* `plaintext` (required): Hexits representing data to encrypt.
* `ciphertext` (required): The expected ciphertext as hexits produced by the operation.
* `ignore_ciphertext_mismatch_on_encrypt` (optional): True if the encryption operation is expected to produce something other than the contents of `ciphertext`.  This is useful when focusing on the decrypt operation, such as in [`fle2aead-decrypt.json`](fle2aead-decrypt.json).
* `encrypt_error` (optional): An error message to expect when attempting to encrypt.
* `decrypt_error` (optional): An error message to expect when attempting to decrypt.
libmongocrypt-1.19.0/test/data/roundtrip/aes-ctr.json000066400000000000000000000516271521103432300226460ustar00rootroot00000000000000[
   {
      "name": "AESTestVectors, CTRTestCase_Random_90",
      "origin": "https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/symmetric_crypto_tests.gen",
      "algo": "AES-256-CTR/NONE",
      "key": "2078bf7187b8792860071c205a9ad88fc285232780f4ac4d77be9146b63c6ec9",
      "iv": "185405c26aca39e507a2c71572b209df",
      "plaintext": "d3e88a3f39bf4788a325e4b9ba829e0a768a66e66b03940c50e6fd92f0fd85be4742f183c69891fc337ea5f9f8680c86827098277e8a32e8899eb6397fc93b94627c38e36815b6a2869a8f41f2e7d4369f8831c8b07a3d39197d",
      "ciphertext": "185405c26aca39e507a2c71572b209dfe36455d10b5884911f5a5bdb4519148ca7a754fd4b54b31ecf8afaaea522f1e0cc917f49bb4869307c19bf9862171905e644fccbebbd764f0015fd362ed33bae74811b7773987e129a3b58d339324a5415467baaacb25b4d3cc1"
   },
   {
      "name": "ESTestVectors, CTRTestCase_Random_1059",
      "origin": "https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/symmetric_crypto_tests.gen",
      "algo": "AES-256-CTR/NONE",
      "key": "faf3ca75c08501664fb9c7fcf78f291fc4a63dc58c179c12c1b70c3b644f71f0",
      "iv": "9413b67c240bb84e94b7bbb716a97ea3",
      "plaintext": "bc561491a030e785b570bc787c0728cb123f1604ec27075c1beae9f7107723e381ecb2275047db59627d7859e48aafd7fc856a7167c25657d751963b0066f8015b06927df12c203a6e0ba795cc859b830142d3ed1ab1a64782e1d5e16b243b5063b3dc9a12d829e8be020382f36dc24bf14e8c01ce447409a63867741c10384ef6cbabbfa28a10d5dfb1392c705ab7286a278e7e877845b3a9f1630311a8cdca3bcc388286d8af0cd366150570598a31d57a4543a3bea0394cf7fc59cf608f6ce6a21a7998ce0cdd07925895eeb4e451581bf037828088e8d30fbf29d4dfe7d77f0a7ad8f0af8074a50f161044e9bd35c56a5693c7ffc96a1a5e1f187be5c2d529c304192313152f83ee676133d675b05b9b277634f3b4795c2b675779ea718e06053d3133d88f3d1e99df11b30ef64e88e81df513c01c89cd3bd809bcc75b244ac8bf7a0bcd0ae53d5fb23198dd0c4dca55f35f9502de834ed1801242f9d65097f360974afbd435ab97bb147f8b0055572752f99df6afee23f28fafa37b8a6bbb00b472e53f2485e47f5ed1f828a0217ff866a94672a99d9c7e76a1b2063d0f86e5854a4f465587a2cf83e73f8983a5337ee01c70675399a6c9d17b2ef12f202a10c865cd33d9becfb16e516e9e86221981a7d056f078b116702de33f4cbe1ac06df55edef0955d6313ecca3b83dc3b4b2adeed2baf86baea922599a423c1375796da6c0f4d7ca811d3153a3be19c055acc377869ad1a41bee3657659981120d0e30f053c8fa2e7657f4648733253f723098347bb5e04d7e6ec5a476a1f501f1c5ef34057d80a03e61bf8df9ef7547c9d569436738cb578d793455be3c5246f153956ce280335a15e28679d9a9a352d9fa74011a3e085418475411db890f9dff15d78432193817af1834ba12c1a644756c74b230db42c35300f6303363e3c63edce5da0d59e172d0af284d3261ea47259a5606485a76059cbd0565086a80c88428d1aeabbcfa914d17107296f828c13ccfaeb424de7d6a094a5b5824a4868e859d16392bb8997b56bbad3318bb9894aca6ad71ba8c07bbf7ecd25df03a71669c1f82134525e6f9cab65e6dc88c3639502bd8cf28da71e01a0d74715c2d5798cad523078d6a318e8e09aa6c5aa40a16fe985470a0d8f665cad9b32f413527fad38c0055a9fe70807db0abc74a07bf7bee957a15c6a72f019b5ea9407ed865545263c6aa5ab019b75298520a65ef8bea2d74297202e9ad4344b6547f8bce3c659f151260a4c63d29d911cf04915e2e1390d6a8e548f5ced7dab122353969744fd87d23415ba2d478bc1420e9507a714ee740b85a67a178ff4f446f30beab5370779d6b32c5a78d539d79e165f2b2321959aed1778a335288bf96f41645622ae3f74653c3d83442e7e1268c6fbbc037a9f3a3eb1746666078a20a156ed09abafd21c0d5d7449d73a44fb617e69c94429c2aba68ad51d56c0e1dccaa7e6d778da948e5ff7",
      "ciphertext": "9413b67c240bb84e94b7bbb716a97ea3e21d884cd383819d07e3b08df52fe31567ee7ffbf7c496d44b262eba1e6c5f94c692ad53cc07b77d04605d8e337f3e99cf96ecbcb277d7f7c8c372fe8e6de7c28769f32bf3a5348b0294e584ff2c9825a7a452aff9b6f03c1ac05b6c6d20661eaedd11cac57bf15234c53371ffc8a3dea7de321b1e45408e3ea3b03c9731739cfc8aec30e6ecda9ed3d5dd8c665a593bf8f7adc5d8f072f9346438ac5dddc6788243ca5c4b9301f6703b85998970af6d4cdea0c21136b0c469bbb3fea47b9132476ee7790cf0da2a9d633f934db54e121502a0b505e55c31a9b91447b684f8a8f3de881ed4e391e6fee8d0d5775fbd2fbc482215122f3d5cc0ed91eb7fa8d20587422b898cede3f2bfb2a809087759e254a54e2a04d476504e978fb071fc73f38ebe15e21145a58cf3bd9e18815555aeb0030158b3fb744a19050a09225d8923eee46ddd3e714b3a67a2a84e47d0de5be84df866b7c236c47b86598d978e619d9e085fa5c743c8535adcf3574e1341e6621699a56f2c4f8ee19224a862244001c44b443e32e43abba22fc60d2b9be5d7bef3513ccbd59f8590b1a61a3302f7e495a127b2e336c4de14d735f4126c932b9c4d9096f97f79f13ea50f000490d39621a0a4edd50b90205144795926d4b3b5b1ad32a6e706a3560739fe014f151d4ad2db4a3629b3734261e4d41ada1aaff9f7a3ba9a6e602b6a7f8faa2e572151f4de8d470f772f9dc86f5dfd343b1f55d1e414d33a208d14fffb662e05b2cfe7474087d2e6d7097a1a3c0347d0590240d6dfceecd3ab65a5edc2f86519dbfef07e9b3c5a6fc29d18126d68efff7af18f76660ce4aecf7b8c3fc1b04fd7789b6ae28b93f13d96806f236144b36abd4cb6aed9b153ec6dfeb6c576b2a3738b9d7832b7e0a0168d8e3f6267f6c3fd6fd40be9ecc4a3a53b5c185a33fb3f27ed42c5092ce832d8f7afe971de8ffe23f1d5f9300ba5a3ffe8bfda3302a128904ae63b0e751987d5359427de6257e329d21263bf2da500eab3cf26d67d69272f27ea9bbcfd1229deb2170912b1c367c3cc73e7ccc16e12f27b2cb0373e0608d829675b2e595c1cfb74938a854f783f4d4b43ba0ada137f64d94aa278cbe90afc7430d31f26888df0ddd431beff3162e2db29a915b4e682e673743ae40f8cf566b5dc6e189c23aa2a3c0f8b9f1aabdc1c6289c6de623b2cbff2917e96c10c632be402ef1d6e1d83553dcb4c492aa38d07444c070566ac58ab9a97b678d56d9ee4589d46fcf2ac68033144340780ea1f1670f03095388aec797184272db78ccd77a34dcb1980effe2c4b898ceb022f0c78239a8624f15555ce89b2ef626aa867b3791fb3507848cf9d38a471ce7ede75b585fea4fc30ff1ed36c3e0e33a11c9e0be3b6fa069a6fe0094159a105abc7eb283ed47be88bddfffe3b5132ea4acd5fcb4fc3c5ff51a883af5e8c52f3f1a09fdcceec1115070187dd0bacce093b941f1e63ea1941b1a9f1"
   },
   {
      "name": "AESTestVectors, CTRTestCase_Random_3888",
      "origin": "https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/symmetric_crypto_tests.gen",
      "algo": "AES-256-CTR/NONE",
      "key": "b17c259ad25e022595d44b91cc1ff933d657ebf90e6e0316754eee085d6014ff",
      "iv": "3fb363e376719096eca5de9976353d45",
      "plaintext": "1615bf789eaea0d98adf1fc47826389080a0a6f8b0edb8d5a54b2501151dcd931a4ead010391255baa9a880825a8537ca452db906082951523123fa5cc5fcd55c920b29acc8fe1221970647c89237c73c2768521925fdbce3ee70c6f24716d81d3ec0a4787ff104425055f4835364cbba2ff9c83055f072ba64ec8ac860ccc83d865eaf263f53ae6a62a2bf91a0e377cc7803a323d9c7be842807c07d105e53bb67ec520a403d4a3f8c04373c93fac5d029b6153db0b78e801ed7e1811ec5474c656664fbf8b3bd65217ba6cb48a300c752101158f37e863e3ae7a8e4e1811471e36cd3f8b920d83eb2c8a419849c575b94f478f5f128b65ae1565f3747b08b7f259cfe0e26695c3e1daec6b93fab9a8bbd395d5afc5d85234f6aa015a8d8434f4e5b83ce9778a4350b714ec7eca553b05e85ca8200555617a8ef717bbc50f370997604fd35138308f647e44480ac26cf0d823d983e68a3903712506a602ace9d2bdc6575dc3a0180198fabeffe0fca21a3b7f6bd6903a529ddf01815b96de44f8972778f61625103cfff5ab0d7684eccecfa30d5eb515a81f60959bb32058ecfe6ea50ffc7c201c062eb24d4f421f374de306ca0caa17782f683a3e4b66a7a65474200f3a79a607947ba147767e03202a71213b74a84c95ea0d1e80e229f54a6680eb5206a881c74942bd32cad488188b377a35b7a025d45c1196fd1b3dcf15ecdf5687b3626e427c333b4a4ece00bc0c0e8ea0c49cbe3574db2097952fd86adb76b51b7f53fbad7d80ca0438b19158cb7b3067bfcdf4e40174d7e82e7318a14c7cb37a31c69945ab08b6effa926ad436cb86ae561e5d05d12df30a1f3af36b8ffcd57ec3e22d4a09ec505d2f8ec617a33436cdb2990d4e48d1b479982546b84d4c359521170aa6b54df60ce4748a604a7057597e6c2741be55912df36f10760c2f3efaed3c7e058ae15d0cf9e831e2542f1e4599e607ca93fd17ac8b1a7523637fee23a3f11e94388fa6a46148b31956eacbbdcb6291cc5b7f86ffd5e7c2d419b8da491b3980691a8d20cc1ae240f2d829abe3fa3385c4f24e2f2df3b560e70966a553d07a8ee1dec38b365702ccaefa121bbc7aa4abc78c43cfb22be0497b79bcde57b8a06070b8b300f90170025fca35fe8490880be9df73cc015a0b31e32702f5a77b4dec681e7de642ca1a0eb03e9e90a6f7a89e3e6b9c43150ca50acc8e7a6d52b4eb9132dd3bbf77eae548bbfba426f63271fa2899c42a9d775efecd07185bb248f2957a4f5ed758a5d54f190b0062526917e3170bc6117b26d7f8692ef0b6c34050e2c32146fe5672fa90ee4f43d661727fd7919b9a68f77ce038c2398fe3a2afd7a3b1a85f5fc3c3df1e6712157b3364b38cd47aa60d80315e9fc0a36d96757632274c66382dc3b6c0a365968a4caf7257f98801a0cef508de13856e71c735760e9d4f9d3bbe190bf3e9eb3aebf9763292cd77b730a0b166eedf54f163444bb9f30401761ca849b8d95e107ee46b86feb948b96ec5022bbe3d2542d1361946393a999f2034d87efb75dadf44537e400d806e0c6d9c753ed1269ceb1924e1d5cec51fb65900a304c344c24e95cb1ec1fe8ab8a03e81e2a620e1cef594876379df07c49bf70ff04a3424564438f483af77123f931b116d9b4d1f4918ed43cb510c3330ecb49f854030d106a29a77b80cc13c94d0fdc46cd9fa06094988df43fcbefe4aa6106c39031daa752c9a5d15d8546826fca561129d7bd0b89c20e0969e630ad79b994aab1c8a77b1b90bb3a14296db3623036dc28d44d5c30d2e25c36740312141aaf544c662c37dc2f57fbc1c13d5d27ac1152868a90e4e864cd22314d2061bafaeeca8373d63471b756b15211907417120b94f3b523ae3486057dd60091cce3130c98ccaad36e4cb2b97cc1dfde8ca491fc54ce5f7f8529b3b5a4f9e976f3ab30adf3673fddafc7dbf775a6ea0b9fe188db3331e66afe37b839578f41d031e9276e7e9c83f5c17416226d417a5e982580664758e23b2f280da149d7d4cd1697f595f4279e784bfadeaa4e4b5df9f03a0d448cbf6dd414e9e044e303565128fc0eb3d567440b44a069d53a87994f9ead9065bdfe75c09bb96d0c1cd4c968a40d3317a6edfed36398b6da426fd24bdaa4d5c83e158901722b93ed3ae42b9c51e4b92d4bb3110c2f237f28c016713d033de06ade95434d1b6323bf7d54bfd6a8d885a089eab2156d79496721e817ef1a85e2ba9348c28eb49b9d1a076037328b37cdac3d80ba8528baccac3bf5773207a80f0294b7a20ffc0ce31462513f2d6eca92a23ac723df5d73269c6cb40991f88f846d8dd35da5a1b22fa8a2c8d30452eecbb372c49de4559452edef4bd44913122afdd49cb831c9e9867de4e9f8f3cbf96a83bb31a6ee37e542e591289c94dc4e7846d4570c0ad2e441ad19053ce4b9968799ac150e4e3d4abece39eab179e609486ffa6643e7c9221922e37bb49f02481a9ea9b6b73f2189c7b72d243fa492a05cc303d5ab31b2bbbce3e79296297001fcb9967b3eaf1b97b7db9d3f0c99ee835f5d6fea0377361d3123ac4715916885221bb56a9dd9af4f07928e3e3e52bb27d0b68656456d1c4db727fc81a7d353eda88c574757ba46734a5f040528e215f1d57f50c8bb4b30e61187fceb51b3e9b9191c9fb17376071c415addd139a5b06b67abb7576330381f41538e9b02d9f2cce47a38243c26afb860d6a49e715bd8153f99d5b9de07f448cdf4e3569a9c9bb56229d9e1fa587289f0abdf0b12c415b5141facd85fbaaceae1c6b5543519d3e3189997903941dba2564dfb5ed000c4b561487189ab91f32b45782694b83e961fab3aea18a5f582baaee360a82913b637cab89067e9ce3c9dc4c41275b7c8f8e74a860a96948beb4432ade1f1fd86769a5322efc2200d533819c5e64c36ec9f69c49f02431446038b10be2cd545e795a34b9093b58a1a5f08670a99aac647d893ee7e8d6d228735fe8f38e0f4f6c4fdc77cbb29c3f344dcfa98297c2c3c3f343e98c9b7821f93bd35779136ddd14232c339fbd1e9d1161247a13841bf980213076d62f357c6f309a19e1d06634850abb3318ac06f5ee04f7bcd2fba6e18b26cec6b45e9fa2e38558881317967cc6a8441e244af61f86f8c7de558106d864152f809bd66a021792e7b78a7ada72567b1de15344f897f150f9dbd6520002d64faf951cd91ea52d51221e56ea170a616a245923d1dc485f21a9c98e7d475ffbbfd7778332b08fd586ff19c18afb7a0bd1ebdcfc6827d5f1bcf6f047f387ca4231b580f714ffbf8685031e1726f470e853a5f065aa53d89dddb99e45e7c87f18cbe62b00af152a9c168dd84b5d12ba935dfc8e4f22685cd43be77ad1fac6f1d7c98cf8f958ff775c6c90cda5751a3531beaa3072647951a75d4f207d334d274debfa950fe3a8516f3058be2c9594d175d890a5493e22c9762e32b8ef15cf8156f8483e83c4fd5b5405d41186366fbc65686ac97ca20998814b304c23cf38194e222203e17af4f9bf18078d8b456238e9662fa1627995bbdbf557fed074361241996238d4578abe51f0a91fb39069e669836b430ef1847b52693a29f4e99b5f5e6a783723c703b958fd3b6663e096b4e78a2d66ebea68bafd548aa286a1b9ceddb774c8b71769f5f693e7a0d70bd9698a525e7b93aa48ca18445473830e4eab58426f04d69d6623ae9cc3e6858db45cc23386b9852df4dddcfbc8f2a9a345fc95dc379752e484f6df9254ea6f85c0f309dbec76682cc3086bc5759f9db4107f3119b1504021d9c6ca9114b5c05dff3a97aa6cb642eae3c0ba6acc83e3718ee71520220240ddd426f0785aa926767d7fd5794f0fc9ceb763738ec84d887d310c28f8abb094f1e1fc8e029dfe37816a9a47d0a9b0b70dcb316a9df7945912eb9b26bcb110504ce7295135f6c0474c0fa558abb7ad026987be83b2f3ebea1e00dd54b267b67a237811f00f52d015307448b131138e5568659b149a5adfd7e641d1e0086be4553a28749d8cd777ea615c34072fd43f7fe91421595730c011f27157af52b00cd34de7ba2b6bf87377b8b92567f4fa43b9aafbb2b653355d197beda8815a8653af54eea5cda9a29578198493396100648c265400493f10f61995a4583f96e26e7ff57a91409734c9e5eb227de87c9668e68c00b8cde14c397d76eed2a312237af32417787baa30a42bb1d3115dd1d06b4d7f78fb02a2b482fc09c3bf41bc0a58d1003fd4c542fd8e6cd9d6f6c260c60771f62d2e4eb4d9231a1b69e1385e0b7c901e38941316656b632825341eaa23642cfc63874c40a61c29d09100c884d0d4c33131dc0a60bc72b1692693762a1dc44860844625c36d24c7d8a039d95c1d3899619fcd6a52cfeefb9415f2a8bc13c2e95dc3d571d732e3387be09cfb7767f3a029b3334dfaefc08f9ed3c3aae56c898a93a0e38fcf64a2d81a7e6d1a12c5a43cd1c81c95fcb7070b3ee1f383360bf6076db9910140d3d7eda4b96e2efa051e7b7e329391d01aa3348f6a627372a209492d80755a26bc8c1582b20d884f174c8d91c54275fcb4074d9d7c0f246ea54aac53f3b3ac81206a9ff7f8fd0e30fdd21e7f843e4ddb23119bebb37d36e48d3b20f3d05d79405d5917d38fcd628b11fecee1853e9e6e97a041aace189889d0182853b25c283c2a7dceb664da4ce77579c948744f960033767f72f7c5fe9592ec9c968e513493a26744cb0f3648a0c8657e6394ec0820430ad77520a322a1ca07e2381e8809950602f717bcd7607e7394bdc270a4b48aba187ab379753429bbe404c384c519bcd9fc90a0ee1715ad225f8776220727359b6e7db56be944c8defb9ad45baf8ad79b5ebdcdbca2854e5b57e277b0e0f4c39c2bf3af38757236d5188cbdc1786ff57f0fab229612061e5eab9ccb6f1eb96d0e63a7231863c55acc4056eeaa9f290ce14203f25a5a5c892319f94d929ca9c142ba44cf668c393bcc2095c7217d1aa5cccffbd5a386912addeb29380df618b32660e00199afe586867c275ed3aa41934b2740a2453a2c826b570e70cc26d3d6259f72337c2b7c5d04248fef27ddbdbce0945613256eb1fae3adc8ce4f094e402ea170b34db89aa1153d23f47c2c214e1395197e2d311b234fcc8ba40d9590e65fe05449d2bb0c9d84beb11335994f766e782ef55aafa65d20343669ecf499492c06cd47d89d2fd897c220ca710879d80bdc025fc21baebde3459274a16fecd0e9da64ecf14c4e1a4eb4b2a5cb562ecd078694fdd57463eb906c31d93977d43d75f1863b58944ac44ebc0b3b70715a13f86a5816d90a053289a5ca4f4be0f799f0b21cc2558e195724ee95fc44855d5c8eb32798b0ef7ec48e05882ddf4e4817cee79cbb451bdd2c720298eb925b1f0107020bc10f42553928d79905fdce432fbbbcf1c635e7083302e1d0d5bb4fbc5f2f6169cde7957377aecab747fb5fbc2c4cfa",
      "ciphertext": "3fb363e376719096eca5de9976353d45999264c56b6cd5f1b6c01834e39a2abdc31a4b73fde183ede01d5506184dcd6fdfba2d2ac17cd54bca45d6f7800f695cca8f1e365f33bc6fee82c7f69285b5720107f7b63881e25ead1bb7be4abc7bee14b81da8f655e999a3836d1eb1f4af171fdb49bd0540efd351ba13b624158222a07642becd3a0cce57108ac577fcdd138dd20bf92c65b78cf6a36871f2bc08b7af6e9bc21994f7f860e9a0bb97cda16a7b849cc922bd37683cafeb6b198a3f6a1f88450080cf5a1ec3e2dac5f2581aab59de3f4b212fbb8e07f7b754122abefc6fc8e09d0fa5bc6a8ac32af418a2f6325678d0f0f0d3861e8aa0a29e806982698c2b6a58eb171a426594fe5c6347794ad4435c1de1f9224d387d95f3b773033f5c96069f49613276f3ba8b2c3f24d7ff523d13266e84b9c753da33ccf21c97d9a120efaca36aafafc940835dc62a7f0b4ae0a9a7842b4eaeb19573a9b332b540d74a2e2a2e714808f293144bfec2d3735b2bc4d9b6510c972c477adfe67e3055ed10bfaebba217153eec70c28c21b4e8b57b3a23868554777dc268d1d1f11084b130f68fefb0a94fc58ee536256ae4f103e2a3ff7953f54a62868213bf4b68f7542c00accb5bdbfc4b592533b86e0e7834b8818b1527de9cc93f98941b7672c6de788bc518e45369ad937c93075e6a948c630fdbce333a138201a9f6c50dd7caa1dba1f3416015f23c7a6e1f712fe13ce86c8a0b8681fa8b82dcab827670d8e912820efb1d329ea5ddc9cef4d16ad664a11f1c8080aad3de9a5c79207732bbb5bef9f06419b2fad7de9054cd25f91b7773e6b307e2b259c70f258929acf02966190027142d1c46bb3058cd1b2510188f25189af92df419426557a1b16a70b08d45a51df48884a207ee0b987c8ca17a1a671bb78a5548dbb020b80d5f73bb3bec4986ba1858f097621377a15746efd8c2c59231624c10d8268865c0f4b58dce54fc71446fbb67935b4a67b0b8f837c92b43850fbf7d7311896515672143f1a2d1c5374fe8d564eb8436817cbc52faa9751002e02295e1e4e53f3070b6de6323a450783bc0ea08b36c215ecb341b86dca0dfc13b8f129bf3599d2de73d774243dc04cbe60fbc0abb1ee77a3af112edd659954ddc50b965226bf93fb04cdb3f8da1e48bf473d1c131fb1f80b123f381ea8b5d3f471ba09f8de65111bc95abdbd77e369719aaa1a9d1949f38ce8abeedd14e8b0624c25de19106e281742a8771c03fda9010a9d598ae91e8eed61e8422da3a94fe157b25fdef8518fedf0ee3a56af2d3801b6baecf38e4dc4dd74f03c7a94a338ccb6188664d0be62feab0dd226fd7dfe5e27427a4ae1df9310a134113bdbb8b61f9e343632537550ac2204e2d3e1cbe82f10825dbb40e08bf9d2a0f6825e260c3a123995356b6c667404c8f42d3e24580cf1d57a0d7d66a4ea35e6d932b909960b488a6a4a017ceb04123bf54dbe64803d54478d303fe9e444dd4dfd713e5195fbd710a7af60dcee39b38cd24d48504e895c357965ca10157319e70e64019ca831019637ffd458aba4242b18c483e42b1ee9b632843344a632a8491ffcb1e30e1ae0ddedf29957185ada4891521d9d2f381a4d4cee31362e1b9b5c4e2e034d7274a3b32af150910d7a30522e4b02b99dbe0a9d9b54efa8663801b4d1a5a9db4d6efdd81447289c67dcac1557b33a04a030ee65e07624f1cb931b302882ac04ccc1ca258f092cb41c70441ab850b590c0aec31241937a427399e78b8c4b7a653f9f9440728b0747200466c088cb378350a48ae088ff50670ba37b7b791a0e8ea9b845628a84910a36b117d2927d6635fbe93bbe4bdbc70c0469239621fb30097bcca92c56ee7cd2bb949fe74b7c4a81df28cb4b2dc0d26c7b01dc2ec7c095972bcd7d86da57c936a1aa8d6c07ce58a3361034658e21eff8246201e8c6edd482cfd4f24c1986f8c9320dda7ed01646f2a567191fd41b559fd677d037bc2e24f82d308dfd4038ee4d1c5c82e05361d9c089ed1926ce25a296db23c6c92e95a6006129dc38c6aa5c85f9fce44548f0bd0a7c321226f2042d1594f4e2026c51085f103433c50b0b183027d426a79b54187701a7c0dcd90c1413399acfe7f06ddfc8e6fba6df15c147811035f11097b82d4f75f296f2b1570f35cca8c5bafdb1edf495bc0e88de303f57267f25f678ef6613e952ae4999bdff8f5413956ca34aabc1d740d75a1b8c4da528fa1c83ef10bd9fd66df75f0cd94f92d29b82ee5d0fcbb3908a91d2b0e84805a5bbf4f629db29ca6aa66965dd602abae69cc4107e18551bca1e1bcee8da0be2cbd3e1c009dfdd8ba1c628f4c0456750794517fbb5226577892e46bcc326accb6201d85301a9720e28b7b531b24fa485fbeb758d06b1f4a732ad44b249d593e73cb153253445d26a5c57db620445627bcb7d20b82350ff32c8f4abc29e24ef3e21b8ded8d88c2962f2f494025a659d1889a32d3afbd2875ab46252c6fc93ad62a89175db177da985993bf569dc52c3fa53cfe215bd536515358d5937a683dccde9f9a98ad35b228ebb567a63bdc06f839ba5d5c5fbe7dcd29cdd50a9c66d477ca318d8ab3ecaff0e167d242d052998630cbac10af3864f3545b8a6c1c6314527a70b46f9cc0f08f3d194053858f7213736702bf3984b7a91453ea18f8c6fab86244dba6b1a0e98a18695752114ac054ab47dc452ff0b94ea9b92798f30680bf4a9f05311ee01e5dbecba47cb442d32513ffd3dded6c1083b98b81f509fd4a8325dfa83d9c3787b06d4f1a564e7f4fb9ea586b68ad11180b721b180715a3e644b687ec84ba37a8ffd1987fb9003ca3fbde0b7ad1239881979a7546ebb31b5b535e4367ea52f591c78535479863b75a5b4cde7e9fd19181cc4648980d617f124ea3584259d245b6fbc36eef4dc4c45b22e2d8b1b76775d3d7e8bddabdd375be0511b22d42dd7ac3450a7a5bff67c29081823a31daf43aff862e90d2afed570550399432691c2beccee99ff2d42425fffad201a70976c4048472fd87e9ea9b899cc32446f597f9a4e763dab0da02c7f0283b26696142d1371006f219935a1cfaef4db6c1e91d9ff3b51567a85f202159fcc19db1c6f8d687bfcc3aa60c429fa605d61fe3d374eb1fcc50ac3a69094412a0dcfdd312c40358ac259e6d027452d12b65b91c75c029db49f3c7605882686de6ffdc492593960ec9354e7e834fd1e7a5d7f94f0c88433897b63ddb1691e2e9a6f41c03d449ee9acc4b9769a58a7f1991071d5360a63a39b85b4c38b5f06ae62187fdf0220f20db673946675fa7be4c67d19eb6140e2f4606fe961a49526f3e40806416f67289cebdbae1b4f8239cd5aa9d7afb9ee1b3f446b0b2b97f8be9020735950b157b73991538286f55322e9531a41e7f47cc855479af3b9858397a9672af3e03d0471b00048f51005411c3b968c41c50fcff0735c083244e06946371228bf2b6385d1a43e2c724ab086e0f0739e392b914e86dc0450e89d55a571c7a37bcd25baaead8e2a49fabd9c05a061c348ca805b7ef089a71b06f4cf4617a285e51e74e9433a66d2dcaae94b944d27d9a9caca7a06e5795eb48ed5ddff471bd534374845dba3455232bbcd2644ed1bd7e6ed22b566f1b250795b6f82d4eb7a0170be792f76d5fe96a86803e92b5c58446e656cbc2e13fecdca44a83af48f70e620ac34d7fbd2bac610a10a010287a67ea40285a85a2c91a0495c5e47d4ea363dec1d69f587975fbe64d70e3423c75cde86602369aa38359dd410c1dad185cabcf161bac22c434b27da6e8f25907307a8c273c37aebfb4f38c06ed145fa13f1e47585cb2070c363a106d0e0f5d9602f7bf1b0e4001e89a59210b3cbf01a1497498548b8948e286bf73b9afda48e0105e07a05cbbd79c66066e74c6480f78dfad23d57d61313d978f08143ac8c77e7c28c0397bdba32b5bf33d2c71606e71c196dac449b3bb03e733b7036ebf0b73e277e135452b2a1338222aa259b1448fc629805483eb15f591639756a20b57d590904ba17b4e3e6b965a8ba7f20f5ab89cd47aece129870507ddc43e2c829c64d93e36883943d3da458b8955a3b1f8b92fac4564e7c219d743bc39331c18d22fe27ead16219a61d82a2c18755bf6bcc553ad5b0683ac6abb5b92e79cddfee4b92d9dbcc6626979c358a4e1a8f37b651241faabb4e1e460110b2db5254971fef2b9877498feb6107f1c5adcc763ed6de820943b703249bdd796adffd76c7cc6046d3baf57a13ff5f55ad589396298c6b0e230da90a0b9230f457eb5db8962b9bb0ae406f29feab529c827117e39c757c2eed15b9b72576605ebbab9b632024bce65a759e17975182d50c04bdee6b8fca4fc488ed2a386fa226c5ce8366f67ec3eba883ceefed963bf29c0de829b406b02a6850f953a430e70fdbaa2998095daf93366819e3df71260b66d035b1c908b20ba62c0d83c27b24cfb9c3b4a5a35316ae8703245f47323e15ff775b41d2953633865f1d6861a9cab887635aae8b80efe7dcd0cfbeb3ffa6aa4a31a0550b0a9b0f6517c83f0247529bf8cfbdbe5c0092c6d4fd1a8c5539704619d28300ac53a9fcd0266fd65ff4e8fd350f3d128eb001312b1ffeacef11d5dde1b7dcf3282eae4c206d76d38a2f22a8fee2d0666ed36012ae4857b75171694e1a89a849007f4dce1f769d465055abfdcdb72e9eab8670b5a359e8d6b18d226ef24af5f72f79b00b4f3473737f65732865970752c6a1efcd03d9846c41403ba3cb9a8e50d89ca49201e899c61ad7acc87b69b9e28f86f9f44fc3400202dd6c010848b3daeea2c3ca54886ce8e11cf5ecef01589f65032cb901f051d291e5dc85d1a72f49bd549c3fb1fc1f08abf0d35fa1eaf3d60b44621e757825963f9407b80ba1a310979fa303ded3e73c34089ab2764143976b002af7ce663ee367a3824951bc7a87e16169db3d65fec5acd329db54fc30ae9f1e65aef750474e3283c858b7f325734b7112206e1992e6db244ef867e1179d544da3dccdbaa79ffd19adb7340b5af3e3de55040734ae354fbcb12efbd02cd1ed6cbbd9057977a60f3440488d26e68cf0e07cfbd65026bb57bba20bb69e53fe87228420cf08e73f12dab7b4ae87e00cb38a010a8368d8c8db225317c60a2cf2a01acbb6de8adee370e62ca4e8c6cf5fa976f45ec62421f084d19c5f724135020fa1c01578fecc38183454911d67ea34ff0bae3ebe29c1d12d14f5f561ff960f770cfc3cdd9f716c5a12e8fedb6928f2f7bdf80d05c7cee7e12d1510eae13bcd1619be41feb16a5f4070563a28b2d59d8ca92be92297910ba63d827b1174cdcac6ce64af153a773945bbb293d8e5c6603684f24a8dfdea742e07ff312f912fa961863d8a665ca68a7875be7a09ee5920bf442767fbcd245b5300e1479f8b082ca2e3adffa4680fd1adea93ea83ecba4d6deca41d07573ed2098862dd3185d651d42c1420de3f64f15c7f246a06fbc"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2-fixed.json000066400000000000000000000004711521103432300232240ustar00rootroot00000000000000[
   {
      "name": "Plaintext is 'test1'",
      "algo": "AES-256-CTR/NONE",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e",
      "plaintext": "7465737431",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb1070cead40b0"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2-generated.json000066400000000000000000000040631521103432300240640ustar00rootroot00000000000000[
   {
      "name": "generated test. M length=1",
      "source": "etc/fle2_generate_tests.py",
      "algo": "AES-256-CTR/NONE",
      "iv": "95874b7fdaf9ae802119db1c8ff57f83",
      "key": "d5ca079dbfa2f2679a644457dd4e7ce16e4487b45a3600b67f07c862547dd945",
      "plaintext": "f4",
      "ciphertext": "95874b7fdaf9ae802119db1c8ff57f8342"
   },
   {
      "name": "generated test. M length=16",
      "source": "etc/fle2_generate_tests.py",
      "algo": "AES-256-CTR/NONE",
      "iv": "b212470f2da37d54e1f7cb97ea80056a",
      "key": "0da6a32d900d022491132ac5f63b4d5fdea41d2f13917e8ece8cc2dc2a85ac47",
      "plaintext": "547f1c41adea2ec92c789060056e11bc",
      "ciphertext": "b212470f2da37d54e1f7cb97ea80056a4d583a9694a83e3ab77347b58d4149f1"
   },
   {
      "name": "generated test. M length=64",
      "source": "etc/fle2_generate_tests.py",
      "algo": "AES-256-CTR/NONE",
      "iv": "68324d32fecab2d74d21aceed29a092f",
      "key": "fb1b6518c8c44598e67ad14166ff1446ef380353106f6b9dfa1096ff6983bcec",
      "plaintext": "8ed05789e2308c5a8f382fdfbc57a47a79115eabb6b3b028ee5d4b2ec06b8718dc2c7f3a86fbc5cdd94a83dedc09f35dc5d566d25e00962b2f48911add81134d",
      "ciphertext": "68324d32fecab2d74d21aceed29a092f43a11a83db67d73b994173f394f8fe6e85183d2dbafa010e5b9f1c808a4d06548d3a5cc9305673466ca4e3666387e554ec768b2ce26408e069291a117bbf595b"
   },
   {
      "name": "generated test. M length=100",
      "source": "etc/fle2_generate_tests.py",
      "algo": "AES-256-CTR/NONE",
      "iv": "a88d18f1eaa9d866a88467ccd4ddf6f5",
      "key": "813f4abdfe7e84e047073347421b75dbec97f9e437a0d5ac9e4b79e738f2e8b9",
      "plaintext": "f4504f0aa8d2a01fe481e84ec16478f448abb45ad1472e401b6ef810f32138e89363adf82654abd1df964209b2561d3b61cb8a347f5b6db89f95c6bee831b3aa502b311683b3a2e5f02107d44067fec1f81c15a5f5a9924b2068d8dc500011536fc3644c",
      "ciphertext": "a88d18f1eaa9d866a88467ccd4ddf6f53f0307f159c40220571b42ef2a3c7f5fd35d646259695d07b3984a2eaa652e0438924be627779aa40373bcb19fdf025b24a6abafe9f71ca28d0631528d6cb4bbe5ddaf709f6382a963a6157828c42be55bacdf35ae45fead4cc55036b1b580416b211caf"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2aead-decrypt.json000066400000000000000000000035361521103432300244170ustar00rootroot00000000000000[
   {
      "name": "Mismatched HMAC",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "74657374310a",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb1070cead40b081ee0cbfe7265dd57a84f6c331421b7fe6a9c8375748b46acbed1ec7a1b9983800",
      "ignore_ciphertext_mismatch_on_encrypt": true,
      "decrypt_error": "HMAC validation failure"
   },
   {
      "name": "Ciphertext too small",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "00",
      "ciphertext": "00",
      "ignore_ciphertext_mismatch_on_encrypt": true,
      "decrypt_error": "input ciphertext too small"
   },
   {
      "name": "Ciphertext symmetric cipher output is 0 bytes",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "74c1b6102bbcb96436795ccbf2703af6",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "00",
      "ciphertext": "74c1b6102bbcb96436795ccbf2703af61703e0e33de37f148490c7ed7989f31720c4ed6a24ecc01cc3622f90ed2b5500",
      "ignore_ciphertext_mismatch_on_encrypt": true,
      "decrypt_error": "input ciphertext too small"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2aead-fixed.json000066400000000000000000000030551521103432300240400ustar00rootroot00000000000000[
   {
      "name": "Plaintext is 'test1'",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "74657374310a",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb1070cead40b081ee0cbfe7265dd57a84f6c331421b7fe6a9c8375748b46acbed1ec7a1b998387c"
   },
   {
      "name": "Plaintext is one byte",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "00",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb1004b2f319e0ec466bc9d265cbf0ae6b895d4d1db028502bb4e2293780d7196af635"
   },
   {
      "name": "Plaintext is zero bytes",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "",
      "encrypt_error": "input plaintext too small"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2aead-generated.json000066400000000000000000000221731521103432300247010ustar00rootroot00000000000000[
   {
      "name": "generated test. AD length=5, M length=1",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "5f8cd7018d18047c24e951095a00974b",
      "aad": "c225f83d18",
      "key": "c4145873aafc4b1dc3587bf2f493f4d72ae3836c95b7d72c11da68972632b2f46cec54424dc45325e30453396487e715925581a557f57b7976a3f63eab5578610000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "d1",
      "ciphertext": "5f8cd7018d18047c24e951095a00974b6118defefa125ef6ccbbbf7e95cdee9aff9e85fc389a4b6c62cbd98ba17e541bad"
   },
   {
      "name": "generated test. AD length=5, M length=16",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "820c2236059ebc4bf3b7e82592098199",
      "aad": "1923d87dfc",
      "key": "295b948495b5eaba751b919c0e4a4a8ceb7f81c649a14b9fde06ad17d41e2e738284d519eeea5bb731c3476c5311b9f85a8c2b2cf77918bbcb1af6a16c292a160000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "3c06a636989d553532d2eb089b144d99",
      "ciphertext": "820c2236059ebc4bf3b7e825920981998d1ebe4ae73cdb550c3ed76342ee170d77733d11f7745bed0244f0cfb8b898ab2911d01161dc127c13287afd99aec2f8"
   },
   {
      "name": "generated test. AD length=5, M length=64",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "20d8392eea831d024c44013f57c04e63",
      "aad": "dccd27ede9",
      "key": "f357344abc192a002f70f2abb6210d4dc6df8988ee3736e43551eb5dbe0a0c04cea86dbd0440eabb3d75014de192ece0f86c91b476bf8fe3b3778477a354f7860000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "3ba03e17129cff2b05217541b39562cff28904451aeb50848f1668f521d6dd9193ee4e494428d70d3120348e984286062d1719a1b4bbbed99e6dbb0fff1071c3",
      "ciphertext": "20d8392eea831d024c44013f57c04e637316cc3b1e9cae5b0f539ae0d606467ad7cea5c3794a9045d11cc033a22342f923bdcd31034955fe0c3ba96948527e821e610870062e87045a387a5cf2a6ace9a052cc9269eb9602dc6602ca83e4f60df065d387501211465a5830a3ab165732"
   },
   {
      "name": "generated test. AD length=5, M length=100",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "066048a059dc3691a6251605f40f4089",
      "aad": "d611c4635c",
      "key": "8c77b9209eed11c0875a37a0d31407b770cc55fd8776e62eeff10fa34c062e30ef856d59bec8855ab6544ff0ad2c51bd2de042c3585e43568bb7f390236ec8640000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "67af140501548da74f2e6865d6866eac5bc880a797a99571813706504544488607d88ecbeabd89c62584ffc7f4d7b29d0792570670628ee39492a4aeaabd192dfead1102c9d8e386e56189f6fee9cefb5f2c9939df94bedeae433670e9544b8d52ad1e2a",
      "ciphertext": "066048a059dc3691a6251605f40f4089f2dfd34e99ab777154e3652311001a56766c0619d013b960f62ee3a6306d20e55d290838d0519077f74fa6db87a34622133b9dbe68862e30d7303724a6c3f9a20f05d32ba33669fd85dca50ceda01ec4f7600cb4b389cc2b6c1581125212567c90df74080ab742ecbc4b367cb3bddbd490fb15f5640b61811d92af8fac317ca1289ad14b"
   },
   {
      "name": "generated test. AD length=10, M length=1",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "5b28df85be62398949b6a3c33af5206e",
      "aad": "96158646db0e9d89abf5",
      "key": "10adae2342f060c56d56cb18a08ca462cecd0a3c9ce78ea6941ee1ba0e6d294e27fc91be4d4e561cdfbb788a72685e84b616cf8aba28237cf458bee3626449fa0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "01",
      "ciphertext": "5b28df85be62398949b6a3c33af5206edac469c25b610abbc8a0ebca17e58002887fa308a5a78ef9422d2e3a8a50c236a4"
   },
   {
      "name": "generated test. AD length=10, M length=16",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "05b88d0b82edc539a1e3aa8107d070b2",
      "aad": "3f9219d32b1a0db0b1e0",
      "key": "d9dc626b911ba9b3907b1c8e0d0c92c400aeddf041d12c79e18c4e10705e322e40a5bf6728537d73b72174ec7f380be5e5aed223609d66be5f1cba680baf287d0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "567f9de1b20c251eade0cc16b91d5ac5",
      "ciphertext": "05b88d0b82edc539a1e3aa8107d070b25ddf05c1a8388ff6f985c2d02907538096049e3598b7b25cf32a2e98aba6e70eb50524f143bfbc25ee6cc100192aa3c6"
   },
   {
      "name": "generated test. AD length=10, M length=64",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "0a31fa49a2afec6a783381e700e161b0",
      "aad": "2579c17bac86d076a29e",
      "key": "5f07fe8e76023f6c2fadf656c176e0d6fa894bf5827fe5541550fbc27cd0344a14fc6a27b406477852a48a3c0a41e1df9dbdbf239607fd7750a31e67d6f0c6ec0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "83dd74ae12649c409fd64004756f372e5fd080cc8f7d168abf6966591750fa3f195c31cd5ee6c973bf850404bf250ff04902128281b8e946907f59b904062566",
      "ciphertext": "0a31fa49a2afec6a783381e700e161b092b7fb6bd5495cb197fffd71eb51c5800cbc81867af2dd2c7cc27c9e124347a7ee5791f3263a843ecdfe51455d393deb996c2849b7527646808cd31dcbb792eadf760d834b7354f52621dfe784ed242325795d220d7ea30f7360d37b0a1c95aa"
   },
   {
      "name": "generated test. AD length=10, M length=100",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "3d13c9658b0d53752c5764af97749443",
      "aad": "6cce5ab29fe6fd67b06a",
      "key": "213d15cf3a8b74883ff7ee71b15c02eacea7e9571edf294676c783231e7d9463d8960a96efd6c99974a36ca23160abc460ac4b4abfb82b5556767bbfbe2df3290000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "4bb184d6dd581f0ea7a943b8091af382a3007b04cfb9bce1f7b7c1a73e94c2fb71a5aef1a0fffbb2d9629e112426dfddcf69d90982c3a78aaeee85598e33a5538b803a239c143693acbc024576e58e615370019015000d5b6e0c6f8230a8271fa5769cab",
      "ciphertext": "3d13c9658b0d53752c5764af977494436e8833ef340e3adadf27e4c9428ef537b3c0930bc1445cedff24425352f41b0d4070937fe9c752c4755ed80ab6e9439133c97dd26ee1a453d0cd5344e05224ee2ccda23dac3b23f126d4809fdbe6f86bb70930bb3b1b6d93d05e7edd97697c08eea30052918a9ca148df8b859b5b0688fd107f659e58c711eea4862558879f816924d9a8"
   },
   {
      "name": "generated test. AD length=20, M length=1",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "946fa491604d45e7d757d1bbb84e834e",
      "aad": "b71a4d87005b8e7f0dcca0ddcda4b4894033debe",
      "key": "e00f828cac1696d4a4168a8b517cfd4dab2287bddd7bbbc81c8605a946754fc8ceb55d8b67a14b7c979e848d29460bada71e85cdfd32ec2e2b00aef3192167e60000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "e1",
      "ciphertext": "946fa491604d45e7d757d1bbb84e834ea29b08d6d3c95b7421a5ef21b8a4447924fa134e3d347ccbf8a3ab38fe3bbd940e"
   },
   {
      "name": "generated test. AD length=20, M length=16",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "92276e63c9463789f5df927d4c83679b",
      "aad": "f5a7519eba1b3c8ef61c0c5585136da9af4f4944",
      "key": "c28fc39c0a89da61a95084145153556a47f57715c2fcca725333ce15d94c39a1351b74722974709918d2f406b2d8232a39a42e158feffd3d7cde9e2f5472dccd0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "4405b7fa30c0b3b4e0a08a8ae69f6347",
      "ciphertext": "92276e63c9463789f5df927d4c83679bf297c9ae0a686767bb0a786565564b450eb0eb6ee14a40d84a8d2c65c4882789fbdc8cc1bd668b110a54413de0601457"
   },
   {
      "name": "generated test. AD length=20, M length=64",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "9df2f988712e7cecbdd4a2f6874e7ee6",
      "aad": "9f87da25af14d5755ea672e361114dd59ed4f930",
      "key": "d5859cf0ba821e2c8b0adfd12ea728fd1b241288ab03e492f0df1dac20ca6974298655b609d674412ae0e747cfff2f4de89c79f6a4911e19ea4e660758f119b10000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "e7e643a2f21a439758edf32c1685d4470ef7f4c6a7fccda186f6e584fd4de74e4d3db5fbf440f588bbfe937f8454ca633850b7c261a0f721e7fca5d8d2668a17",
      "ciphertext": "9df2f988712e7cecbdd4a2f6874e7ee67d6c1bbf072bbd2ebdd13a97bf1fa0801283c41af27281bbee75fb783f33661f0bbee69dac5b792331c68612b76658d4fd9c18869023fc06824d0f73a06321bd3bdd002b0c35b3ffa4a1dc5da13eaf2910221a1be2b951a3fcb2d0319685bc00"
   },
   {
      "name": "generated test. AD length=20, M length=100",
      "origin": "etc/fle2_aead_generate_tests.py",
      "algo": "AES-256-CTR/SHA-256",
      "iv": "8a48039b1bee4c011a35d2b8b990d59a",
      "aad": "3f8a52d9642c7c596b0f8bb2a6e5a568ebfcdebc",
      "key": "5e32c06ebbd28ea8b1d325e3cf6b85192eeeda6f672bc6436c43ea2ef710daa435dea75714bcb2f5e6d962f5479eec3827f85a50db17dd5c4f31e551e321249c0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "8e76b3f282c2c788fd903b3eeec7eb6f133a280bb2d598a9f6d919c5288eeb786615ce6f8ac105b433f4f8c62fb0a11af3996cbf3b9078001241efe776cb39614417481def9390d1a89bfc85be1d1f2249ac8e6d68a3c5c953495eb7b975032d9e4018ff",
      "ciphertext": "8a48039b1bee4c011a35d2b8b990d59ad386ef0af4dc7b462bda7b1b1b6dce6fad692b55f2ec74dfe66345f80b584539ff5c96d20d11a96e9840473e228ee34d3c00e24d2d81ee6a4cc48d9af819cf7bbb4efa1fde0b5904f957d9752a06053b765b388974a72a95650071493c004e5ea871e0bded9d6400c56b3b705695141c4ac81927cf023d996c3049c33abb6a4235d3740b"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2v2-aead-fixed.json000066400000000000000000000031371521103432300243660ustar00rootroot00000000000000[
   {
      "name": "Plaintext is 'test1'",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "74657374310a",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb1099db649a94f48960a9dbc0c438c904ccc042c6cf4f80af9fa4c8a00a1f41f11581b25d8ea083851460f03501ddb90dfb"
   },
   {
      "name": "Plaintext is one byte",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "00",
      "ciphertext": "918ab83c8966995dfb528a0020d9bb101dd2f66aba26a2cfbf868527c5c983076efca019407d3e82f60e3e9b97412a703fb14e40bc35d6af124b1d2433663334"
   },
   {
      "name": "Plaintext is zero bytes",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "918ab83c8966995dfb528a0020d9bb10",
      "aad": "99f05406f40d1af74cc737a96c1932fdec90",
      "key": "c0b091fd93dfbb2422e53553f971d8127f3731058ba67f32b1549c53fce4120e50ecc9c6c1a6277ad951f729b3cc6446e21b4024345088a0edda82231a46ca9a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "",
      "encrypt_error": "input plaintext too small"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/fle2v2-aead-generated.json000066400000000000000000000226711521103432300252310ustar00rootroot00000000000000[
   {
      "name": "generated test. AD length=5, M length=1",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "fb1d6c81c02cb6b58a38b2bfe81e72ed",
      "aad": "ff6e78cc5a",
      "key": "a58df9360b011f8a49ccbc0f45a3c7b3e3c79afa0d1aa3a986be22942d619cfb046b3ed7468ebe5f0e987b7adbfe274dbdf18b841e32db0ef8266e4e0dff1d4f0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "49",
      "ciphertext": "fb1d6c81c02cb6b58a38b2bfe81e72ed4d302fe9487190c8f5a21640dbcdab4aae4d7fe426df86baf50f3b5ac793fc799c182f6657fe3fe30307cd6e55d72291"
   },
   {
      "name": "generated test. AD length=5, M length=16",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "17a826cd1dd9d69c05026bed44709c66",
      "aad": "d80ddf23c5",
      "key": "8a1c5e0bbca800b67e7b775e0f771e457a63484ef8429b90b7d829a3b69c7b21a6e48664ceb969dfd3b117dfeac5e405470a7d682d9bbb1e7b8de07948fe84270000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "2b66ae58212ec33b71487f1561c01554",
      "ciphertext": "17a826cd1dd9d69c05026bed44709c668bda7372f4a66870c2650a936d420405650eef307c81635bba0ed7d19843fed615099a2278bd08f3055b07b7d0865ee7f55b90c4623d87ae0d42ec35188d3175"
   },
   {
      "name": "generated test. AD length=5, M length=64",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "cff68ad5356c7dce46df063a1b09a205",
      "aad": "3a8462a3ac",
      "key": "26472cf07e7acd1c649ed2648a21bd1779940df7ec263c8e87eb116acbf223cb5f18d82716554498bd86a3a1772d92b9ed28208d86fef1a41a7272dbb24df8b20000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "16fe1a1a40378950b49542da67e6c5eabe6260f1378ae82d1ab02e006d80dacc635c271913ad46c8afd8ef114a0dc8d4cfb00f36e7f67365b3037b5c59d25a67",
      "ciphertext": "cff68ad5356c7dce46df063a1b09a2050469720e9166fe244860e2da3514b9e444766cc0903a5a91e579a0f7bcb81c3db8800f8da003cbbe0aadc4b2b7619a961227215f26847d9f03e8fe19a44bfc56aa24d6be802c628e6bb6945e96f11d67df6e36b532de9e4b7bbfe3bb44139bbee6f25337021afa50f00dfe7d40acf69c"
   },
   {
      "name": "generated test. AD length=5, M length=100",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "f06ae9800331df58e18d5964f1d28ed1",
      "aad": "6f80661806",
      "key": "41880f49b2aba514495a03ab437a005817330a55d62da466504e955ee8a58273b4fa1d61505ffbce877a315b42f2aff2770c053a5fefde6c7c2208eac977db070000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "94a8938d4abb700dc2aec1c2be533d816018d6eb576c2ee4fe9d5cc509615f51762e4a74bae3a084b677fae278555fa0250465bf8f5c790750d4cdfdd8c3b0b884886db23cc434ee98f8dc88f303795eb4de0b30119e4a6d0af3a04034f1645667aa179b",
      "ciphertext": "f06ae9800331df58e18d5964f1d28ed12210c460ca3345f6e086f45d4b718917c8e720725b2fa33ae9d4f59916e3c3aade685287e10123c35bfcfd19a1560d4a72d84f5c526ed39f654a47b5ea4d6b4c2b3a6f5f7ed27b27a4c0c2fcc6c061d6265f5e60e2765462cb7e2cc79780940530063cd8285a1f20f017724406e6556e6b99662022f1a22cea7b10768a6a11da39b086d9b44e39722a2f8549691b6468"
   },
   {
      "name": "generated test. AD length=10, M length=1",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "c5a137ea066a5e82707d5eddcbc9d6a4",
      "aad": "b4abbaa384d7bbd96f03",
      "key": "9b53522a62a4c4c5252f79e0dcdd33062c7bd070a748cfd3687b41e19a06f22fd2469dc7ba40f5ac81477f45b8d9d85f399c1e90b7097a2637d12bb3bfdc8b4b0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "bb",
      "ciphertext": "c5a137ea066a5e82707d5eddcbc9d6a4132d6f47d1d902a2bd0d07c01a8f2c415aadf64b65945a26b3ea1389b7ad336d81840f3797f2ec0fb5e57e94ba9ce265"
   },
   {
      "name": "generated test. AD length=10, M length=16",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "39cedf823a3efd6776a5adb90b631f34",
      "aad": "3f1b82afa35bfde34a4c",
      "key": "19242d129a8f57efc583a465e759e8d944406d5d6751c7ac83a9918fea1c341ba8fd6bc81dd0594c9e708bdbd403aebd7bd7b448bea798e67ceb40ba7195350a0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "e635cff558de4c6888da603a25e7fa46",
      "ciphertext": "39cedf823a3efd6776a5adb90b631f344231627c81d7b57f7496ccaedccb2d5b5412619bbb26b60170269f6aec58bc945f483e4c937aa71f22668d37df8fe6730773262adf42df1f1f54e638d46c0cb3"
   },
   {
      "name": "generated test. AD length=10, M length=64",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "d9a613d0724951825a4cd3eee0f3145f",
      "aad": "e2ecf7e131f45c609aee",
      "key": "00cc4ba44ddf60e7a3388f1de5911eceddc261a2f06a7216636df1ffd4e96438b9d3c75c47004c73c91e29911b74709f912748a12e3a644fe406308ddc1b11320000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "cf9b0932953195de207b705f4e636446eae7edfc4375f91906c8939205e8b0618b05b80ea865a1047abf1e160109ed143bf367c4f8e47e59e8d59fce33dcdf71",
      "ciphertext": "d9a613d0724951825a4cd3eee0f3145f4dcd76e8863a731feae223a2de5800db5522afe6cb57908a558aa278b858fd2ba0dd2fc49912ddfd33c29f9b5da63cc1b8a7b9cd636fc8db4476ea7294e1ce7118818e8e6ab573ffd81bb02b27a31e5a5310ecf7c24cdf21e722f99e961bb66dc1684a6bf839c1c34751473fe6b48c3a"
   },
   {
      "name": "generated test. AD length=10, M length=100",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "8466405c5fe9a790cb6f4650132bce73",
      "aad": "4041fcc02b394fbbac7f",
      "key": "70dfb2d08ec15436eefdd211ffba22b605399af0367531461ea96f55a22a824def7b0cf489dad6277d99be2c9f7083c0e6fc5dca5a5e57b3b8b0f63a3afe0e890000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "db17e9f5d09370ef8b26fdcd177b8e7c52d3479ff16182a4ff3be30f9088104d1203e6a8048210b86650a313d134db6729e85b3f7237a605bcf32c0363706d8f73e28feb8bfc78ed9311fe5826d579c5e8685474548e501397921f877163b0582efea6bc",
      "ciphertext": "8466405c5fe9a790cb6f4650132bce73ae3027d215290969960e4245831ce0c08e9fa4a31e0d6ffd017334ca5314705eeb22297cea4dcfd960b29e14b4e95efaf701896d9233cac43a40a8d7d560bb10fb3c0d85d69e29419894c32c58f5986dd934ec0c473c57e33d252084528ba709ab4f84674b6e40190eef9582228efbd8cf42df8a81015c6866397c81b56f9e8c548aabd35eb9ac4a63065445ccfd1ed2"
   },
   {
      "name": "generated test. AD length=20, M length=1",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "43d601f463e000ed0d10a51f5145f9e1",
      "aad": "9d57dd5b87dd7bd7b8633494537e26a2870af052",
      "key": "b9208f5af904c60ff0d01ba03522e2c9df68334bb70a698e543b7b034192ec5199675c39148535f8db28522d516b2ff822564e68dbfdb22b01f17e3f80253d5f0000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "1d",
      "ciphertext": "43d601f463e000ed0d10a51f5145f9e158afe11eb61498a4046397160588aafd7dca2da66eb26a13b62be7f9ee86975c99727108d82c0c98b10e94d34ddad5bf"
   },
   {
      "name": "generated test. AD length=20, M length=16",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "1928360d70d848107e7c0c2b7d51fbd0",
      "aad": "92a7b11d85f7704487aaad08c4aa1cf96f1a85d8",
      "key": "489783288d6b06845c160dacbfa880f293b7aed9ef2d32744d4a7ff3a8d7b6f21f592ec9cbc66e417df6b4c5e42c6ffd4b223a2520acf634d3e259b12b23c2350000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "7d59536214b6c6ed5873e8d494910c32",
      "ciphertext": "1928360d70d848107e7c0c2b7d51fbd08728a6e93cd58511be79fcd0e3f22a11a823ea583775c99e1eea9631550a7e84dcb1c29f7afdcdbc6aef25e1ee2e0eeb3d09c21c849911ee2341b3cacfcf5239"
   },
   {
      "name": "generated test. AD length=20, M length=64",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "0f006c7e5a9ec1508de9db0aa6b626f3",
      "aad": "a67faf62dd67ad07bf27644edfddbb9ebdae6d55",
      "key": "b44af86deb6dbb59df8528c2414681f05e0bcd087333441ab097f2fd9eff6625612281875489f93cb90d80847318a9f266fc6b79f17203a43aafa766e0d746780000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "cbab838e2128b184a016ea9c13a20c2eeeb77cf34bea7349408c6c97db948c0eea17ef66d1b0faf92fb656500e443baa0aaa33d6ec8aabdb82f2faebe5b2f989",
      "ciphertext": "0f006c7e5a9ec1508de9db0aa6b626f3ecf4a00d9231ae317550cfd689a5e93fa9df633b989f9a63fbcebbc21ca99842a1f5f1847a97ebaad2f54dc8c6c818eae8525b4d178b5066b343b7d44c02f9b699fb77a1b1aad638267e494712405d835729c209b6c5a0842913881494a92e5274f703f49d6004183e307b532b356113"
   },
   {
      "name": "generated test. AD length=20, M length=100",
      "origin": "etc/fle2v2_generate_tests.py",
      "algo": "AES-256-CBC/SHA-256",
      "iv": "bc2f11970b4b6e01cb23b57d697dff5d",
      "aad": "080e6a6c5c1c6af249460eade36f7b0459881e65",
      "key": "75b5dc7b15c76bb30707d0587562a96111116cd8befcada105c6e4238ef3bb045eb78f724c44100278687067b7636986dbb40793a5b9e58ff79bb1b561fad9d90000000000000000000000000000000000000000000000000000000000000000",
      "plaintext": "b70fc343ebaeeea399ec9ca4e41c1b5d09a6f403dc7457a3ea9a6ec04189925584bf0ac2e63034137b1c2e218cc3dae823e6d82ab2aca85d7553da477aa18fb472a2d6ac8547b6fff9c88534e56f29ccd447a5cf3ecae02992ac873d473a34ee1729bb3b",
      "ciphertext": "bc2f11970b4b6e01cb23b57d697dff5ded97a254c782cca5449a73c2098579fec3b850b219c4f8c1fefc213153a75fdfe83baa2bc0ecbbd1f6d4f4c291d245142a21d4f42bfff8b81241af24e24f037bd2f2ba30c4369825b04b2e8f72ef7da047443948ac503b46eacb8dc89a44f9b70b0d2986c56ed8d3b8b383b9aabb630d2785bb76dd9bd229179e28e635693caaf2a82c65db13e437bcbf60803df69638"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/mcgrew.json000066400000000000000000000023471521103432300225670ustar00rootroot00000000000000[
   {
      "name": "mcgrew aes-256-cbc sha-512-256",
      "algo": "AES-256-CBC/SHA-512-256",
      "origin": "https://datatracker.ietf.org/doc/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05#section-5.4",
      "key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f0000000000000000000000000000000000000000000000000000000000000000",
      "aad": "546865207365636f6e64207072696e6369706c65206f662041756775737465204b6572636b686f666673",
      "iv": "1af38c2dc2b96ffdd86694092341bc04",
      "plaintext": "41206369706865722073797374656d206d757374206e6f7420626520726571756972656420746f206265207365637265742c20616e64206974206d7573742062652061626c6520746f2066616c6c20696e746f207468652068616e6473206f662074686520656e656d7920776974686f757420696e636f6e76656e69656e6365",
      "ciphertext": "1af38c2dc2b96ffdd86694092341bc044affaaadb78c31c5da4b1b590d10ffbd3dd8d5d302423526912da037ecbcc7bd822c301dd67c373bccb584ad3e9279c2e6d12a1374b77f077553df829410446b36ebd97066296ae6427ea75c2e0846a11a09ccf5370dc80bfecbad28c73f09b3a3b75e662a2594410ae496b2e2e6609e31e6e02cc837f053d21f37ff4f51950bbe2638d09dd7a4930930806d0703b1f64dd3b4c088a7f45c216839645b2012bf2e6269a8c56a816dbc1b267761955bc5"
   }
]
libmongocrypt-1.19.0/test/data/roundtrip/nist.json000066400000000000000000000016101521103432300222500ustar00rootroot00000000000000[
   {
      "name": "NIST AES-256-CTR/NONE",
      "origin": "NIST SP 800-38A section F.5.5",
      "algo": "AES-256-CTR/NONE",
      "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
      "iv": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
      "plaintext":  "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
      "ciphertext": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6"
   },
   {
      "name": "Not 64 byte aligned input",
      "algo": "AES-256-CTR/NONE",
      "key": "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
      "iv": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
      "plaintext": "AAAA",
      "ciphertext": "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeffA175"
   }
]

libmongocrypt-1.19.0/test/data/schema-broker/000077500000000000000000000000001521103432300210765ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/schema-broker/collinfo-encryptedFields.json000066400000000000000000000020441521103432300267200ustar00rootroot00000000000000{
    "name": "coll",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.coll.esc",
            "ecocCollection": "enxcol_.coll.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    },
                    "path": "encryptedIndexed",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 8
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "EbcEFH/bSl+ToU4flzUn3w==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/schema-broker/collinfo-encryptedFields2.json000066400000000000000000000020501521103432300267770ustar00rootroot00000000000000{
    "name": "coll2",
    "type": "collection",
    "options": {
        "encryptedFields": {
            "escCollection": "enxcol_.coll2.esc",
            "ecocCollection": "enxcol_.coll2.ecoc",
            "fields": [
                {
                    "keyId": {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    },
                    "path": "encryptedIndexed2",
                    "bsonType": "string",
                    "queries": {
                        "queryType": "equality",
                        "contention": 8
                    }
                }
            ]
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "EbcEFH/bSl+ToU4flzUn3w==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/schema-broker/collinfo-jsonSchema.json000066400000000000000000000022161521103432300256670ustar00rootroot00000000000000{
    "name": "coll",
    "type": "collection",
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "encrypted": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    },
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "KFfLQpZgQPiyhXHLIiHwDg==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/schema-broker/collinfo-noSchema.json000066400000000000000000000005751521103432300253400ustar00rootroot00000000000000{
    "name": "coll",
    "type": "collection",
    "options": {},
    "info": {
        "readOnly": false,
        "uuid": {
            "$binary": {
                "base64": "e5Xykq4/RBatC025ofA2yQ==",
                "subType": "04"
            }
        }
    },
    "idIndex": {
        "v": 2,
        "key": {
            "_id": 1
        },
        "name": "_id_"
    }
}
libmongocrypt-1.19.0/test/data/schema-broker/create-with-jsonSchema.json000066400000000000000000000011501521103432300262720ustar00rootroot00000000000000{
   "create": "coll",
   "validator": {
      "$jsonSchema": {
         "properties": {
            "encrypted": {
               "encrypt": {
                  "keyId": [
                     {
                        "$binary": {
                           "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                           "subType": "04"
                        }
                     }
                  ],
                  "bsonType": "string",
                  "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
               }
            }
         },
         "bsonType": "object"
      }
   }
}
libmongocrypt-1.19.0/test/data/schema-broker/encryptedFields.json000066400000000000000000000007571521103432300251260ustar00rootroot00000000000000{
    "escCollection": "enxcol_.coll.esc",
    "ecocCollection": "enxcol_.coll.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                    "subType": "04"
                }
            },
            "path": "encryptedIndexed",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": 8
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/schema-broker/encryptedFields2.json000066400000000000000000000007621521103432300252040ustar00rootroot00000000000000{
    "escCollection": "enxcol_.coll2.esc",
    "ecocCollection": "enxcol_.coll2.ecoc",
    "fields": [
        {
            "keyId": {
                "$binary": {
                    "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                    "subType": "04"
                }
            },
            "path": "encryptedIndexed2",
            "bsonType": "string",
            "queries": {
                "queryType": "equality",
                "contention": 8
            }
        }
    ]
}
libmongocrypt-1.19.0/test/data/schema-broker/encryptedFieldsMap.json000066400000000000000000000022351521103432300255550ustar00rootroot00000000000000{
    "db.coll": {
        "escCollection": "enxcol_.coll.esc",
        "ecocCollection": "enxcol_.coll.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                        "subType": "04"
                    }
                },
                "path": "encryptedIndexed",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 8
                }
            }
        ]
    },
    "db.coll2": {
        "escCollection": "enxcol_.coll2.esc",
        "ecocCollection": "enxcol_.coll2.ecoc",
        "fields": [
            {
                "keyId": {
                    "$binary": {
                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                        "subType": "04"
                    }
                },
                "path": "encryptedIndexed2",
                "bsonType": "string",
                "queries": {
                    "queryType": "equality",
                    "contention": 8
                }
            }
        ]
    }
}
libmongocrypt-1.19.0/test/data/schema-broker/jsonSchema.json000066400000000000000000000007701521103432300240670ustar00rootroot00000000000000{
    "properties": {
        "encrypted": {
            "encrypt": {
                "keyId": [
                    {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    }
                ],
                "bsonType": "string",
                "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
        }
    },
    "bsonType": "object"
}
libmongocrypt-1.19.0/test/data/schema-broker/jsonSchema2.json000066400000000000000000000007711521103432300241520ustar00rootroot00000000000000{
    "properties": {
        "encrypted2": {
            "encrypt": {
                "keyId": [
                    {
                        "$binary": {
                            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                            "subType": "04"
                        }
                    }
                ],
                "bsonType": "string",
                "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
            }
        }
    },
    "bsonType": "object"
}
libmongocrypt-1.19.0/test/data/schema-broker/schemaMap.json000066400000000000000000000022451521103432300236720ustar00rootroot00000000000000{
    "db.coll": {
        "properties": {
            "encrypted": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        },
        "bsonType": "object"
    },
    "db.coll2": {
        "properties": {
            "encrypted2": {
                "encrypt": {
                    "keyId": [
                        {
                            "$binary": {
                                "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                "subType": "04"
                            }
                        }
                    ],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        },
        "bsonType": "object"
    }
}
libmongocrypt-1.19.0/test/data/schema-map.json000066400000000000000000000015041521103432300212620ustar00rootroot00000000000000{
  "test.test": {
    "properties": {
      "ssn": {
        "encrypt": {
          "keyId": [
            {
              "$binary": {
                "base64": "AAAAAAAAAAAAAAAAAAAAAA==",
                "subType": "04"
              }
            }
          ],
          "bsonType": "string",
          "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
        }
      }
    },
    "bsonType": "object"
  },
  "test.test2": {
    "properties": {
      "ssn": {
        "encrypt": {
          "keyId": [
            {
              "$binary": {
                "base64": "AAAAAAAAAAAAAAAAAAAAAA==",
                "subType": "04"
              }
            }
          ],
          "bsonType": "string",
          "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
        }
      }
    },
    "bsonType": "object"
  }
}libmongocrypt-1.19.0/test/data/tokens/000077500000000000000000000000001521103432300176575ustar00rootroot00000000000000libmongocrypt-1.19.0/test/data/tokens/README.md000066400000000000000000000036101521103432300211360ustar00rootroot00000000000000Documents in this test data directory provide inputs and corresponding expected outputs for token derivation functions in libmongocrypt.
Each document is a single-depth set of key value pairs. All values are 32 octet hexit string except `counter` which is numeric.

[server.json](server.json) comes from [TEST(FLETokens, TestVectors)](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/fle_crypto_test.cpp).

| field | Description |
| ----- | ----------- |
| root | The base key from which all other tokens are derived. |
| value | Data used in `DerivedFromData` tokens. |
| contentionFactor | Data used in `DerivedFromDataAndContentionFactor` tokens. |
| collectionsLevel1Token | HMAC(root, 1) |
| serverTokenDerivationLevel1Token | HMAC(root, 2) |
| serverDataEncryptionLevel1Token | HMAC(root, 3) |
| EDCToken | HMAC(collectionsLevel1Token, 1) |
| ESCToken | HMAC(collectionsLevel1Token, 2) |
| ECCToken | HMAC(collectionsLevel1Token, 3) |
| ECOCToken | HMAC(collectionsLevel1Token, 4) |
| EDCDerivedFromDataToken | HMAC(EDCToken, value) |
| ESCDerivedFromDataToken | HMAC(ESCToken, value) |
| ECCDerivedFromDataToken | HMAC(ECCToken, value) |
| EDCDerivedFromDataTokenAndContentionFactor| HMAC(EDCDerivedFromDataToken, contentionFactor) |
| ESCDerivedFromDataTokenAndContentionFactor| HMAC(ESCDerivedFromDataToken, contentionFactor) |
| ECCDerivedFromDataTokenAndContentionFactor| HMAC(ECCDerivedFromDataToken, contentionFactor) |
| EDCTwiceDerivedToken | HMAC(EDCDerivedFromDataTokenAndContentionFactor, 1) |
| ESCTwiceDerivedTagToken | HMAC(ESCDerivedFromDataTokenAndContentionFactor, 1) |
| ESCTwiceDerivedValueToken | HMAC(ESCDerivedFromDataTokenAndContentionFactor, 2) |
| serverDerivedFromDataToken | HMAC(serverTokenDerivationLevel1Token, value) |
| serverCountAndContentionFactorEncryptionToken | HMAC(serverDerivedFromDataToken, 1) |
| serverZerosEncryptionToken | HMAC(serverDerivedFromDataToken, 2) |

libmongocrypt-1.19.0/test/data/tokens/mc.json000066400000000000000000000134121521103432300211520ustar00rootroot00000000000000{
   "root": "6eda88c8496ec990f5d5518dd2ad6f3d9c33b6055904b120f12de82911fbd933",
   "value": "c07c0df51257948e1a0fc70dd4568e3af99b23b3434c9858237ca7db62db9766",
   "contentionFactor": 1234567890,
   "collectionsLevel1Token": "ff2103ff205a36f39704f643c270c129919f008c391d9589a6d2c86a7429d0d3",
   "serverDataEncryptionLevel1Token": "d915ccc1eb81687fb5fc5b799f48c99fbe17e7a011a46a48901b9ae3d790656b",
   "ServerTokenDerivationLevel1Token": "1adc114b462741e6ac9f52eacf3dcb8cbca19827693c571d9418fda570c29d82",
   "EDCToken": "167d2d2ff8e4144df37ff759db593fde0ecc7d9636f96d62dacad672eccad349",
   "ESCToken": "bfd480f1658f49f48985734737bc07d0bc36b88210277605c55ff3c9c3ef50b0",
   "ECCToken": "9d34f9c182d75a5a3347c2f903e3e647105c651d52cf9555c9420ba07ddd3aa2",
   "ECOCToken": "e354e3b05e81e08b970ca061cb365163fd33dec2f982ddf9440e742ed288a8f8",
   "EDCDerivedFromDataToken": "53eaa4c23a3ff65e6b7c7dbc4b1389cf0a6151b1ede5383a0673ff9c67855ff9",
   "ESCDerivedFromDataToken": "acb3fab332131bbeaf112814f29ae0f2b10e97dc94b62db56c594661248e7467",
   "ECCDerivedFromDataToken": "826cfd35c35dcc7d4fbe13f33a3520749853bd1ea4c47919482252fba3a70cec",
   "serverDerivedFromDataToken": "4a671dbf25d68b6c040a077dabb4e63869e03f4d466803609233b16356ec6d66",
   "EDCDerivedFromDataTokenAndContentionFactor": "70fb9a3f760996f2f1438c5bf2a4d52bcba01b0badc3596276f49ffb2f0b136e",
   "ESCDerivedFromDataTokenAndContentionFactor": "7076c7b05fb4be4fe585eed930b852a6d088a0c55f3c96b50069e8a26ebfb347",
   "ECCDerivedFromDataTokenAndContentionFactor": "6c6a349956c19f9c5e638e612011a71fbb71921edb540310c17cd0208b7f548b",
   "EDCTwiceDerivedToken": "3643fd370e2719c03234cdeec787dfdc7d8fceecafa8a992e3c1f9d4d53449fe",
   "ESCTwiceDerivedTagToken": "c73bc4ff5e70222c653140b2b4998b4d62db973f20f116f66ff811a9a907a78f",
   "ESCTwiceDerivedValueToken": "34150c6f5ab56dc39ddb935accb7f53e5276322fa937650b76a4dda9723d6fba",
   "serverCountAndContentionFactorEncryptionToken": "a8c4a477e5518a527f718a5a1530919f2dad13b1f3559833e17b479711cbdbb9",
   "serverZerosEncryptionToken": "95fda1e139ef1dc84260d4d3ebaab7627a9676f4af7aeb1b596c7c14a0b3a2ea",
   "AnchorPaddingTokenRoot": "d5072cc56d9241470b9771292c3f75d950cba1cf842ca6707d1adfec5ec6f3e7",
   "AnchorPaddingKeyToken": "4665ff3ea2d8268d3e6c5d85ddbe6a79d74c45898012afdd4f5d2afb4472957e",
   "AnchorPaddingValueToken": "38cba0ac9914890d646387c9796edbba771b039ce9615457d7cc169df502f78f",
   "EDCTextExactToken": "be75dff4bbe8671213ea36fcb716f783af51fa00477fdea2558284b8b6d6b290",
   "EDCTextSubstringToken": "7eff82fa8a912a3cc60be036b0fddbbbcac45366e4cdf41090d49d770d2b28ef",
   "EDCTextSuffixToken": "d72607e0c681b39af3769ea93fc7c6b533c8963669a2f5f3d4205469d8e2977a",
   "EDCTextPrefixToken": "26c020f4bb3e50cb094f29da9e40b99fa9a862ea6df9e14a4838d22089f5e48c",
   "ESCTextExactToken": "591d0e6c50f38bbac01ebbff96b2f602eefbbb138c674fcf1261dcb26508ac36",
   "ESCTextSubstringToken": "38511f2fc6a90259b1d66f161f4f01893437703b8e07be7013ab2ebe1a5199fd",
   "ESCTextSuffixToken": "574e5d597c469798871e349e3f4151a9a75aa4fca438e6ffa19020a9a4d239df",
   "ESCTextPrefixToken": "700b0a35a12f36b5511ed3ea2ca8f7fd41386a6d6d45e5c028edca240ba617d6",
   "ServerTextExactToken": "1b0b904479d408fe805b14cd8f269b7aa9f022128816da09bc94ddd7021aa2ee",
   "ServerTextSubstringToken": "dc79b64dab53c09dcbcc2c203e1ed5d88e0fed75b1e3a3a3f192ead85d762e6a",
   "ServerTextSuffixToken": "a69ea838e79f9c11e2d6bd54e665bf0eee4864d5e517a7c9e9fb763e1a187cbe",
   "ServerTextPrefixToken": "b89b65fc9274678e1fe107ff8ae485f6ba46400870f87ba08c710bd605792cfc",
   "EDCTextExactDerivedFromDataToken": "a76c2da86efb5118f572180c0899a4701e574e7ca27ef972f31406cec61d96ca",
   "EDCTextSubstringDerivedFromDataToken": "b30c36483986fdc61a11d84a94579e20afcb07baf042688b534d6beb957d1305",
   "EDCTextSuffixDerivedFromDataToken": "d93b492c3c6b46971504bfdda134a4a887e0ccef9ce2bc450c51ce9a63c13125",
   "EDCTextPrefixDerivedFromDataToken": "a452a61a74f5dbf3c321b2a9e24a9d0554e0ce192f355aa11c5489bc76e925a0",
   "EDCTextExactDerivedFromDataTokenAndContentionFactorToken": "5f073c558114c2ff39a56e837d64f64c68328b14ebe2f6f142304e966cb618c4",
   "EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken": "ac333c5086216d14bcbe9996dc7c9c815625aeef47e6c08d83b348dd6bb62834",
   "EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken": "bd9a705c59a292635101523014ae997f2f56b8ba2ab4df3573c8f2524f5a9e50",
   "EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken": "06c4e44724305bb706e404c1caae6fff604c2b0c6809c3bcf79658ff3c93b951",
   "ESCTextExactDerivedFromDataToken": "e198e94d4c07083a48bce1cb24202c7215cceee3b43b9d7c6b7114045eb2336c",
   "ESCTextSubstringDerivedFromDataToken": "f3501ae0e842a204553e0b930563a69d6b260a54855bccd3c38cdf3fc5aa5de9",
   "ESCTextSuffixDerivedFromDataToken": "c9b245e4f0bf009484cccac911bb82ea0eee9da782fc57325f62e60cc7cffb79",
   "ESCTextPrefixDerivedFromDataToken": "7ed9d36faf86cc5e11c1ea7da839ff019e0f87949c16dfefcdafcb684aa2022b",
   "ESCTextExactDerivedFromDataTokenAndContentionFactorToken": "680b35d73a3f778792b8a03df267aa945cb9718ed4e6925c9da069d354032b7a",
   "ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken": "380c456ea3c2b9d80324556931764ea22c5e097b2702e658dfe81254b1ec9688",
   "ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken": "db3418028ec9079fe27643071d575e9f6e0607bdfee185d1380a68921ce58ff6",
   "ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken": "263cd30dbdce8a4d00bcd24b45b2d40058646f1aa9fb38313c3162b1a455889b",
   "ServerTextExactDerivedFromDataToken": "42dde13769bb2f18d35654fc673172b82db7fdcc62fb3ddf0767953bddc7e981",
   "ServerTextSubstringDerivedFromDataToken": "7d474158a817b9c6d45c497d591d6bc1d1f4f40066c40eaaf7e7f3c3ce4e11f0",
   "ServerTextSuffixDerivedFromDataToken": "3523904f3d15e552cea7c8643396b8090ba591a394eb91921c8b33e6fe07b0a1",
   "ServerTextPrefixDerivedFromDataToken": "854a5420921027c2a96415191a122d82a6f90d21ecef070e44174527eb4d7f2b"
}libmongocrypt-1.19.0/test/data/tokens/server.json000066400000000000000000000134121521103432300220610ustar00rootroot00000000000000{
   "root": "e3d9219f79586582112740b2add88e1030d91926ae8afc13ee575cfb8bb965b7",
   "value": "c07c0df51257948e1a0fc70dd4568e3af99b23b3434c9858237ca7db62db9766",
   "contentionFactor": 1234567890,
   "collectionsLevel1Token": "BD53ACAC665EDD01E0CA30CB648B2B8F4967544047FD4E7D12B1A9BF07339928",
   "ServerTokenDerivationLevel1Token": "C17FDF249DE234F9AB15CD95137EA7EC82AE4E5B51F6BFB0FC1B8FEB6800F74C",
   "serverDataEncryptionLevel1Token": "EB9A73F7912D86A4297E81D2F675AF742874E4057E3A890FEC651A23EEE3F3EC",
   "EDCToken": "82B0AB0F8F1D31AEB6F4DBC915EF17CBA2FE21E36EC436984EB63BECEC173831",
   "ESCToken": "279C575B52B73677EEF07D9C1126EBDF08C35369570A9B75E44A9AFDCCA96B6D",
   "ECCToken": "C58F671F04A8CFDD8FB1F718F563139F1286D7950E97C0C4A94EDDF0EDB127FE",
   "ECOCToken": "9E837ED3926CB8ED680E0E7DCB2A481A3E398BE7851FA1CE4D738FA5E67FFCC9",
   "EDCDerivedFromDataToken": "CEA098AA664E578D4E9CE05B50ADD15DF2F0316CD5CCB08E720C61D8C7580E2A",
   "ESCDerivedFromDataToken": "DE6A1AC292BC62094C33E94647B044B9B10514317B75F4128DDA2E0FB686704F",
   "ECCDerivedFromDataToken": "9A95D4F44734447E3F0266D1629513A0B7698CCE8C1524F329CE7970627FFD06",
   "serverDerivedFromDataToken": "EDBC92F3BFE4CCB3F088FED8D42379A83F26DC37F2B6D513D4F568A6F32C8C80",
   "EDCDerivedFromDataTokenAndContentionFactor": "D8CC38AE6A64BD1BF195A2D35734C13AF2B1729AD1052A81BE00BF29C67A696E",
   "ESCDerivedFromDataTokenAndContentionFactor": "8AAF04CBA6DC16BFB37CADBA43DCA66C183634CB3DA278DE174556AE6E17CEBB",
   "ECCDerivedFromDataTokenAndContentionFactor": "E9580F805E0D07AF384EBA185384F28A49C3DB93AFA4A187A1F4DA129271D82C",
   "EDCTwiceDerivedToken": "B39A7EC33FD976EFB8EEBBBF3A265A933E2128D709BB88C77E3D42AA735F697C",
   "ESCTwiceDerivedTagToken": "D6F76A9D4767E0889B709517C8CF0412D81874AEB6E6CEBFBDDFF7B013EB7154",
   "ESCTwiceDerivedValueToken": "53F0A51A43447B9881D5E79BA4C5F78E80BC2BC6AA42B00C81079EBF4C9D5A7C",
   "serverCountAndContentionFactorEncryptionToken": "2F30DBCC06B722B60BC1FF018FC28D5FAEE2F222496BE34A264EF3267E811DA0",
   "serverZerosEncryptionToken": "986F23F132FF7F14F748AC69373CFC982AD0AD4BAD25BE92008B83AB43E96029",
   "AnchorPaddingTokenRoot": "4312890F621FE3CA7497C3405DFD8AAF46A578C77F7404D28C12BA853A4D3327",
   "AnchorPaddingKeyToken": "EF6D80379C462FC724CE8C245DC177ED507154B4EBB04DED780FA0DDAF1A2247",
   "AnchorPaddingValueToken": "A3308597F3C5271D5BAB640F749E619E9272A2C33F4CD372680F55F84CC4DF7F",
   "EDCTextExactToken": "17dde6bd0c0d783aa2bf84e255b162b9362032e5ebd6d655d6b478c4d77dc077",
   "EDCTextSubstringToken": "4dde679aa0568701a0fda6b1cae21e99da32500541e4ad832ea83db94497478f",
   "EDCTextSuffixToken": "61fa8b8f02a5e7f3cfd2c3e58d3fb8c2d1bfe8a1acc32e43f26478a52944af78",
   "EDCTextPrefixToken": "926e96d7142b2d187d10579a26a11499d6c30aac2e3fdd56eb1cd536875decfd",
   "ESCTextExactToken": "2fea10a92e84cce913ea0ffd7fd59964507e9e96cdffa4f1b861521f3e653260",
   "ESCTextSubstringToken": "d6a94cacc9f5dd10b2b980bd4c4044e16ff1b29ad50c692e603487c46cbe610e",
   "ESCTextSuffixToken": "336f39da6fa984a3477261ea19147b77f02e843f82511c94a91ec77bd72dca68",
   "ESCTextPrefixToken": "2575c2a275e8135ec73093ae2edc793f6a3a0b8ed89767c3a8695ad93f1c6e9e",
   "ServerTextExactToken": "2ae33773be27c9fe3522ff0459f621670e93a7423166e63e9a43687a503c7438",
   "ServerTextSubstringToken": "183ff338509675d5377d09978e9becd31b197458e2c8cd45b670c672ceb6dc53",
   "ServerTextSuffixToken": "baf7c9392bb37607c6aa1f04163f4db8628872e7e7122e754bcd72771934ccbd",
   "ServerTextPrefixToken": "52387ea6942d9299a89f70a2a4d8a4209ea1dfe56e29f6c0e3182ca7818b5c9c",
   "EDCTextExactDerivedFromDataToken": "6006d61e9e985ee8f6490aebf0bfd120bf7a94317646165584894da6ecbf0e97",
   "EDCTextSubstringDerivedFromDataToken": "4dab3b274aa5d5e99ecbd7f139cd420f25305c9f082b25aa96f1c9fe6e2122e3",
   "EDCTextSuffixDerivedFromDataToken": "997f4a0b518f6c872951b65dcf26676d3886c98502fe0a7cc4f9c5ea4f30e94b",
   "EDCTextPrefixDerivedFromDataToken": "d3ebdd34bcacd11c5929a09d2a897eb7938abd4756cab805f46ff0f4affa7583",
   "EDCTextExactDerivedFromDataTokenAndContentionFactorToken": "3551507189d32cc7768390cdd83071deeb3055ca86c4756b16bb740024b23610",
   "EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken": "4b52f6daf3b688971eb5819820c3468b3c79ba45fd3e86134351f9baf203e0d1",
   "EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken": "c1f273913631a9a1b36fea884b3c8a3a141c9e21556981094ba15f262e540ac7",
   "EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken": "a8d68ebdb0e2d31754f693c56071b0b1d225e6ec5568aff875bd4f7c48c24f66",
   "ESCTextExactDerivedFromDataToken": "6d3311b3de0e32dbfe55a565327ad4b99d670474ddef52aa200fc79d76b8c7c4",
   "ESCTextSubstringDerivedFromDataToken": "0e9d0f7c42658dd6894d3cbe34ffa0d39be00ba72a21ac79bc3712b25783d247",
   "ESCTextSuffixDerivedFromDataToken": "a95860b4a08b39b002e5aee557268786be4e6d5a8552090dc397fbdb34374313",
   "ESCTextPrefixDerivedFromDataToken": "8df7341dd42c1cee8411657afaea424bfa818a539bf0668e1c355fc2a555e11a",
   "ESCTextExactDerivedFromDataTokenAndContentionFactorToken": "4cd82a4883ab0a6224a24937066f94827f5107e8bb2f0fa841e10aff8d49e8e4",
   "ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken": "94f116183f14442e335902756e5b730683cd998c683b90d04dc9e8f48684bdb2",
   "ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken": "13390024c674c2d131771cf95af9787c8c7ef76b5d63078b3dc482cb4075a634",
   "ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken": "9d0d5f84de779360c43be8e61710deb4b2705b6bf1e11ee41fba09fd4486b7c6",
   "ServerTextExactDerivedFromDataToken": "ae210cd90c230bb8cb96a4e840c9cf88740ae156f3514260d3f1ce94b0bf941e",
   "ServerTextSubstringDerivedFromDataToken": "7b514ede2cd6d364f2da2580bf173a4e68f9e0617c123aee8b9dda4d8d4b47d7",
   "ServerTextSuffixDerivedFromDataToken": "3e242be8d4c8a5894e81aa5fe0729cf48355dbe219c5c6b5ceb8b0eef124ba40",
   "ServerTextPrefixDerivedFromDataToken": "8d8d41ac0618b0e98b086d662a2466f4aa1527d6536acdbcf220c724073331eb"
}libmongocrypt-1.19.0/test/example-no-bson.c000066400000000000000000000013431521103432300206140ustar00rootroot00000000000000/*
 * 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 
#include 

int main() {
    printf("mongocrypt_version=%s\n", mongocrypt_version(0));
    return 0;
}
libmongocrypt-1.19.0/test/example-state-machine.c000066400000000000000000000263051521103432300217700ustar00rootroot00000000000000
/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 
#include 
#include 

#include 
#include 

static void _log_to_stderr(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) {
    switch (level) {
    case MONGOCRYPT_LOG_LEVEL_FATAL: fprintf(stderr, "FATAL"); break;
    case MONGOCRYPT_LOG_LEVEL_ERROR: fprintf(stderr, "ERROR"); break;
    case MONGOCRYPT_LOG_LEVEL_WARNING: fprintf(stderr, "WARNING"); break;
    case MONGOCRYPT_LOG_LEVEL_INFO: fprintf(stderr, "INFO"); break;
    case MONGOCRYPT_LOG_LEVEL_TRACE: fprintf(stderr, "TRACE"); break; /* UNUSED */
    default: fprintf(stderr, "UNKNOWN"); break;
    }
    fprintf(stderr, " %s\n", message);
}

static void _load_json_as_bson(const char *path, bson_t *as_bson) {
    bson_error_t error;
    bson_json_reader_t *reader;

    reader = bson_json_reader_new_from_file(path, &error);
    if (!reader) {
        fprintf(stderr, "could not open: %s\n", path);
        abort();
    }
    bson_init(as_bson);
    if (!bson_json_reader_read(reader, as_bson, &error)) {
        fprintf(stderr, "could not read json from: %s\n", path);
        abort();
    }

    bson_json_reader_destroy(reader);
}

static mongocrypt_binary_t *_read_json(const char *path, uint8_t **data) {
    bson_t as_bson;
    uint32_t len;

    _load_json_as_bson(path, &as_bson);

    *data = bson_destroy_with_steal(&as_bson, true, &len);
    return mongocrypt_binary_new_from_data(*data, len);
}

static mongocrypt_binary_t *_read_http(const char *path, uint8_t **data) {
    int fd;
    char *contents = NULL;
    int n_read;
    int filesize = 0;
    char storage[512];
    int i;
    uint32_t len;

    fd = open(path, O_RDONLY);
    while ((n_read = read(fd, storage, sizeof(storage))) > 0) {
        filesize += n_read;
        contents = bson_realloc(contents, filesize);
        memcpy(contents + (filesize - n_read), storage, n_read);
    }

    if (n_read < 0) {
        fprintf(stderr, "failed to read %s\n", path);
        abort();
    }

    close(fd);
    len = 0;

    /* Copy and fix newlines: \n becomes \r\n. */
    *data = bson_malloc0(filesize * 2);
    BSON_ASSERT(*data);

    for (i = 0; i < filesize; i++) {
        if (contents[i] == '\n' && contents[i - 1] != '\r') {
            (*data)[len++] = '\r';
        }
        (*data)[len++] = contents[i];
    }

    bson_free(contents);
    return mongocrypt_binary_new_from_data(*data, len);
}

static void _print_binary_as_bson(mongocrypt_binary_t *binary) {
    bson_t as_bson;
    char *str;

    BSON_ASSERT(binary);

    bson_init_static(&as_bson, mongocrypt_binary_data(binary), mongocrypt_binary_len(binary));
    str = bson_as_relaxed_extended_json(&as_bson, NULL);
    printf("%s\n", str);
    bson_free(str);
}

static void _print_binary_as_text(mongocrypt_binary_t *binary) {
    uint32_t i;
    uint8_t *ptr;

    ptr = (uint8_t *)mongocrypt_binary_data(binary);
    for (i = 0; i < mongocrypt_binary_len(binary); i++) {
        if (ptr[i] == '\r') {
            printf("\\r");
        } else if (ptr[i] == '\n') {
            printf("\\n");
        } else {
            printf("%c", (char)ptr[i]);
        }
    }
    printf("\n");
}

#define CHECK(stmt)                                                                                                    \
    if (!stmt) {                                                                                                       \
        continue;                                                                                                      \
    } else                                                                                                             \
        ((void)0)

static void _run_state_machine(mongocrypt_ctx_t *ctx, bson_t *result) {
    mongocrypt_binary_t *input, *output = NULL;
    bson_t tmp;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_ctx_state_t state;
    mongocrypt_status_t *status;
    uint8_t *data;
    bool done;

    done = false;
    status = mongocrypt_status_new();
    bson_init(result);

    while (!done) {
        state = mongocrypt_ctx_state(ctx);
        switch (state) {
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
            output = mongocrypt_binary_new();
            CHECK(mongocrypt_ctx_mongo_op(ctx, output));
            printf("\nrunning listCollections on mongod with this filter:\n");
            _print_binary_as_bson(output);
            mongocrypt_binary_destroy(output);
            printf("\nmocking reply from file:\n");
            input = _read_json("./test/example/collection-info.json", &data);
            _print_binary_as_bson(input);
            CHECK(mongocrypt_ctx_mongo_feed(ctx, input));
            mongocrypt_binary_destroy(input);
            bson_free(data);
            CHECK(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
            output = mongocrypt_binary_new();
            CHECK(mongocrypt_ctx_mongo_op(ctx, output));
            printf("\nrunning cmd on mongocryptd with this schema:\n");
            _print_binary_as_bson(output);
            mongocrypt_binary_destroy(output);
            printf("\nmocking reply from file:\n");
            input = _read_json("./test/example/mongocryptd-reply.json", &data);
            _print_binary_as_bson(input);
            CHECK(mongocrypt_ctx_mongo_feed(ctx, input));
            mongocrypt_binary_destroy(input);
            bson_free(data);
            CHECK(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
            output = mongocrypt_binary_new();
            CHECK(mongocrypt_ctx_mongo_op(ctx, output));
            printf("\nrunning a find on the key vault coll with this filter:\n");
            _print_binary_as_bson(output);
            mongocrypt_binary_destroy(output);
            printf("\nmocking reply from file:\n");
            input = _read_json("./test/example/key-document.json", &data);
            _print_binary_as_bson(input);
            CHECK(mongocrypt_ctx_mongo_feed(ctx, input));
            mongocrypt_binary_destroy(input);
            bson_free(data);
            CHECK(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_KMS:
            while ((kms = mongocrypt_ctx_next_kms_ctx(ctx))) {
                output = mongocrypt_binary_new();
                CHECK(mongocrypt_kms_ctx_message(kms, output));
                printf("\nsending the following to kms:\n");
                _print_binary_as_text(output);
                mongocrypt_binary_destroy(output);
                printf("\nmocking reply from file\n");
                input = _read_http("./test/example/kms-decrypt-reply.txt", &data);
                _print_binary_as_text(input);
                CHECK(mongocrypt_kms_ctx_feed(kms, input));
                mongocrypt_binary_destroy(input);
                bson_free(data);
                assert(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
            }
            mongocrypt_ctx_kms_done(ctx);
            break;
        case MONGOCRYPT_CTX_READY:
            output = mongocrypt_binary_new();
            CHECK(mongocrypt_ctx_finalize(ctx, output));
            printf("\nfinal bson is:\n");
            _print_binary_as_bson(output);
            bson_init_static(&tmp, mongocrypt_binary_data(output), mongocrypt_binary_len(output));
            bson_destroy(result);
            bson_copy_to(&tmp, result);
            bson_destroy(&tmp);
            mongocrypt_binary_destroy(output);
            break;
        case MONGOCRYPT_CTX_DONE: done = true; break;
        case MONGOCRYPT_CTX_ERROR:
            mongocrypt_ctx_status(ctx, status);
            printf("\ngot error: %s\n", mongocrypt_status_message(status, NULL));
            abort();
        case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
            // We don't handle KMS credentials
            // fallthrough
        default: BSON_ASSERT(0);
        }
    }

    mongocrypt_status_destroy(status);
}

static mongocrypt_binary_t *_iter_to_binary(bson_iter_t *iter) {
    uint8_t *data;
    uint32_t len;

    BSON_ASSERT(BSON_ITER_HOLDS_BINARY(iter));
    bson_iter_binary(iter, NULL, &len, (const uint8_t **)&data);
    return mongocrypt_binary_new_from_data(data, len);
}

int main(void) {
    bson_iter_t iter;
    bson_t result;
    bson_t key_doc;
    bson_t *wrapped;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *msg;
    mongocrypt_binary_t *key_id;
    mongocrypt_binary_t *input;
    uint8_t *data;

    printf("******* ENCRYPTION *******\n\n");
    crypt = mongocrypt_new();
    mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1);
    mongocrypt_setopt_log_handler(crypt, _log_to_stderr, NULL);
    if (!mongocrypt_init(crypt)) {
        fprintf(stderr, "failed to initialize");
        abort();
    }

    ctx = mongocrypt_ctx_new(crypt);
    msg = _read_json("./test/example/cmd.json", &data);
    mongocrypt_ctx_encrypt_init(ctx, "test", -1, msg);
    mongocrypt_binary_destroy(msg);
    bson_free(data);
    _run_state_machine(ctx, &result);
    mongocrypt_ctx_destroy(ctx);

    printf("\n******* DECRYPTION *******\n\n");
    ctx = mongocrypt_ctx_new(crypt);
    input = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&result), result.len);
    mongocrypt_ctx_decrypt_init(ctx, input);
    mongocrypt_binary_destroy(input);
    bson_destroy(&result);
    _run_state_machine(ctx, &result);
    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);

    printf("\n******* EXPLICIT ENCRYPTION *******\n");

    ctx = mongocrypt_ctx_new(crypt);

    /* Explicit encryption requires a key_id option */
    _load_json_as_bson("./test/example/key-document.json", &key_doc);
    bson_iter_init_find(&iter, &key_doc, "_id");
    key_id = _iter_to_binary(&iter);
    mongocrypt_ctx_setopt_key_id(ctx, key_id);
    bson_destroy(&key_doc);
    mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANDOM_STR, -1);

    wrapped = BCON_NEW("v", "hello");
    msg = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(wrapped), wrapped->len);
    mongocrypt_ctx_explicit_encrypt_init(ctx, msg);
    mongocrypt_binary_destroy(msg);
    _run_state_machine(ctx, &result);
    mongocrypt_ctx_destroy(ctx);

    printf("\n******* EXPLICIT DECRYPTION *******\n");

    ctx = mongocrypt_ctx_new(crypt);
    input = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&result), result.len);
    mongocrypt_ctx_explicit_decrypt_init(ctx, input);
    mongocrypt_binary_destroy(input);
    bson_destroy(&result);
    _run_state_machine(ctx, &result);
    bson_destroy(&result);

    mongocrypt_ctx_destroy(ctx);
    bson_destroy(wrapped);
    mongocrypt_binary_destroy(key_id);
    mongocrypt_destroy(crypt);

    return 0;
}
libmongocrypt-1.19.0/test/example/000077500000000000000000000000001521103432300170765ustar00rootroot00000000000000libmongocrypt-1.19.0/test/example/README.md000066400000000000000000000005401521103432300203540ustar00rootroot00000000000000# libmongocrypt example data #

This directory contains a simple example of mocked responses to test libmongocrypt and driver wrappers. Data for other scenarios and edge cases is in the `data` directory.

The HTTP reply file, kms-decrypt-reply.txt, has regular newline endings \n that MUST be replaced by \r\n endings when reading the file for testing.libmongocrypt-1.19.0/test/example/cmd.json000066400000000000000000000001121521103432300205260ustar00rootroot00000000000000{
    "find": "test",
    "filter": {
        "ssn": "457-55-5462"
    }
}libmongocrypt-1.19.0/test/example/collection-info.json000066400000000000000000000020611521103432300230540ustar00rootroot00000000000000{
    "type": "collection",
    "name": "test",
    "idIndex": {
        "ns": "test.test",
        "name": "_id_",
        "key": {
            "_id": {
                "$numberInt": "1"
            }
        },
        "v": {
            "$numberInt": "2"
        }
    },
    "options": {
        "validator": {
            "$jsonSchema": {
                "properties": {
                    "ssn": {
                        "encrypt": {
                            "keyId": [
                                {
                                    "$binary": {
                                        "base64": "YWFhYWFhYWFhYWFhYWFhYQ==",
                                        "subType": "04"
                                    }
                                }
                            ],
                            "bsonType": "string",
                            "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                        }
                    }
                },
                "bsonType": "object"
            }
        }
    }
}libmongocrypt-1.19.0/test/example/encrypted-command.json000066400000000000000000000003501521103432300234000ustar00rootroot00000000000000{
  "filter": {
    "ssn": {
      "$binary": "AQAAAAAAAAAAAAAAAAAAAAACaWlpaWlpaWlpaWlpaWlpaSZ395PIDuuYBFAnTRUHO7xoDP9En3VnnXEo5HXgKNlAKsp9WCMnoR6Zn0y0HoR+q4T8XzjyuzN7t77qxYHM1zY=",
      "$type": "06"
    }
  },
  "find": "test"
}
libmongocrypt-1.19.0/test/example/key-document-custom-endpoint.json000066400000000000000000000021041521103432300255200ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "region": "us-east-1", 
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", 
        "provider": "aws",
        "endpoint": "example.com"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", 
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    },
    "keyAltNames": [ "keyDocumentName" ]
}libmongocrypt-1.19.0/test/example/key-document.json000066400000000000000000000020411521103432300223720ustar00rootroot00000000000000{
    "status": {
        "$numberInt": "1"
    }, 
    "_id": {
        "$binary": {
            "base64": "YWFhYWFhYWFhYWFhYWFhYQ==", 
            "subType": "04"
        }
    }, 
    "masterKey": {
        "region": "us-east-1", 
        "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", 
        "provider": "aws"
    }, 
    "updateDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    }, 
    "keyMaterial": {
        "$binary": {
            "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", 
            "subType": "00"
        }
    }, 
    "creationDate": {
        "$date": {
            "$numberLong": "1557827033449"
        }
    },
    "keyAltNames": [ "keyDocumentName" ]
}libmongocrypt-1.19.0/test/example/kms-decrypt-reply.txt000066400000000000000000000005561521103432300232400ustar00rootroot00000000000000HTTP/1.1 200 OK
x-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e
Content-Type: application/x-amz-json-1.1
Content-Length: 233

{"KeyId": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", "Plaintext": "TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR"}libmongocrypt-1.19.0/test/example/mongocryptd-reply.json000066400000000000000000000006611521103432300234720ustar00rootroot00000000000000{
    "schemaRequiresEncryption": true, 
    "ok": {
        "$numberInt": "1"
    }, 
    "result": {
        "filter": {
            "ssn": {
                "$binary": {
                    "base64": "ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA", 
                    "subType": "06"
                }
            }
        }, 
        "find": "test"
    }, 
    "hasEncryptedPlaceholders": true
}libmongocrypt-1.19.0/test/fuzz_kms.c000066400000000000000000000022421521103432300174570ustar00rootroot00000000000000#include "kms_message/kms_message.h"
#include "kms_message_private.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* Fuzzer for targeted the kms_response_parser_feed and
 * kms_request_new functions.
 */
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    kms_response_parser_t *parser = NULL;
    parser = kms_response_parser_new();
    if (parser != NULL) {
        kms_response_parser_feed(parser, data, size);
        kms_response_parser_destroy(parser);
    }

    if (size > 50) {
        /* Create two null-terminated strings */
        char *method = malloc(25);
        memcpy(method, data, 24);
        method[24] = '\0';
        data += 24;
        size -= 24;

        char *uri_path = malloc(25);
        memcpy(uri_path, data, 24);
        uri_path[24] = '\0';

        kms_request_t *request = NULL;
        request = kms_request_new(method, uri_path, NULL);
        if (request != NULL) {
            kms_request_destroy(request);
        }
        free(method);
        free(uri_path);
    }
    return 0;
}
libmongocrypt-1.19.0/test/test-dll.cpp000066400000000000000000000010511521103432300176740ustar00rootroot00000000000000/**
 * This file is not a test case. This file is to generate a dynamic library for
 * testing the dll loading code.
 */

#include 
#include 

static std::string global_str;

#if defined(_MSC_VER)
#define SAY_HELLO_EXPORT __declspec(dllexport)
#else
#define SAY_HELLO_EXPORT __attribute__((visibility("default")))
#endif

extern "C" {

SAY_HELLO_EXPORT int say_hello(); // -Wmissing-prototypes: for testing only.

int say_hello() {
    global_str = "Hello, DLL!";
    std::cout << global_str << "\n";
    return 42;
}

} // extern "C"
libmongocrypt-1.19.0/test/test-gcp-auth.c000066400000000000000000000243101521103432300202740ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

/* Test authentication with the "gcp" KMS provider. */

static void _test_createdatakey_with_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms;
    const char *kek = "{"
                      "'provider': 'gcp',"
                      "'projectId': 'test-projectId',"
                      "'location': 'test-location',"
                      "'keyRing': 'test-keyRing',"
                      "'keyName': 'test-keyName'"
                      "}";

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON_STR(kek)), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    /* Assert first CTX_NEED_KMS state requests access token. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("oauth2.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    /* Assert second CTX_NEED_KMS state requests encryption. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("cloudkms.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/encrypt-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_with_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_binary_t *uuid;
    const char *uuid_data = "\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61";

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    uuid = mongocrypt_binary_new_from_data((uint8_t *)uuid_data, UUID_LEN);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, uuid), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 1}")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-gcp.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    /* Assert first CTX_NEED_KMS state requests access token. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("oauth2.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    /* Assert second CTX_NEED_KMS state requests decryption. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("cloudkms.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    mongocrypt_binary_destroy(uuid);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_createdatakey_with_accesstoken(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms;
    const char *kek = "{"
                      "'provider': 'gcp',"
                      "'projectId': 'test-projectId',"
                      "'location': 'test-location',"
                      "'keyRing': 'test-keyRing',"
                      "'keyName': 'test-keyName'"
                      "}";

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'gcp': {}}")), crypt);
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON_STR(kek)), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    { ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx, TEST_BSON("{'gcp': { 'accessToken': 'foobar' } }")), ctx); }

    /* Assert first CTX_NEED_KMS state requests encryption. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("cloudkms.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/encrypt-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_with_accesstoken(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_binary_t *uuid;
    const char *uuid_data = "\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61";

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'gcp': {}}")), crypt);
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    uuid = mongocrypt_binary_new_from_data((uint8_t *)uuid_data, UUID_LEN);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, uuid), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 1}")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    { ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx, TEST_BSON("{'gcp': { 'accessToken': 'foobar' } }")), ctx); }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-gcp.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);

    /* Assert first CTX_NEED_KMS state requests decryption. */
    {
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(kms, "expected KMS context, got NULL");

        const char *endpoint;
        mongocrypt_kms_ctx_endpoint(kms, &endpoint);
        ASSERT_STREQUAL("cloudkms.googleapis.com:443", endpoint);

        /* Satisfy request. */
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kms);
        ASSERT_CMPINT((int)mongocrypt_kms_ctx_bytes_needed(kms), ==, 0);

        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OR_PRINT_MSG(NULL == kms, "expected NULL KMS context, got non-NULL");
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    mongocrypt_binary_destroy(uuid);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_gcp_auth(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_createdatakey_with_credentials);
    INSTALL_TEST(_test_encrypt_with_credentials);
    INSTALL_TEST(_test_createdatakey_with_accesstoken);
    INSTALL_TEST(_test_encrypt_with_accesstoken);
}
libmongocrypt-1.19.0/test/test-mc-cmp.c000066400000000000000000000246211521103432300177450ustar00rootroot00000000000000/*
 * Copyright 2018-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.
 */

// `test-mc-cmp.h` is a modified copy of `test-bson-cmp.c` from libbson 1.28.0.

#include "test-mongocrypt.h"

#include "mc-cmp-private.h"

static void test_mc_cmp_equal(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(mc_cmp_equal_ss(0, 0));
    ASSERT(!mc_cmp_equal_ss(0, -1));
    ASSERT(!mc_cmp_equal_ss(0, 1));
    ASSERT(!mc_cmp_equal_ss(-1, 0));
    ASSERT(mc_cmp_equal_ss(-1, -1));
    ASSERT(!mc_cmp_equal_ss(-1, 1));
    ASSERT(!mc_cmp_equal_ss(1, 0));
    ASSERT(!mc_cmp_equal_ss(1, -1));
    ASSERT(mc_cmp_equal_ss(1, 1));

    ASSERT(mc_cmp_equal_uu(0u, 0u));
    ASSERT(!mc_cmp_equal_uu(0u, 1u));
    ASSERT(!mc_cmp_equal_uu(1u, 0u));
    ASSERT(mc_cmp_equal_uu(1u, 1u));

    ASSERT(mc_cmp_equal_su(0, 0u));
    ASSERT(!mc_cmp_equal_su(0, 1u));
    ASSERT(!mc_cmp_equal_su(-1, 0u));
    ASSERT(!mc_cmp_equal_su(-1, 1u));
    ASSERT(!mc_cmp_equal_su(1, 0u));
    ASSERT(mc_cmp_equal_su(1, 1u));

    ASSERT(mc_cmp_equal_us(0u, 0));
    ASSERT(!mc_cmp_equal_us(0u, -1));
    ASSERT(!mc_cmp_equal_us(0u, 1));
    ASSERT(!mc_cmp_equal_us(1u, 0));
    ASSERT(!mc_cmp_equal_us(1u, -1));
    ASSERT(mc_cmp_equal_us(1u, 1));
}

static void test_mc_cmp_not_equal(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(!mc_cmp_not_equal_ss(0, 0));
    ASSERT(mc_cmp_not_equal_ss(0, -1));
    ASSERT(mc_cmp_not_equal_ss(0, 1));
    ASSERT(mc_cmp_not_equal_ss(-1, 0));
    ASSERT(!mc_cmp_not_equal_ss(-1, -1));
    ASSERT(mc_cmp_not_equal_ss(-1, 1));
    ASSERT(mc_cmp_not_equal_ss(1, 0));
    ASSERT(mc_cmp_not_equal_ss(1, -1));
    ASSERT(!mc_cmp_not_equal_ss(1, 1));

    ASSERT(!mc_cmp_not_equal_uu(0u, 0u));
    ASSERT(mc_cmp_not_equal_uu(0u, 1u));
    ASSERT(mc_cmp_not_equal_uu(1u, 0u));
    ASSERT(!mc_cmp_not_equal_uu(1u, 1u));

    ASSERT(!mc_cmp_not_equal_su(0, 0u));
    ASSERT(mc_cmp_not_equal_su(0, 1u));
    ASSERT(mc_cmp_not_equal_su(-1, 0u));
    ASSERT(mc_cmp_not_equal_su(-1, 1u));
    ASSERT(mc_cmp_not_equal_su(1, 0u));
    ASSERT(!mc_cmp_not_equal_su(1, 1u));

    ASSERT(!mc_cmp_not_equal_us(0u, 0));
    ASSERT(mc_cmp_not_equal_us(0u, -1));
    ASSERT(mc_cmp_not_equal_us(0u, 1));
    ASSERT(mc_cmp_not_equal_us(1u, 0));
    ASSERT(mc_cmp_not_equal_us(1u, -1));
    ASSERT(!mc_cmp_not_equal_us(1u, 1));
}

static void test_mc_cmp_less(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(!mc_cmp_less_ss(0, 0));
    ASSERT(!mc_cmp_less_ss(0, -1));
    ASSERT(mc_cmp_less_ss(0, 1));
    ASSERT(mc_cmp_less_ss(-1, 0));
    ASSERT(!mc_cmp_less_ss(-1, -1));
    ASSERT(mc_cmp_less_ss(-1, 1));
    ASSERT(!mc_cmp_less_ss(1, 0));
    ASSERT(!mc_cmp_less_ss(1, -1));
    ASSERT(!mc_cmp_less_ss(1, 1));

    ASSERT(!mc_cmp_less_uu(0u, 0u));
    ASSERT(mc_cmp_less_uu(0u, 1u));
    ASSERT(!mc_cmp_less_uu(1u, 0u));
    ASSERT(!mc_cmp_less_uu(1u, 1u));

    ASSERT(!mc_cmp_less_su(0, 0u));
    ASSERT(mc_cmp_less_su(0, 1u));
    ASSERT(mc_cmp_less_su(-1, 0u));
    ASSERT(mc_cmp_less_su(-1, 1u));
    ASSERT(!mc_cmp_less_su(1, 0u));
    ASSERT(!mc_cmp_less_su(1, 1u));

    ASSERT(!mc_cmp_less_us(0u, 0));
    ASSERT(!mc_cmp_less_us(0u, -1));
    ASSERT(mc_cmp_less_us(0u, 1));
    ASSERT(!mc_cmp_less_us(1u, 0));
    ASSERT(!mc_cmp_less_us(1u, -1));
    ASSERT(!mc_cmp_less_us(1u, 1));
}

static void test_mc_cmp_greater(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(!mc_cmp_greater_ss(0, 0));
    ASSERT(mc_cmp_greater_ss(0, -1));
    ASSERT(!mc_cmp_greater_ss(0, 1));
    ASSERT(!mc_cmp_greater_ss(-1, 0));
    ASSERT(!mc_cmp_greater_ss(-1, -1));
    ASSERT(!mc_cmp_greater_ss(-1, 1));
    ASSERT(mc_cmp_greater_ss(1, 0));
    ASSERT(mc_cmp_greater_ss(1, -1));
    ASSERT(!mc_cmp_greater_ss(1, 1));

    ASSERT(!mc_cmp_greater_uu(0u, 0u));
    ASSERT(!mc_cmp_greater_uu(0u, 1u));
    ASSERT(mc_cmp_greater_uu(1u, 0u));
    ASSERT(!mc_cmp_greater_uu(1u, 1u));

    ASSERT(!mc_cmp_greater_su(0, 0u));
    ASSERT(!mc_cmp_greater_su(0, 1u));
    ASSERT(!mc_cmp_greater_su(-1, 0u));
    ASSERT(!mc_cmp_greater_su(-1, 1u));
    ASSERT(mc_cmp_greater_su(1, 0u));
    ASSERT(!mc_cmp_greater_su(1, 1u));

    ASSERT(!mc_cmp_greater_us(0u, 0));
    ASSERT(mc_cmp_greater_us(0u, -1));
    ASSERT(!mc_cmp_greater_us(0u, 1));
    ASSERT(mc_cmp_greater_us(1u, 0));
    ASSERT(mc_cmp_greater_us(1u, -1));
    ASSERT(!mc_cmp_greater_us(1u, 1));
}

static void test_mc_cmp_less_equal(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(mc_cmp_less_equal_ss(0, 0));
    ASSERT(!mc_cmp_less_equal_ss(0, -1));
    ASSERT(mc_cmp_less_equal_ss(0, 1));
    ASSERT(mc_cmp_less_equal_ss(-1, 0));
    ASSERT(mc_cmp_less_equal_ss(-1, -1));
    ASSERT(mc_cmp_less_equal_ss(-1, 1));
    ASSERT(!mc_cmp_less_equal_ss(1, 0));
    ASSERT(!mc_cmp_less_equal_ss(1, -1));
    ASSERT(mc_cmp_less_equal_ss(1, 1));

    ASSERT(mc_cmp_less_equal_uu(0u, 0u));
    ASSERT(mc_cmp_less_equal_uu(0u, 1u));
    ASSERT(!mc_cmp_less_equal_uu(1u, 0u));
    ASSERT(mc_cmp_less_equal_uu(1u, 1u));

    ASSERT(mc_cmp_less_equal_su(0, 0u));
    ASSERT(mc_cmp_less_equal_su(0, 1u));
    ASSERT(mc_cmp_less_equal_su(-1, 0u));
    ASSERT(mc_cmp_less_equal_su(-1, 1u));
    ASSERT(!mc_cmp_less_equal_su(1, 0u));
    ASSERT(mc_cmp_less_equal_su(1, 1u));

    ASSERT(mc_cmp_less_equal_us(0u, 0));
    ASSERT(!mc_cmp_less_equal_us(0u, -1));
    ASSERT(mc_cmp_less_equal_us(0u, 1));
    ASSERT(!mc_cmp_less_equal_us(1u, 0));
    ASSERT(!mc_cmp_less_equal_us(1u, -1));
    ASSERT(mc_cmp_less_equal_us(1u, 1));
}

static void test_mc_cmp_greater_equal(_mongocrypt_tester_t *tester) {
    (void)tester;

    ASSERT(mc_cmp_greater_equal_ss(0, 0));
    ASSERT(mc_cmp_greater_equal_ss(0, -1));
    ASSERT(!mc_cmp_greater_equal_ss(0, 1));
    ASSERT(!mc_cmp_greater_equal_ss(-1, 0));
    ASSERT(mc_cmp_greater_equal_ss(-1, -1));
    ASSERT(!mc_cmp_greater_equal_ss(-1, 1));
    ASSERT(mc_cmp_greater_equal_ss(1, 0));
    ASSERT(mc_cmp_greater_equal_ss(1, -1));
    ASSERT(mc_cmp_greater_equal_ss(1, 1));

    ASSERT(mc_cmp_greater_equal_uu(0u, 0u));
    ASSERT(!mc_cmp_greater_equal_uu(0u, 1u));
    ASSERT(mc_cmp_greater_equal_uu(1u, 0u));
    ASSERT(mc_cmp_greater_equal_uu(1u, 1u));

    ASSERT(mc_cmp_greater_equal_su(0, 0u));
    ASSERT(!mc_cmp_greater_equal_su(0, 1u));
    ASSERT(!mc_cmp_greater_equal_su(-1, 0u));
    ASSERT(!mc_cmp_greater_equal_su(-1, 1u));
    ASSERT(mc_cmp_greater_equal_su(1, 0u));
    ASSERT(mc_cmp_greater_equal_su(1, 1u));

    ASSERT(mc_cmp_greater_equal_us(0u, 0));
    ASSERT(mc_cmp_greater_equal_us(0u, -1));
    ASSERT(!mc_cmp_greater_equal_us(0u, 1));
    ASSERT(mc_cmp_greater_equal_us(1u, 0));
    ASSERT(mc_cmp_greater_equal_us(1u, -1));
    ASSERT(mc_cmp_greater_equal_us(1u, 1));
}

static void test_mc_in_range(_mongocrypt_tester_t *tester) {
    (void)tester;

    const int64_t int8_min = INT8_MIN;
    const int64_t int8_max = INT8_MAX;
    const int64_t int32_min = INT32_MIN;
    const int64_t int32_max = INT32_MAX;

    const uint64_t uint8_max = UINT8_MAX;
    const uint64_t uint32_max = UINT32_MAX;

    const ssize_t ssize_min = SSIZE_MIN;
    const ssize_t ssize_max = SSIZE_MAX;

    ASSERT(!mc_in_range_signed(int8_t, int8_min - 1));
    ASSERT(mc_in_range_signed(int8_t, int8_min));
    ASSERT(mc_in_range_signed(int8_t, 0));
    ASSERT(mc_in_range_signed(int8_t, int8_max));
    ASSERT(!mc_in_range_signed(int8_t, int8_max + 1));

    ASSERT(mc_in_range_unsigned(int8_t, 0u));
    ASSERT(mc_in_range_unsigned(int8_t, (uint64_t)int8_max));
    ASSERT(!mc_in_range_unsigned(int8_t, (uint64_t)(int8_max + 1)));

    ASSERT(!mc_in_range_signed(uint8_t, int8_min - 1));
    ASSERT(!mc_in_range_signed(uint8_t, int8_min));
    ASSERT(mc_in_range_signed(uint8_t, 0));
    ASSERT(mc_in_range_signed(uint8_t, int8_max));
    ASSERT(mc_in_range_signed(uint8_t, int8_max + 1));
    ASSERT(mc_in_range_signed(uint8_t, (int64_t)uint8_max));
    ASSERT(!mc_in_range_signed(uint8_t, (int64_t)uint8_max + 1));

    ASSERT(mc_in_range_unsigned(uint8_t, 0u));
    ASSERT(mc_in_range_unsigned(uint8_t, uint8_max));
    ASSERT(!mc_in_range_unsigned(uint8_t, uint8_max + 1u));

    ASSERT(!mc_in_range_signed(int32_t, int32_min - 1));
    ASSERT(mc_in_range_signed(int32_t, int32_min));
    ASSERT(mc_in_range_signed(int32_t, 0));
    ASSERT(mc_in_range_signed(int32_t, int32_max));
    ASSERT(!mc_in_range_signed(int32_t, int32_max + 1));

    ASSERT(mc_in_range_unsigned(int32_t, 0u));
    ASSERT(mc_in_range_unsigned(int32_t, (uint64_t)int32_max));
    ASSERT(!mc_in_range_unsigned(int32_t, (uint64_t)(int32_max + 1)));

    ASSERT(!mc_in_range_signed(uint32_t, int32_min - 1));
    ASSERT(!mc_in_range_signed(uint32_t, int32_min));
    ASSERT(mc_in_range_signed(uint32_t, 0));
    ASSERT(mc_in_range_signed(uint32_t, int32_max));
    ASSERT(mc_in_range_signed(uint32_t, int32_max + 1));
    ASSERT(mc_in_range_signed(uint32_t, (int64_t)uint32_max));
    ASSERT(!mc_in_range_signed(uint32_t, (int64_t)uint32_max + 1));

    ASSERT(mc_in_range_unsigned(uint32_t, 0u));
    ASSERT(mc_in_range_unsigned(uint32_t, uint32_max));
    ASSERT(!mc_in_range_unsigned(uint32_t, uint32_max + 1u));

    ASSERT(mc_in_range_signed(ssize_t, ssize_min));
    ASSERT(mc_in_range_signed(ssize_t, 0));
    ASSERT(mc_in_range_signed(ssize_t, ssize_max));

    ASSERT(mc_in_range_unsigned(ssize_t, 0u));
    ASSERT(mc_in_range_unsigned(ssize_t, (size_t)ssize_max));
    ASSERT(!mc_in_range_unsigned(ssize_t, (size_t)ssize_max + 1u));

    ASSERT(!mc_in_range_signed(size_t, ssize_min));
    ASSERT(mc_in_range_signed(size_t, 0));
    ASSERT(mc_in_range_signed(size_t, ssize_max));

    ASSERT(mc_in_range_unsigned(size_t, 0u));
    ASSERT(mc_in_range_unsigned(size_t, (size_t)ssize_max));
    ASSERT(mc_in_range_unsigned(size_t, (size_t)ssize_max + 1u));
}

void _mongocrypt_tester_install_mc_cmp(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mc_cmp_equal);
    INSTALL_TEST(test_mc_cmp_not_equal);
    INSTALL_TEST(test_mc_cmp_less);
    INSTALL_TEST(test_mc_cmp_greater);
    INSTALL_TEST(test_mc_cmp_less_equal);
    INSTALL_TEST(test_mc_cmp_greater_equal);
    INSTALL_TEST(test_mc_in_range);
}
libmongocrypt-1.19.0/test/test-mc-efc.c000066400000000000000000000162431521103432300177240ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

#include "mc-efc-private.h"

static void _load_test_file(_mongocrypt_tester_t *tester, const char *path, bson_t *out) {
    mongocrypt_binary_t *bin = TEST_FILE(path);
    ASSERT(bson_init_static(out, mongocrypt_binary_data(bin), mongocrypt_binary_len(bin)));
}

static void _test_efc(_mongocrypt_tester_t *tester) {
    bson_t efc_bson;
    mc_EncryptedFieldConfig_t efc;
    mc_EncryptedField_t *ptr;
    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_buffer_t expect_keyId1;
    _mongocrypt_buffer_t expect_keyId2;
    _mongocrypt_buffer_t expect_keyId3;

    _mongocrypt_buffer_copy_from_hex(&expect_keyId1, "12345678123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&expect_keyId2, "abcdefab123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&expect_keyId3, "12345678123498761234123456abcdef");

    {
        _load_test_file(tester, "./test/data/efc/efc-oneField.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, 0);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-oneField-goodVersionSet.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, 1);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-extraField.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, 0);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-twoFields.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, 0);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "lastName");
        ASSERT_CMPBUF(expect_keyId2, ptr->keyId);
        ASSERT(ptr->next != NULL);
        ptr = ptr->next;
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-missingKeyId.json", &efc_bson);
        ASSERT_FAILS_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status),
                            status,
                            "unable to find 'keyId' or 'keyAltName' in 'field' document");
        mc_EncryptedFieldConfig_cleanup(&efc);
        _mongocrypt_status_reset(status);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-oneField-badVersionSet.json", &efc_bson);
        ASSERT_FAILS_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status),
                            status,
                            "'strEncodeVersion' of 99 is not supported");
        mc_EncryptedFieldConfig_cleanup(&efc);
        _mongocrypt_status_reset(status);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-textSearchFields.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, LATEST_STR_ENCODE_VERSION);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "middleName");
        ASSERT_CMPBUF(expect_keyId3, ptr->keyId);
        ASSERT(ptr->supported_queries == (SUPPORTS_SUFFIX_QUERIES | SUPPORTS_PREFIX_QUERIES));
        ASSERT(ptr->next != NULL);
        ptr = ptr->next;
        ASSERT_STREQUAL(ptr->path, "lastName");
        ASSERT_CMPBUF(expect_keyId2, ptr->keyId);
        ASSERT(ptr->supported_queries == (SUPPORTS_SUFFIX_QUERIES | SUPPORTS_PREFIX_QUERIES));
        ASSERT(ptr->next != NULL);
        ptr = ptr->next;
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->supported_queries == SUPPORTS_SUBSTRING_PREVIEW_QUERIES);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-textSearchFields-goodVersionSet.json", &efc_bson);
        ASSERT_OK_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status), status);
        ASSERT_CMPUINT8(efc.str_encode_version, ==, 1);
        ptr = efc.fields;
        ASSERT(ptr);
        ASSERT_STREQUAL(ptr->path, "lastName");
        ASSERT_CMPBUF(expect_keyId2, ptr->keyId);
        ASSERT(ptr->supported_queries == (SUPPORTS_SUFFIX_QUERIES | SUPPORTS_PREFIX_QUERIES));
        ASSERT(ptr->next != NULL);
        ptr = ptr->next;
        ASSERT_STREQUAL(ptr->path, "firstName");
        ASSERT_CMPBUF(expect_keyId1, ptr->keyId);
        ASSERT(ptr->supported_queries == SUPPORTS_SUBSTRING_PREVIEW_QUERIES);
        ASSERT(ptr->next == NULL);
        mc_EncryptedFieldConfig_cleanup(&efc);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-textSearchFields-badVersionSet.json", &efc_bson);
        ASSERT_FAILS_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status),
                            status,
                            "'strEncodeVersion' of 99 is not supported");
        mc_EncryptedFieldConfig_cleanup(&efc);
        _mongocrypt_status_reset(status);
    }

    {
        _load_test_file(tester, "./test/data/efc/efc-duplicateKeyAltName.json", &efc_bson);
        ASSERT_FAILS_STATUS(mc_EncryptedFieldConfig_parse(&efc, &efc_bson, status),
                            status,
                            "duplicate keyAltName 'myKeyAltName' found in encrypted field config");
        mc_EncryptedFieldConfig_cleanup(&efc);
        _mongocrypt_status_reset(status);
    }

    _mongocrypt_buffer_cleanup(&expect_keyId3);
    _mongocrypt_buffer_cleanup(&expect_keyId2);
    _mongocrypt_buffer_cleanup(&expect_keyId1);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_efc(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_efc);
}
libmongocrypt-1.19.0/test/test-mc-fle2-encryption-placeholder.c000066400000000000000000000551451521103432300244730ustar00rootroot00000000000000/*
 * Copyright 2024-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 

#include "mongocrypt-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define RAW_STRING(...) #__VA_ARGS__

static void _test_FLE2EncryptionPlaceholder_parse(_mongocrypt_tester_t *tester) {
    mc_FLE2EncryptionPlaceholder_t placeholder;
    bson_t as_bson;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t buf;

    status = mongocrypt_status_new();
    _mongocrypt_buffer_copy_from_hex(&buf,
                                     "03610000001074000100000010610002000000056b690010000000041234567812349876"
                                     "1234123456789012056b75001000000004abcdefab123498761234123456789012027600"
                                     "0900000076616c75653132330012636d00000000000000000000");
    ASSERT(bson_init_static(&as_bson, buf.data + 1, buf.len - 1));
    mc_FLE2EncryptionPlaceholder_init(&placeholder);
    ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, &as_bson, status), status);

    ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
    ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_EQUALITY);
    ASSERT(BSON_ITER_HOLDS_UTF8(&placeholder.v_iter));
    ASSERT_STREQUAL(bson_iter_utf8(&placeholder.v_iter, NULL), "value123");

    _mongocrypt_buffer_t expect_index_key_id;
    _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
    ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
    _mongocrypt_buffer_cleanup(&expect_index_key_id);

    _mongocrypt_buffer_t expect_user_key_id;
    _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
    ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
    _mongocrypt_buffer_cleanup(&expect_user_key_id);

    ASSERT(placeholder.maxContentionFactor == 0);

    mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
    _mongocrypt_buffer_cleanup(&buf);
    mongocrypt_status_destroy(status);
}

static void _test_FLE2EncryptionPlaceholder_range_parse(_mongocrypt_tester_t *tester) {
    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT.
    {
        mc_FLE2EncryptionPlaceholder_t placeholder;
        bson_t as_bson;
        mongocrypt_status_t *status;
        _mongocrypt_buffer_t buf;

        status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&buf,
                                         "037d0000001074000100000010610003000000056b690010000000041234567812349"
                                         "8761234123456789012056b75001000000004abcdefab123498761234123456789012"
                                         "0376001e00000010760040e20100106d696e0000000000106d61780087d6120000126"
                                         "36d000000000000000000127300010000000000000000");
        ASSERT(bson_init_static(&as_bson, buf.data + 1, buf.len - 1));
        mc_FLE2EncryptionPlaceholder_init(&placeholder);
        ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, &as_bson, status), status);

        ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
        ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);

        _mongocrypt_buffer_t expect_index_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
        ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
        _mongocrypt_buffer_cleanup(&expect_index_key_id);

        _mongocrypt_buffer_t expect_user_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
        ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
        _mongocrypt_buffer_cleanup(&expect_user_key_id);

        ASSERT_CMPINT64(placeholder.sparsity, ==, 1);

        // Parse FLE2RangeInsertSpec.
        {
            mc_FLE2RangeInsertSpec_t spec;

            ASSERT_OK_STATUS(mc_FLE2RangeInsertSpec_parse(&spec, &placeholder.v_iter, status), status);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.v));
            ASSERT_CMPINT32(bson_iter_int32(&spec.v), ==, 123456);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.min));
            ASSERT_CMPINT32(bson_iter_int32(&spec.min), ==, 0);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.max));
            ASSERT_CMPINT32(bson_iter_int32(&spec.max), ==, 1234567);
        }

        mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
        _mongocrypt_buffer_cleanup(&buf);
        mongocrypt_status_destroy(status);
    }

    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND.
    {
        mc_FLE2EncryptionPlaceholder_t placeholder;
        bson_t as_bson;
        mongocrypt_status_t *status;
        _mongocrypt_buffer_t buf;

        status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&buf,
                                         "03ec0000001074000200000010610003000000056b690010000000041234567812349"
                                         "8761234123456789012056b75001000000004abcdefab123498761234123456789012"
                                         "0376008d000000036564676573496e666f005b000000106c6f776572426f756e64000"
                                         "0000000086c62496e636c756465640001107570706572426f756e640087d612000875"
                                         "62496e636c75646564000110696e6465784d696e000000000010696e6465784d61780"
                                         "087d6120000107061796c6f6164496400d20400001066697273744f70657261746f72"
                                         "00010000000012636d000000000000000000127300010000000000000000");
        ASSERT(bson_init_static(&as_bson, buf.data + 1, buf.len - 1));
        mc_FLE2EncryptionPlaceholder_init(&placeholder);
        ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, &as_bson, status), status);

        ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND);
        ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);

        _mongocrypt_buffer_t expect_index_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
        ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
        _mongocrypt_buffer_cleanup(&expect_index_key_id);

        _mongocrypt_buffer_t expect_user_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
        ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
        _mongocrypt_buffer_cleanup(&expect_user_key_id);

        ASSERT_CMPINT64(placeholder.sparsity, ==, 1);

        // Parse FLE2RangeFindSpec.
        {
            mc_FLE2RangeFindSpec_t spec;

            ASSERT_OK_STATUS(mc_FLE2RangeFindSpec_parse(&spec, &placeholder.v_iter, status), status);

            ASSERT(spec.edgesInfo.set);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.edgesInfo.value.lowerBound));
            ASSERT_CMPINT32(bson_iter_int32(&spec.edgesInfo.value.lowerBound), ==, 0);
            ASSERT(spec.edgesInfo.value.lbIncluded);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.edgesInfo.value.upperBound));
            ASSERT_CMPINT32(bson_iter_int32(&spec.edgesInfo.value.upperBound), ==, 1234567);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.edgesInfo.value.indexMin));
            ASSERT_CMPINT32(bson_iter_int32(&spec.edgesInfo.value.indexMin), ==, 0);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT(BSON_ITER_HOLDS_INT32(&spec.edgesInfo.value.indexMax));
            ASSERT_CMPINT32(bson_iter_int32(&spec.edgesInfo.value.indexMax), ==, 1234567);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT_CMPINT32(spec.payloadId, ==, 1234);

            ASSERT_CMPINT(spec.firstOperator, ==, FLE2RangeOperator_kGt);
            ASSERT_CMPINT(spec.secondOperator, ==, FLE2RangeOperator_kNone);
        }

        mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
        _mongocrypt_buffer_cleanup(&buf);
        mongocrypt_status_destroy(status);
    }

    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND with precision.
    {
        mc_FLE2EncryptionPlaceholder_t placeholder;
        bson_t as_bson;
        mongocrypt_status_t *status;
        _mongocrypt_buffer_t buf;

        status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&buf,
                                         "030b0100001074000200000010610003000000056b690010000000041234567812349"
                                         "8761234123456789012056b75001000000004abcdefab123498761234123456789012"
                                         "037600ac000000036564676573496e666f007a000000016c6f776572426f756e64000"
                                         "000000000000000086c62496e636c756465640001017570706572426f756e64000000"
                                         "000000006940087562496e636c75646564000110707265636973696f6e00020000000"
                                         "1696e6465784d696e00000000000000000001696e6465784d61780000000000000069"
                                         "4000107061796c6f6164496400d20400001066697273744f70657261746f720001000"
                                         "0000012636d000000000000000000127300010000000000000000");
        ASSERT(bson_init_static(&as_bson, buf.data + 1, buf.len - 1));
        mc_FLE2EncryptionPlaceholder_init(&placeholder);
        ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, &as_bson, status), status);

        ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND);
        ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);

        _mongocrypt_buffer_t expect_index_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
        ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
        _mongocrypt_buffer_cleanup(&expect_index_key_id);

        _mongocrypt_buffer_t expect_user_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
        ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
        _mongocrypt_buffer_cleanup(&expect_user_key_id);

        ASSERT_CMPINT64(placeholder.sparsity, ==, 1);

        // Parse FLE2RangeFindSpec.
        {
            mc_FLE2RangeFindSpec_t spec;

            ASSERT_OK_STATUS(mc_FLE2RangeFindSpec_parse(&spec, &placeholder.v_iter, status), status);

            ASSERT(spec.edgesInfo.set);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.edgesInfo.value.lowerBound));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.edgesInfo.value.lowerBound), ==, 0.0);
            ASSERT(spec.edgesInfo.value.lbIncluded);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.edgesInfo.value.upperBound));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.edgesInfo.value.upperBound), ==, 200.0);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.edgesInfo.value.indexMin));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.edgesInfo.value.indexMin), ==, 0);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.edgesInfo.value.indexMax));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.edgesInfo.value.indexMax), ==, 200.0);
            ASSERT(spec.edgesInfo.value.ubIncluded);

            ASSERT_CMPDOUBLE(spec.payloadId, ==, 1234);

            ASSERT_CMPINT(spec.firstOperator, ==, FLE2RangeOperator_kGt);
            ASSERT(spec.edgesInfo.value.precision.set);
            ASSERT_CMPUINT32(spec.edgesInfo.value.precision.value, ==, 2);
        }

        mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
        _mongocrypt_buffer_cleanup(&buf);
        mongocrypt_status_destroy(status);
    }

    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT with precision.
    {
        mc_FLE2EncryptionPlaceholder_t placeholder;
        bson_t as_bson;
        mongocrypt_status_t *status;
        _mongocrypt_buffer_t buf;

        status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&buf,
                                         "03980000001074000100000010610003000000056b690010000000041234567812349"
                                         "8761234123456789012056b75001000000004abcdefab123498761234123456789012"
                                         "0376003900000001760077be9f1a2fdd5e40016d696e000000000000000000016d617"
                                         "800000000000000694010707265636973696f6e00020000000012636d000000000000"
                                         "000000127300010000000000000000");
        ASSERT(bson_init_static(&as_bson, buf.data + 1, buf.len - 1));
        mc_FLE2EncryptionPlaceholder_init(&placeholder);
        ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, &as_bson, status), status);

        ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
        ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_RANGE);

        _mongocrypt_buffer_t expect_index_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
        ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
        _mongocrypt_buffer_cleanup(&expect_index_key_id);

        _mongocrypt_buffer_t expect_user_key_id;
        _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
        ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
        _mongocrypt_buffer_cleanup(&expect_user_key_id);

        ASSERT_CMPINT64(placeholder.sparsity, ==, 1);

        // Parse FLE2RangeInsertSpec.
        {
            mc_FLE2RangeInsertSpec_t spec;

            ASSERT_OK_STATUS(mc_FLE2RangeInsertSpec_parse(&spec, &placeholder.v_iter, status), status);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.v));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.v), ==, 123.456);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.min));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.min), ==, 0.0);

            ASSERT(BSON_ITER_HOLDS_DOUBLE(&spec.max));
            ASSERT_CMPDOUBLE(bson_iter_double(&spec.max), ==, 200.0);

            ASSERT(spec.precision.set);
            ASSERT_CMPUINT32(spec.precision.value, ==, 2);
        }

        mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
        _mongocrypt_buffer_cleanup(&buf);
        mongocrypt_status_destroy(status);
    }
}

static bool _parse_text_search_spec_from_placeholder(_mongocrypt_tester_t *tester,
                                                     const char *spec_json_in,
                                                     mc_FLE2TextSearchInsertSpec_t *spec_out,
                                                     mongocrypt_status_t *status_out) {
#define PLACEHOLDER_TEMPLATE                                                                                           \
    RAW_STRING({                                                                                                       \
        "t" : {"$numberInt" : "1"},                                                                                    \
        "a" : {"$numberInt" : "4"},                                                                                    \
        "ki" : {"$binary" : {"base64" : "EjRWeBI0mHYSNBI0VniQEg==", "subType" : "04"}},                                \
        "ku" : {"$binary" : {"base64" : "q83vqxI0mHYSNBI0VniQEg==", "subType" : "04"}},                                \
        "v" : MC_BSON,                                                                                                 \
        "cm" : {"$numberLong" : "7"}                                                                                   \
    })

    bson_t *const as_bson = TMP_BSONF(PLACEHOLDER_TEMPLATE, TMP_BSON_STR(spec_json_in));

    mc_FLE2EncryptionPlaceholder_t placeholder;
    mc_FLE2EncryptionPlaceholder_init(&placeholder);
    ASSERT_OK_STATUS(mc_FLE2EncryptionPlaceholder_parse(&placeholder, as_bson, status_out), status_out);

    ASSERT(placeholder.type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT);
    ASSERT(placeholder.algorithm == MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH);

    _mongocrypt_buffer_t expect_index_key_id;
    _mongocrypt_buffer_copy_from_hex(&expect_index_key_id, "12345678123498761234123456789012");
    ASSERT_CMPBUF(placeholder.index_key_id, expect_index_key_id);
    _mongocrypt_buffer_cleanup(&expect_index_key_id);

    _mongocrypt_buffer_t expect_user_key_id;
    _mongocrypt_buffer_copy_from_hex(&expect_user_key_id, "abcdefab123498761234123456789012");
    ASSERT_CMPBUF(placeholder.user_key_id, expect_user_key_id);
    _mongocrypt_buffer_cleanup(&expect_user_key_id);

    ASSERT_CMPINT64(placeholder.sparsity, ==, 0);
    ASSERT(placeholder.maxContentionFactor == 7);

    bool res = mc_FLE2TextSearchInsertSpec_parse(spec_out, &placeholder.v_iter, status_out);

    mc_FLE2EncryptionPlaceholder_cleanup(&placeholder);
    return res;

#undef PLACEHOLDER_TEMPLATE
}

static void _test_FLE2EncryptionPlaceholder_textSearch_parse(_mongocrypt_tester_t *tester) {
    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT with substr + suffix + prefix specs
    {
        const char *input = RAW_STRING({
            "v" : "foobar",
            "casef" : false,
            "diacf" : true,
            "substr" : {"ub" : {"$numberInt" : "200"}, "lb" : {"$numberInt" : "20"}, "mlen" : {"$numberInt" : "2000"}},
            "suffix" : {"ub" : {"$numberInt" : "300"}, "lb" : {"$numberInt" : "30"}},
            "prefix" : {"ub" : {"$numberInt" : "400"}, "lb" : {"$numberInt" : "400"}}
        });
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec;
        ASSERT_OK_STATUS(_parse_text_search_spec_from_placeholder(tester, input, &spec, status), status);
        ASSERT(BSON_ITER_HOLDS_UTF8(&spec.v_iter));
        ASSERT(bson_iter_utf8(&spec.v_iter, NULL) == spec.v);
        ASSERT(strlen("foobar") == spec.len);
        ASSERT(0 == strncmp("foobar", spec.v, spec.len));
        ASSERT(spec.diacf == true);
        ASSERT(spec.casef == false);
        ASSERT(spec.substr.set == true);
        ASSERT(spec.substr.value.lb == 20);
        ASSERT(spec.substr.value.ub == 200);
        ASSERT(spec.substr.value.mlen == 2000);
        ASSERT(spec.suffix.set == true);
        ASSERT(spec.suffix.value.lb == 30);
        ASSERT(spec.suffix.value.ub == 300);
        ASSERT(spec.prefix.set == true);
        ASSERT(spec.prefix.value.lb == 400);
        ASSERT(spec.prefix.value.ub == 400);
        mongocrypt_status_destroy(status);
    }

    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT with lb > ub
#define LB_GT_UB_TEST(Type)                                                                                            \
    do {                                                                                                               \
        const char *input = RAW_STRING({                                                                               \
            "v" : "foobar",                                                                                            \
            "casef" : false,                                                                                           \
            "diacf" : true,                                                                                            \
            Type : {"ub" : {"$numberInt" : "30"}, "lb" : {"$numberInt" : "40"}, "mlen" : {"$numberInt" : "400"}}       \
        });                                                                                                            \
        mongocrypt_status_t *status = mongocrypt_status_new();                                                         \
        mc_FLE2TextSearchInsertSpec_t spec;                                                                            \
        ASSERT_FAILS_STATUS(_parse_text_search_spec_from_placeholder(tester, input, &spec, status),                    \
                            status,                                                                                    \
                            "upper bound cannot be less than the lower bound");                                        \
        mongocrypt_status_destroy(status);                                                                             \
    } while (0)
    LB_GT_UB_TEST("substr");
    LB_GT_UB_TEST("suffix");
    LB_GT_UB_TEST("prefix");
#undef LB_GT_UB_TEST

    // Test type=MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT with mlen < ub
    {
        const char *input = RAW_STRING({
            "v" : "foobar",
            "casef" : false,
            "diacf" : true,
            "substr" :
                {"ub" : {"$numberInt" : "2000"}, "lb" : {"$numberInt" : "20"}, "mlen" : {"$numberInt" : "200"}}
        });
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec;
        ASSERT_FAILS_STATUS(_parse_text_search_spec_from_placeholder(tester, input, &spec, status),
                            status,
                            "maximum indexed length cannot be less than the upper bound");
        mongocrypt_status_destroy(status);
    }
}

static void _test_FLE2EncryptionPlaceholder_parse_errors(_mongocrypt_tester_t *tester) {
    bson_t *input_bson = TMP_BSON_STR(BSON_STR({
        "t" : {"$numberInt" : "1"},
        "a" : {"$numberInt" : "1"},
        "ki" : {"$binary" : {"base64" : "EjRWeBI0mHYSNBI0VniQEg==", "subType" : "04"}},
        "ku" : {"$binary" : {"base64" : "q83vqxI0mHYSNBI0VniQEg==", "subType" : "04"}},
        "v" : "foobar",
        "cm" : "wrong type!"
    }));

    mc_FLE2EncryptionPlaceholder_t payload;
    mc_FLE2EncryptionPlaceholder_init(&payload);
    mongocrypt_status_t *status = mongocrypt_status_new();
    ASSERT_FAILS_STATUS(mc_FLE2EncryptionPlaceholder_parse(&payload, input_bson, status),
                        status,
                        "'cm' must be an int64");
    mc_FLE2EncryptionPlaceholder_cleanup(&payload);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_fle2_encryption_placeholder(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_FLE2EncryptionPlaceholder_parse);
    INSTALL_TEST(_test_FLE2EncryptionPlaceholder_range_parse);
    INSTALL_TEST(_test_FLE2EncryptionPlaceholder_textSearch_parse);
    INSTALL_TEST(_test_FLE2EncryptionPlaceholder_parse_errors);
}
libmongocrypt-1.19.0/test/test-mc-fle2-find-equality-payload-v2.c000066400000000000000000000121351521103432300245400ustar00rootroot00000000000000/*
 * Copyright 2023-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 

#include "mc-fle2-find-equality-payload-private-v2.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define TEST_FIND_EQ_PAYLOAD_HEX_V2                                                                                    \
    "890000000564002000000000fe88309ead860127cc991c969ed9d4157aed46b40d7984104"                                        \
    "d082cdd72197de605730020000000004e66e2f903d7ffbea7af1cb2482d7389329e141a95"                                        \
    "ac2833898a33b5e4f7150e056c002000000000c3c8980ed11e63ec199104bc9a0889322c9"                                        \
    "eb80d00eee0b5148dc83f7e78adb712636d00040000000000000000"

static void _test_FLE2FindEqualityPayloadV2_roundtrip(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_edcDerivedToken;
    _mongocrypt_buffer_t expect_escDerivedToken;
    _mongocrypt_buffer_t expect_serverDerivedFromDataToken;
    bson_t in_bson;
    bson_t out_bson;
    mc_FLE2FindEqualityPayloadV2_t payload;

    _mongocrypt_buffer_copy_from_hex(&expect_edcDerivedToken,
                                     "fe88309ead860127cc991c969ed9d4157aed46b40d7984104d082cdd72197de6");

    _mongocrypt_buffer_copy_from_hex(&expect_escDerivedToken,
                                     "4e66e2f903d7ffbea7af1cb2482d7389329e141a95ac2833898a33b5e4f7150e");

    _mongocrypt_buffer_copy_from_hex(&expect_serverDerivedFromDataToken,
                                     "c3c8980ed11e63ec199104bc9a0889322c9eb80d00eee0b5148dc83f7e78adb7");

    _mongocrypt_buffer_copy_from_hex(&input, TEST_FIND_EQ_PAYLOAD_HEX_V2);

    ASSERT(bson_init_static(&in_bson, input.data, input.len));

    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_FLE2FindEqualityPayloadV2_init(&payload);

    ASSERT_OK_STATUS(mc_FLE2FindEqualityPayloadV2_parse(&payload, &in_bson, status), status);

    ASSERT_CMPBUF(expect_edcDerivedToken, payload.edcDerivedToken);
    ASSERT_CMPBUF(expect_escDerivedToken, payload.escDerivedToken);
    ASSERT_CMPBUF(expect_serverDerivedFromDataToken, payload.serverDerivedFromDataToken);

    bson_init(&out_bson);
    mc_FLE2FindEqualityPayloadV2_serialize(&payload, &out_bson);
    ASSERT_EQUAL_BSON(&in_bson, &out_bson);

    mongocrypt_status_destroy(status);
    mc_FLE2FindEqualityPayloadV2_cleanup(&payload);
    bson_destroy(&out_bson);
    bson_destroy(&in_bson);
    _mongocrypt_buffer_cleanup(&expect_serverDerivedFromDataToken);
    _mongocrypt_buffer_cleanup(&expect_escDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_edcDerivedToken);
    _mongocrypt_buffer_cleanup(&input);
}

#undef TEST_FIND_EQ_PAYLOAD_HEX_V2

static void _test_FLE2FindEqualityPayloadV2_errors(_mongocrypt_tester_t *tester) {
    // Test non-int64 'cm'
    {
        bson_t *input_bson = TMP_BSON_STR(BSON_STR({
            "d" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "s" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "l" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "cm" : "wrong type!"
        }));

        mc_FLE2FindEqualityPayloadV2_t payload;
        mc_FLE2FindEqualityPayloadV2_init(&payload);
        mongocrypt_status_t *status = mongocrypt_status_new();
        ASSERT_FAILS_STATUS(mc_FLE2FindEqualityPayloadV2_parse(&payload, input_bson, status),
                            status,
                            "Field 'cm' expected to hold an int64");
        mc_FLE2FindEqualityPayloadV2_cleanup(&payload);
        mongocrypt_status_destroy(status);
    }

    // Test negative 'cm'
    {
        bson_t *input_bson = TMP_BSON_STR(BSON_STR({
            "d" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "s" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "l" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}},
            "cm" : {"$numberLong" : "-1"}
        }));

        mc_FLE2FindEqualityPayloadV2_t payload;
        mc_FLE2FindEqualityPayloadV2_init(&payload);
        mongocrypt_status_t *status = mongocrypt_status_new();
        ASSERT_FAILS_STATUS(mc_FLE2FindEqualityPayloadV2_parse(&payload, input_bson, status),
                            status,
                            "must be non-negative");
        mc_FLE2FindEqualityPayloadV2_cleanup(&payload);
        mongocrypt_status_destroy(status);
    }
}

void _mongocrypt_tester_install_fle2_payload_find_equality_v2(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_FLE2FindEqualityPayloadV2_roundtrip);
    INSTALL_TEST(_test_FLE2FindEqualityPayloadV2_errors);
}
libmongocrypt-1.19.0/test/test-mc-fle2-find-range-payload-v2.c000066400000000000000000000114671521103432300240060ustar00rootroot00000000000000/*
 * Copyright 2023-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 

#include "mc-fle2-find-range-payload-private-v2.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define TEST_FIND_RANGE_PAYLOAD_HEX_V2                                                                                 \
    "dd000000037061796c6f61640099000000046700850000000330007d00000005640020000"                                        \
    "00000280edab4190763d1f4183d383f8830773859c3a74a13161094e6fd44e7390fed0573"                                        \
    "0020000000008773322a2b9e6c08886db6bc65b46ffdd64651e8a49400a9e55ff5bc550d4"                                        \
    "5bf056c00200000000086cc691b04514af096dfe1497bbb27151ac71f18d61ddc4c7c3a75"                                        \
    "503df6974b000012636d00040000000000000000107061796c6f616449640000000000106"                                        \
    "6697273744f70657261746f720002000000107365636f6e644f70657261746f7200040000"                                        \
    "0000"

static void _test_FLE2FindRangePayloadV2_roundtrip(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    mc_FLE2FindRangePayloadV2_t payload;
    _mongocrypt_buffer_t expect_edcDerivedToken;
    _mongocrypt_buffer_t expect_escDerivedToken;
    _mongocrypt_buffer_t expect_serverDerivedFromDataToken;
    mc_FLE2RangeOperator_t expect_firstOperator = FLE2RangeOperator_kGte;
    mc_FLE2RangeOperator_t expect_secondOperator = FLE2RangeOperator_kLte;

    bson_t in_bson;
    bson_t out_bson;

    _mongocrypt_buffer_copy_from_hex(&expect_edcDerivedToken,
                                     "280edab4190763d1f4183d383f8830773859c3a74a13161094e6fd44e7390fed");

    _mongocrypt_buffer_copy_from_hex(&expect_escDerivedToken,
                                     "8773322a2b9e6c08886db6bc65b46ffdd64651e8a49400a9e55ff5bc550d45bf");

    _mongocrypt_buffer_copy_from_hex(&expect_serverDerivedFromDataToken,
                                     "86cc691b04514af096dfe1497bbb27151ac71f18d61ddc4c7c3a75503df6974b");

    _mongocrypt_buffer_copy_from_hex(&input, TEST_FIND_RANGE_PAYLOAD_HEX_V2);

    ASSERT(bson_init_static(&in_bson, input.data, input.len));

    mc_FLE2FindRangePayloadV2_init(&payload);

    {
        mc_EdgeFindTokenSetV2_t tokenSet;
        tokenSet.edcDerivedToken = expect_edcDerivedToken;
        tokenSet.escDerivedToken = expect_escDerivedToken;
        tokenSet.serverDerivedFromDataToken = expect_serverDerivedFromDataToken;
        _mc_array_append_val(&payload.payload.value.edgeFindTokenSetArray, tokenSet);
        payload.payload.value.maxContentionFactor = (uint64_t)4;
    }

    payload.payload.set = true;
    payload.payloadId = 0x0;

    payload.firstOperator = expect_firstOperator;
    payload.secondOperator = expect_secondOperator;

    bson_init(&out_bson);
    mc_FLE2FindRangePayloadV2_serialize(&payload, &out_bson);

    ASSERT_EQUAL_BSON(&in_bson, &out_bson);

    bson_destroy(&out_bson);
    bson_destroy(&in_bson);
    mc_FLE2FindRangePayloadV2_cleanup(&payload);
    _mongocrypt_buffer_cleanup(&input);
}

#undef TEST_FIND_RANGE_PAYLOAD_HEX_V2

static void _test_FLE2FindRangePayloadV2_includes_crypto_params(_mongocrypt_tester_t *tester) {
    mc_FLE2FindRangePayloadV2_t payload;
    mc_FLE2FindRangePayloadV2_init(&payload);
    payload.sparsity = OPT_I64(1);
    payload.precision = OPT_I32(2);
    payload.trimFactor = OPT_I32(3);
    bson_value_t indexMin = {.value.v_int32 = 4, .value_type = BSON_TYPE_INT32};
    bson_value_copy(&indexMin, &payload.indexMin);
    bson_value_t indexMax = {.value.v_int32 = 5, .value_type = BSON_TYPE_INT32};
    bson_value_copy(&indexMax, &payload.indexMax);

    // Test crypto params from SERVER-91889 are included in "range" payload.
    {
        bson_t got = BSON_INITIALIZER;
        ASSERT(mc_FLE2FindRangePayloadV2_serialize(&payload, &got));
        _assert_match_bson(&got, TMP_BSON(BSON_STR({"sp" : 1, "pn" : 2, "tf" : 3, "mn" : 4, "mx" : 5})));
        bson_destroy(&got);
    }

    mc_FLE2FindRangePayloadV2_cleanup(&payload);
}

void _mongocrypt_tester_install_fle2_payload_find_range_v2(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_FLE2FindRangePayloadV2_roundtrip);
    INSTALL_TEST(_test_FLE2FindRangePayloadV2_includes_crypto_params);
}
libmongocrypt-1.19.0/test/test-mc-fle2-find-text-payload.c000066400000000000000000000375741521103432300233600ustar00rootroot00000000000000/*
 * Copyright 2025-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 

#include "mc-fle2-find-text-payload-private.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define VALIDATE_TOKEN_SET(p, e)                                                                                       \
    ASSERT_CMPBUF((p)->edcDerivedToken, (e)->edcDerivedToken);                                                         \
    ASSERT_CMPBUF((p)->escDerivedToken, (e)->escDerivedToken);                                                         \
    ASSERT_CMPBUF((p)->serverDerivedFromDataToken, (e)->serverDerivedFromDataToken)

static void _validate_parsed_payload(_mongocrypt_tester_t *tester,
                                     mc_FLE2FindTextPayload_t *expected,
                                     mc_FLE2FindTextPayload_t *parsed) {
    ASSERT(parsed->tokenSets.exact.set == expected->tokenSets.exact.set);
    ASSERT(parsed->tokenSets.substring.set == expected->tokenSets.substring.set);
    ASSERT(parsed->tokenSets.suffix.set == expected->tokenSets.suffix.set);
    ASSERT(parsed->tokenSets.prefix.set == expected->tokenSets.prefix.set);
    ASSERT(parsed->caseFold == expected->caseFold);
    ASSERT(parsed->diacriticFold == expected->diacriticFold);
    ASSERT(parsed->substringSpec.set == expected->substringSpec.set);
    ASSERT(parsed->suffixSpec.set == expected->suffixSpec.set);
    ASSERT(parsed->prefixSpec.set == expected->prefixSpec.set);

    if (parsed->tokenSets.exact.set) {
        VALIDATE_TOKEN_SET(&parsed->tokenSets.exact.value, &expected->tokenSets.exact.value);
    }
    if (parsed->tokenSets.substring.set) {
        VALIDATE_TOKEN_SET(&parsed->tokenSets.substring.value, &expected->tokenSets.substring.value);
    }
    if (parsed->tokenSets.suffix.set) {
        VALIDATE_TOKEN_SET(&parsed->tokenSets.suffix.value, &expected->tokenSets.suffix.value);
    }
    if (parsed->tokenSets.prefix.set) {
        VALIDATE_TOKEN_SET(&parsed->tokenSets.prefix.value, &expected->tokenSets.prefix.value);
    }

    if (parsed->substringSpec.set) {
        ASSERT_CMPUINT32(parsed->substringSpec.value.lb, ==, expected->substringSpec.value.lb);
        ASSERT_CMPUINT32(parsed->substringSpec.value.ub, ==, expected->substringSpec.value.ub);
        ASSERT_CMPUINT32(parsed->substringSpec.value.mlen, ==, expected->substringSpec.value.mlen);
    }
    if (parsed->suffixSpec.set) {
        ASSERT_CMPUINT32(parsed->suffixSpec.value.lb, ==, expected->suffixSpec.value.lb);
        ASSERT_CMPUINT32(parsed->suffixSpec.value.ub, ==, expected->suffixSpec.value.ub);
    }
    if (parsed->prefixSpec.set) {
        ASSERT_CMPUINT32(parsed->prefixSpec.value.lb, ==, expected->prefixSpec.value.lb);
        ASSERT_CMPUINT32(parsed->prefixSpec.value.ub, ==, expected->prefixSpec.value.ub);
    }
}

#undef VALIDATE_TOKEN_SET

static void
_do_roundtrip_test(_mongocrypt_tester_t *tester, mc_FLE2FindTextPayload_t *spec, const char *expectedFailMsg) {
    mongocrypt_status_t *status = mongocrypt_status_new();

    bson_t out_bson;
    bson_init(&out_bson);
    ASSERT(mc_FLE2FindTextPayload_serialize(spec, &out_bson));

    mc_FLE2FindTextPayload_t parsed;
    mc_FLE2FindTextPayload_init(&parsed);

    bool ret = mc_FLE2FindTextPayload_parse(&parsed, &out_bson, status);
    if (expectedFailMsg) {
        ASSERT_FAILS_STATUS(ret, status, expectedFailMsg);
    } else {
        ASSERT_OK_STATUS(ret, status);
        _validate_parsed_payload(tester, spec, &parsed);
    }

    mc_FLE2FindTextPayload_cleanup(&parsed);
    bson_destroy(&out_bson);
    mongocrypt_status_destroy(status);
}

static void _do_parse_error_test(_mongocrypt_tester_t *tester, bson_t *to_parse, const char *expectedFailMsg) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_FLE2FindTextPayload_t parsed;
    mc_FLE2FindTextPayload_init(&parsed);
    bool ret = mc_FLE2FindTextPayload_parse(&parsed, to_parse, status);
    ASSERT_FAILS_STATUS(ret, status, expectedFailMsg);
    mc_FLE2FindTextPayload_cleanup(&parsed);
    mongocrypt_status_destroy(status);
}

static const char *k_edcToken = "280edab4190763d1f4183d383f8830773859c3a74a13161094e6fd44e7390fed";
static const char *k_escToken = "c3c8980ed11e63ec199104bc9a0889322c9eb80d00eee0b5148dc83f7e78adb7";
static const char *k_svrToken = "8773322a2b9e6c08886db6bc65b46ffdd64651e8a49400a9e55ff5bc550d45bf";

// Tests mc_FLE2FindTextPayload_serialize() works correctly, and non-error cases
// for mc_FLE2FindTextPayload_parse().
static void _test_FLE2FindTextPayload_roundtrip(_mongocrypt_tester_t *tester) {
    // Test exact token set + prefix spec present
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        payload.tokenSets.exact.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.serverDerivedFromDataToken, k_svrToken);
        payload.caseFold = true;
        payload.diacriticFold = false;
        payload.maxContentionFactor = 43;
        payload.prefixSpec.set = true;
        payload.prefixSpec.value.lb = 2;
        payload.prefixSpec.value.ub = 20;
        _do_roundtrip_test(tester, &payload, NULL);
        mc_FLE2FindTextPayload_cleanup(&payload);
    }

    // Test substring token set + substring spec present
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        payload.tokenSets.substring.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.substring.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.substring.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.substring.value.serverDerivedFromDataToken, k_svrToken);
        payload.caseFold = false;
        payload.diacriticFold = true;
        payload.maxContentionFactor = 12;
        payload.substringSpec.set = true;
        payload.substringSpec.value.lb = 3;
        payload.substringSpec.value.ub = 30;
        payload.substringSpec.value.mlen = 300;
        _do_roundtrip_test(tester, &payload, NULL);
        mc_FLE2FindTextPayload_cleanup(&payload);
    }

    // Test suffix token set + suffix & prefix specs present
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        payload.tokenSets.suffix.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.serverDerivedFromDataToken, k_svrToken);
        payload.caseFold = true;
        payload.diacriticFold = true;
        payload.maxContentionFactor = 21;
        payload.suffixSpec.set = true;
        payload.suffixSpec.value.lb = 4;
        payload.suffixSpec.value.ub = 40;
        payload.prefixSpec.set = true;
        payload.prefixSpec.value.lb = 5;
        payload.prefixSpec.value.ub = 50;
        _do_roundtrip_test(tester, &payload, NULL);
        mc_FLE2FindTextPayload_cleanup(&payload);
    }

    // Test prefix token set + all specs present
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        payload.tokenSets.prefix.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.prefix.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.prefix.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.prefix.value.serverDerivedFromDataToken, k_svrToken);
        payload.caseFold = false;
        payload.diacriticFold = false;
        payload.maxContentionFactor = 55;
        payload.substringSpec.set = true;
        payload.substringSpec.value.lb = 3;
        payload.substringSpec.value.ub = 30;
        payload.substringSpec.value.mlen = 300;
        payload.suffixSpec.set = true;
        payload.suffixSpec.value.lb = 4;
        payload.suffixSpec.value.ub = 40;
        payload.prefixSpec.set = true;
        payload.prefixSpec.value.lb = 5;
        payload.prefixSpec.value.ub = 50;
        _do_roundtrip_test(tester, &payload, NULL);
        mc_FLE2FindTextPayload_cleanup(&payload);
    }
}

#define INT64_JSON "{'$numberLong': '5'}"
#define TOKEN_JSON "{'$binary': 'abcd', '$type': '0'}"
#define BADTOKEN_JSON "{'$binary': 'abcd', '$type': '2'}"
#define D_JSON "'d': " TOKEN_JSON
#define S_JSON "'s': " TOKEN_JSON
#define L_JSON "'l': " TOKEN_JSON
#define TOKENSET_JSON "{" D_JSON ", " S_JSON ", " L_JSON "}"
#define TS_JSON "'ts':{'e':" TOKENSET_JSON "}"
#define CM_JSON "'cm':" INT64_JSON
#define CF_JSON "'cf':false"
#define DF_JSON "'df':true"

// Tests error cases for mc_FLE2FindTextPayload_parse().
static void _test_FLE2FindTextPayload_parse_errors(_mongocrypt_tester_t *tester) {
    // Test multiple optional fields present under ts is disallowed when parsing
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        payload.tokenSets.exact.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.exact.value.serverDerivedFromDataToken, k_svrToken);
        payload.tokenSets.suffix.set = true;
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.edcDerivedToken, k_edcToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.escDerivedToken, k_escToken);
        _mongocrypt_buffer_copy_from_hex(&payload.tokenSets.suffix.value.serverDerivedFromDataToken, k_svrToken);
        _do_roundtrip_test(tester,
                           &payload,
                           "Error parsing TextSearchFindTokenSets: cannot have multiple optional fields present");
        mc_FLE2FindTextPayload_cleanup(&payload);
    }
    // Test empty ts object is disallowed when parsing
    {
        mc_FLE2FindTextPayload_t payload;
        mc_FLE2FindTextPayload_init(&payload);
        _do_roundtrip_test(tester,
                           &payload,
                           "Error parsing TextSearchFindTokenSets: exactly one optional field is required");
        mc_FLE2FindTextPayload_cleanup(&payload);
    }

    // Test missing required fields in top-level object
    {
        bson_t *no_ts = TMP_BSON("{%s, %s, %s}", CM_JSON, CF_JSON, DF_JSON);
        bson_t *no_cm = TMP_BSON("{%s, %s, %s}", TS_JSON, CF_JSON, DF_JSON);
        bson_t *no_cf = TMP_BSON("{%s, %s, %s}", TS_JSON, CM_JSON, DF_JSON);
        bson_t *no_df = TMP_BSON("{%s, %s, %s}", TS_JSON, CM_JSON, CF_JSON);
        _do_parse_error_test(tester, no_ts, "Missing required field 'ts'");
        _do_parse_error_test(tester, no_cm, "Missing required field 'cm'");
        _do_parse_error_test(tester, no_cf, "Missing required field 'cf'");
        _do_parse_error_test(tester, no_df, "Missing required field 'df'");
    }

    const char *all_required = TS_JSON ", " CM_JSON ", " CF_JSON ", " DF_JSON;
    const char *required_no_ts = CM_JSON ", " CF_JSON ", " DF_JSON;

    // Test missing required fields in token set
    {
        bson_t *no_e_d = TMP_BSON("{'ts': {'e': {%s, %s}}, %s}", S_JSON, L_JSON, required_no_ts);
        bson_t *no_e_s = TMP_BSON("{'ts': {'e': {%s, %s}}, %s}", D_JSON, L_JSON, required_no_ts);
        bson_t *no_e_l = TMP_BSON("{'ts': {'e': {%s, %s}}, %s}", S_JSON, D_JSON, required_no_ts);
        _do_parse_error_test(tester, no_e_d, "Missing required field 'd'");
        _do_parse_error_test(tester, no_e_s, "Missing required field 's'");
        _do_parse_error_test(tester, no_e_l, "Missing required field 'l'");
    }

    // Test invalid types in top-level object
    {
        bson_t *bad_ts = TMP_BSON("{'ts': 23, %s}", required_no_ts);
        bson_t *bad_cm = TMP_BSON("{'cm': 'foo', %s, %s, %s}", TS_JSON, CF_JSON, DF_JSON);
        bson_t *bad_cf = TMP_BSON("{'cf': 'foo', %s, %s, %s}", TS_JSON, CM_JSON, DF_JSON);
        bson_t *bad_df = TMP_BSON("{'df': 'foo', %s, %s, %s}", TS_JSON, CF_JSON, CM_JSON);
        bson_t *bad_ss = TMP_BSON("{'ss': 'foo', %s}", required_no_ts);
        bson_t *bad_fs = TMP_BSON("{'fs': 'foo', %s}", required_no_ts);
        bson_t *bad_ps = TMP_BSON("{'ps': 'foo', %s}", required_no_ts);
        _do_parse_error_test(tester, bad_ts, "expected to be a document");
        _do_parse_error_test(tester, bad_cm, "expected to be int64");
        _do_parse_error_test(tester, bad_cf, "expected to be boolean");
        _do_parse_error_test(tester, bad_df, "expected to be boolean");
        _do_parse_error_test(tester, bad_ss, "must be an iterator to a document");
        _do_parse_error_test(tester, bad_fs, "must be an iterator to a document");
        _do_parse_error_test(tester, bad_ps, "must be an iterator to a document");
    }

    // Test invalid types in ts object
    {
        bson_t *bad_e = TMP_BSON("{'ts': {'e': 23}, %s}", required_no_ts);
        bson_t *bad_s = TMP_BSON("{'ts': {'s': 23}, %s}", required_no_ts);
        bson_t *bad_u = TMP_BSON("{'ts': {'u': 23}, %s}", required_no_ts);
        bson_t *bad_p = TMP_BSON("{'ts': {'p': 23}, %s}", required_no_ts);
        _do_parse_error_test(tester, bad_e, "expected to be a document");
        _do_parse_error_test(tester, bad_s, "expected to be a document");
        _do_parse_error_test(tester, bad_u, "expected to be a document");
        _do_parse_error_test(tester, bad_p, "expected to be a document");
    }

    // Test invalid types in token set
    {
        bson_t *bad_d = TMP_BSON("{'ts': {'e': {'d': 23, %s, %s}}, %s}", S_JSON, L_JSON, required_no_ts);
        bson_t *bad_s = TMP_BSON("{'ts': {'e': {'s': 23, %s, %s}}, %s}", D_JSON, L_JSON, required_no_ts);
        bson_t *bad_l = TMP_BSON("{'ts': {'e': {'l': 23, %s, %s}}, %s}", S_JSON, D_JSON, required_no_ts);
        bson_t *bad_subtype =
            TMP_BSON("{'ts': {'e': {'d': " BADTOKEN_JSON ", %s, %s}}, %s}", S_JSON, L_JSON, required_no_ts);
        _do_parse_error_test(tester, bad_d, "expected to be bindata, got: INT32");
        _do_parse_error_test(tester, bad_s, "expected to be bindata, got: INT32");
        _do_parse_error_test(tester, bad_l, "expected to be bindata, got: INT32");
        _do_parse_error_test(tester, bad_subtype, "expected to be bindata subtype 0");
    }

    // Test unrecognized fields
    {
        bson_t *level1 = TMP_BSON("{'kk': 2, %s}", all_required);
        bson_t *level2 = TMP_BSON("{'ts': {'oo': 2}, %s}", required_no_ts);
        bson_t *level3 = TMP_BSON("{'ts': {'e': {%s, %s, %s, 'pp': 1}}, %s}", D_JSON, S_JSON, L_JSON, required_no_ts);
        _do_parse_error_test(tester, level1, "Unrecognized field 'kk'");
        _do_parse_error_test(tester, level2, "Unrecognized field 'oo'");
        _do_parse_error_test(tester, level3, "Unrecognized field 'pp'");
    }

    // Test invalid contention:
    {
        bson_t *bad_cm = TMP_BSON("{'cm': {'$numberLong': '-1' }, %s}", TS_JSON ", " CF_JSON ", " DF_JSON);
        _do_parse_error_test(tester, bad_cm, "must be non-negative");
    }
}

void _mongocrypt_tester_install_fle2_payload_find_text(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_FLE2FindTextPayload_roundtrip);
    INSTALL_TEST(_test_FLE2FindTextPayload_parse_errors);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-iev-v2.c000066400000000000000000000705241521103432300225560ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-fle-blob-subtype-private.h"
#include "mc-fle2-payload-iev-private-v2.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

typedef enum { kTypeEquality, kTypeRange, kTypeText } _mc_fle2_iev_type;

typedef struct {
    _mc_fle2_iev_type type;
    _mongocrypt_buffer_t payload;
    _mongocrypt_buffer_t S_KeyId;
    _mongocrypt_buffer_t S_Key;
    _mongocrypt_buffer_t K_KeyId;
    _mongocrypt_buffer_t K_Key;
    uint8_t bson_value_type;
    _mongocrypt_buffer_t bson_value;
    uint32_t edge_count;
    uint32_t substr_tag_count;
    uint32_t suffix_tag_count;
    mc_FLE2TagAndEncryptedMetadataBlock_t *metadata;
} _mc_fle2_iev_v2_test;

#define kMetadataLen 96U
#define kMetadataFieldLen 32U
#define kMinServerEncryptedValueLen 17U // IV(16) + EncryptCTR(1byte)

static void _mc_fle2_iev_v2_test_destroy(_mc_fle2_iev_v2_test *test) {
    _mongocrypt_buffer_cleanup(&test->payload);
    _mongocrypt_buffer_cleanup(&test->S_KeyId);
    _mongocrypt_buffer_cleanup(&test->S_Key);
    _mongocrypt_buffer_cleanup(&test->K_KeyId);
    _mongocrypt_buffer_cleanup(&test->K_Key);
    _mongocrypt_buffer_cleanup(&test->bson_value);
    for (uint32_t i = 0; i < test->edge_count; ++i) {
        mc_FLE2TagAndEncryptedMetadataBlock_cleanup(&test->metadata[i]);
    }

    bson_free(test->metadata);
}

static _mongocrypt_buffer_t copy_buf(_mongocrypt_buffer_t *src) {
    _mongocrypt_buffer_t dst;
    _mongocrypt_buffer_init(&dst);
    _mongocrypt_buffer_copy_to(src, &dst);
    return dst;
}

// Fill out the fields manually with no checks, then serialize.
static void
_mc_fle2_iev_v2_test_serialize_payload(mongocrypt_t *crypt, _mc_fle2_iev_v2_test *test, _mongocrypt_buffer_t *payload) {
    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();
    iev->type = test->type == kTypeEquality ? kFLE2IEVTypeEqualityV2
              : test->type == kTypeRange    ? kFLE2IEVTypeRangeV2
                                            : kFLE2IEVTypeText;
    iev->fle_blob_subtype = test->type == kTypeEquality ? MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2
                          : test->type == kTypeRange    ? MC_SUBTYPE_FLE2IndexedRangeEncryptedValueV2
                                                        : MC_SUBTYPE_FLE2IndexedTextEncryptedValue;
    iev->bson_value_type = test->bson_value_type;
    iev->edge_count = test->edge_count;
    iev->substr_tag_count = test->substr_tag_count;
    iev->suffix_tag_count = test->suffix_tag_count;
    iev->S_KeyId = copy_buf(&test->S_KeyId);
    _mongocrypt_buffer_init(&iev->ServerEncryptedValue);

    // Encrypt the client value (bson_value) into the SEV
    // First, encrypt with K_Key -> CEV
    mongocrypt_status_t *status = mongocrypt_status_new();
    const _mongocrypt_value_encryption_algorithm_t *fle2v2aead = _mcFLE2v2AEADAlgorithm();
    const uint32_t ClientEncryptedValueLen = fle2v2aead->get_ciphertext_len(test->bson_value.len, status);
    ASSERT_OK_STATUS(ClientEncryptedValueLen, status);

    _mongocrypt_buffer_t clientEncryptedValue;
    _mongocrypt_buffer_init_size(&clientEncryptedValue, ClientEncryptedValueLen);

    _mongocrypt_buffer_t iv;
    _mongocrypt_buffer_init_size(&iv, MONGOCRYPT_IV_LEN);
    ASSERT_OK_STATUS(_mongocrypt_random(crypt->crypto, &iv, MONGOCRYPT_IV_LEN, status), status);

    uint32_t bytes_written = 0;
    ASSERT_OK_STATUS(fle2v2aead->do_encrypt(crypt->crypto,
                                            &iv,
                                            &test->K_KeyId,
                                            &test->K_Key,
                                            &test->bson_value,
                                            &clientEncryptedValue,
                                            &bytes_written,
                                            status),
                     status);
    ASSERT(bytes_written == ClientEncryptedValueLen);

    // Then, add K_Key_Id + CEV -> DSEV
    _mongocrypt_buffer_t decryptedServerEncryptedValue;
    _mongocrypt_buffer_init_size(&decryptedServerEncryptedValue, clientEncryptedValue.len + UUID_LEN);
    memcpy(decryptedServerEncryptedValue.data, test->K_KeyId.data, UUID_LEN);
    memcpy(decryptedServerEncryptedValue.data + UUID_LEN, clientEncryptedValue.data, clientEncryptedValue.len);
    // Finally, encrypt DSEV -> SEV
    /* Get the TokenKey from the last 32 bytes of S_Key */
    _mongocrypt_buffer_t TokenKey;
    ASSERT(_mongocrypt_buffer_from_subrange(&TokenKey,
                                            &test->S_Key,
                                            test->S_Key.len - MONGOCRYPT_TOKEN_KEY_LEN,
                                            MONGOCRYPT_TOKEN_KEY_LEN));

    /* Use TokenKey to create ServerDataEncryptionLevel1Token and encrypt
     * DSEV into ServerEncryptedValue */
    mc_ServerDataEncryptionLevel1Token_t *token =
        mc_ServerDataEncryptionLevel1Token_new(crypt->crypto, &TokenKey, status);
    ASSERT_OK_STATUS(token, status);

    const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
    const uint32_t ServerEncryptedValueLen = fle2alg->get_ciphertext_len(decryptedServerEncryptedValue.len, status);
    ASSERT_OK_STATUS(ServerEncryptedValueLen, status);

    _mongocrypt_buffer_resize(&iev->ServerEncryptedValue, ServerEncryptedValueLen);
    bytes_written = 0;
    ASSERT_OK_STATUS(fle2alg->do_encrypt(crypt->crypto,
                                         &iv,
                                         NULL /* aad */,
                                         mc_ServerDataEncryptionLevel1Token_get(token),
                                         &decryptedServerEncryptedValue,
                                         &iev->ServerEncryptedValue,
                                         &bytes_written,
                                         status),
                     status);
    ASSERT(bytes_written == ServerEncryptedValueLen);

    // Finally, add metadata, and we have a complete IEV.
    iev->metadata = bson_malloc0(test->edge_count * sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t));
    for (uint32_t i = 0; i < test->edge_count; i++) {
        _mongocrypt_buffer_t tmp_buf;
        _mongocrypt_buffer_init_size(&tmp_buf, kMetadataLen);
        memcpy(tmp_buf.data, test->metadata[i].encryptedCount.data, kMetadataFieldLen);
        memcpy(tmp_buf.data + kMetadataFieldLen, test->metadata[i].tag.data, kMetadataFieldLen);
        memcpy(tmp_buf.data + kMetadataFieldLen * 2, test->metadata[i].encryptedZeros.data, kMetadataFieldLen);

        ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_parse(&iev->metadata[i], &tmp_buf, status), status);
        _mongocrypt_buffer_cleanup(&tmp_buf);
    }

    // Now serialize our IEV to the payload.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_serialize(iev, payload, status), status);
    mongocrypt_status_destroy(status);
    _mongocrypt_buffer_cleanup(&clientEncryptedValue);
    _mongocrypt_buffer_cleanup(&iv);
    _mongocrypt_buffer_cleanup(&decryptedServerEncryptedValue);
    mc_ServerDataEncryptionLevel1Token_destroy(token);
    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
}

static bool _mc_fle2_iev_v2_test_parse(_mc_fle2_iev_v2_test *test, bson_iter_t *iter) {
    bool hasType = false;

    while (bson_iter_next(iter)) {
        const char *field = bson_iter_key(iter);
        ASSERT(field);

#define HEXBUF_FIELD(Name)                                                                                             \
    if (!strcmp(field, #Name)) {                                                                                       \
        ASSERT_OR_PRINT_MSG(!test->Name.data, "Duplicate field '" #Name "' in test");                                  \
        ASSERT(BSON_ITER_HOLDS_UTF8(iter));                                                                            \
        const char *value = bson_iter_utf8(iter, NULL);                                                                \
        _mongocrypt_buffer_copy_from_hex(&test->Name, value);                                                          \
        ASSERT(strlen(value) == (test->Name.len * 2));                                                                 \
    } else
        HEXBUF_FIELD(payload)
        HEXBUF_FIELD(S_KeyId)
        HEXBUF_FIELD(S_Key)
        HEXBUF_FIELD(K_KeyId)
        HEXBUF_FIELD(K_Key)
        HEXBUF_FIELD(bson_value)
#undef HEXBUF_FIELD
        /* else */ if (!strcmp(field, "bson_value_type")) {
            ASSERT_OR_PRINT_MSG(!test->bson_value_type, "Duplicate field 'bson_value_type'");
            ASSERT(BSON_ITER_HOLDS_INT32(iter) || BSON_ITER_HOLDS_INT64(iter));
            int64_t value = bson_iter_as_int64(iter);
            ASSERT_OR_PRINT_MSG((value > 0) && (value < 128), "Field 'bson_value_type' must be 1..127");
            test->bson_value_type = (uint8_t)value;
        } else if (!strcmp(field, "substr_tag_count")) {
            ASSERT_OR_PRINT_MSG(!test->substr_tag_count, "Duplicate field 'substr_tag_count'");
            ASSERT(BSON_ITER_HOLDS_INT32(iter) || BSON_ITER_HOLDS_INT64(iter));
            int64_t value = bson_iter_as_int64(iter);
            ASSERT_OR_PRINT_MSG((value >= 0) && (value < UINT32_MAX), "Field 'substr_tag_count' must be 0..2^32-1");
            test->substr_tag_count = (uint32_t)value;
        } else if (!strcmp(field, "suffix_tag_count")) {
            ASSERT_OR_PRINT_MSG(!test->suffix_tag_count, "Duplicate field 'suffix_tag_count'");
            ASSERT(BSON_ITER_HOLDS_INT32(iter) || BSON_ITER_HOLDS_INT64(iter));
            int64_t value = bson_iter_as_int64(iter);
            ASSERT_OR_PRINT_MSG((value >= 0) && (value < UINT32_MAX), "Field 'suffix_tag_count' must be 0..2^32-1");
            test->suffix_tag_count = (uint32_t)value;
        } else if (!strcmp(field, "type")) {
            ASSERT_OR_PRINT_MSG(!hasType, "Duplicate field 'type'");
            ASSERT(BSON_ITER_HOLDS_UTF8(iter));
            const char *value = bson_iter_utf8(iter, NULL);
            if (!strcmp(value, "equality")) {
                test->type = kTypeEquality;
            } else if (!strcmp(value, "range")) {
                test->type = kTypeRange;
            } else if (!strcmp(value, "text")) {
                test->type = kTypeText;
            } else {
                TEST_ERROR("Unknown type '%s'", value);
            }
            hasType = true;
        } else if (!strcmp(field, "metadata")) {
            ASSERT_OR_PRINT_MSG(!test->metadata, "Duplicate field 'metadata'");

            // Use bson functions to loop through array
            ASSERT(BSON_ITER_HOLDS_ARRAY(iter));
            const uint8_t *metadata_array_data = NULL;
            uint32_t metadata_array_len = 0;
            bson_iter_array(iter, &metadata_array_len, &metadata_array_data);

            bson_t metadata_array;
            bson_iter_t metadata_array_iter;
            if (!bson_init_static(&metadata_array, metadata_array_data, metadata_array_len)
                || !bson_iter_init(&metadata_array_iter, &metadata_array)) {
                TEST_ERROR("Failed to initialize array iterator");
                return false;
            }

            // Count metadata blocks
            size_t metadata_count = 0;
            while (bson_iter_next(&metadata_array_iter)) {
                ASSERT(BSON_ITER_HOLDS_UTF8(&metadata_array_iter));
                metadata_count++;
            }

            // Allocate memory for the metadata array
            test->metadata = (mc_FLE2TagAndEncryptedMetadataBlock_t *)bson_malloc0(
                metadata_count * sizeof(mc_FLE2TagAndEncryptedMetadataBlock_t));
            if (!test->metadata) {
                TEST_ERROR("Failed to allocate memory for metadata array");
                return false;
            }

            // Reinitialize iter and parse each metadata block
            bson_iter_init(&metadata_array_iter, &metadata_array);
            uint32_t i = 0;
            while (bson_iter_next(&metadata_array_iter)) {
                ASSERT(BSON_ITER_HOLDS_UTF8(&metadata_array_iter));

                mongocrypt_status_t *tmp_status = mongocrypt_status_new();
                const char *value = bson_iter_utf8(&metadata_array_iter, NULL);

                _mongocrypt_buffer_t tmp_buf;
                _mongocrypt_buffer_copy_from_hex(&tmp_buf, value);

                ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_parse(&test->metadata[i], &tmp_buf, tmp_status),
                                 tmp_status);

                _mongocrypt_buffer_cleanup(&tmp_buf);
                mongocrypt_status_destroy(tmp_status);
                i++;
            }

            test->edge_count = i;

        } else {
            TEST_ERROR("Unknown field '%s'", field);
        }
    }

#define CHECK_HAS(Name) ASSERT_OR_PRINT_MSG(test->Name.data, "Missing field '" #Name "'")
    CHECK_HAS(payload);
    CHECK_HAS(S_KeyId);
    CHECK_HAS(S_Key);
    CHECK_HAS(K_KeyId);
    CHECK_HAS(K_Key);
    CHECK_HAS(bson_value);
#undef CHECK_HAS
    ASSERT_OR_PRINT_MSG(hasType, "Missing field 'type'");
    ASSERT_OR_PRINT_MSG(test->bson_value_type, "Missing field 'bson_value_type'");

    return true;
}

static void _mc_fle2_iev_v2_test_run(mongocrypt_t *crypt, _mongocrypt_tester_t *tester, _mc_fle2_iev_v2_test *test) {
    mongocrypt_status_t *status = mongocrypt_status_new();

    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();

    if (!test->payload.data) {
        // This is a self-test; i.e. we serialize the payload from the given fields, parse, and assert sameness.
        _mc_fle2_iev_v2_test_serialize_payload(crypt, test, &test->payload);
    }

    // Parse payload.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_parse(iev, &test->payload, status), status);

    // Reserialize and assert that the result is the same as the initial input
    _mongocrypt_buffer_t serialized_buf;
    _mongocrypt_buffer_init_size(&serialized_buf, test->payload.len);
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_serialize(iev, &serialized_buf, status), status);
    ASSERT_CMPBUF(serialized_buf, test->payload);
    _mongocrypt_buffer_cleanup(&serialized_buf);

    // Validate S_KeyId as parsed.
    const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValueV2_get_S_KeyId(iev, status);
    ASSERT_OK_STATUS(S_KeyId, status);
    ASSERT_CMPBUF(*S_KeyId, test->S_KeyId);

    // Validate bson_value_type as parsed.
    bson_type_t bson_value_type = mc_FLE2IndexedEncryptedValueV2_get_bson_value_type(iev, status);
    ASSERT_OK_STATUS(bson_value_type, status);
    ASSERT_CMPINT(bson_value_type, ==, test->bson_value_type);

    // Decrypt ServerEncryptedValue.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_add_S_Key(crypt->crypto, iev, &test->S_Key, status), status);

    // Validate K_KeyId as decrypted.
    const _mongocrypt_buffer_t *K_KeyId = mc_FLE2IndexedEncryptedValueV2_get_K_KeyId(iev, status);
    ASSERT_OK_STATUS(K_KeyId, status);
    ASSERT_CMPBUF(*K_KeyId, test->K_KeyId);

    // Decrypt ClientEncryptedValue.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_add_K_Key(crypt->crypto, iev, &test->K_Key, status), status);

    // Validate decrypted value.
    const _mongocrypt_buffer_t *bson_value = mc_FLE2IndexedEncryptedValueV2_get_ClientValue(iev, status);
    ASSERT_OK_STATUS(bson_value, status);
    ASSERT_CMPBUF(*bson_value, test->bson_value);

    uint32_t edge_count = 1;
    if (test->type == kTypeRange || test->type == kTypeText) {
        // Validate edge count
        edge_count = mc_FLE2IndexedEncryptedValueV2_get_edge_count(iev, status);
        ASSERT_OK_STATUS(edge_count, status);
        ASSERT_CMPUINT32(edge_count, ==, test->edge_count);
    }

    uint32_t substr_tag_count = 0, suffix_tag_count = 0, prefix_tag_count = 0;
    if (test->type == kTypeText) {
        // Validate substr/suffix/prefix tag counts
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_get_substr_tag_count(iev, &substr_tag_count, status), status);
        ASSERT_CMPUINT32(substr_tag_count, ==, test->substr_tag_count);
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_get_suffix_tag_count(iev, &suffix_tag_count, status), status);
        ASSERT_CMPUINT32(suffix_tag_count, ==, test->suffix_tag_count);
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_get_prefix_tag_count(iev, &prefix_tag_count, status), status);
        ASSERT_CMPUINT32(prefix_tag_count, ==, test->edge_count - test->substr_tag_count - test->suffix_tag_count - 1);
    }

    // Validate edges/metadata
    mc_FLE2TagAndEncryptedMetadataBlock_t metadata;
    for (uint32_t i = 0; i < edge_count; ++i) {
        if (test->type == kTypeText) {
            if (i == 0) {
                ASSERT(mc_FLE2IndexedEncryptedValueV2_get_exact_metadata(iev, &metadata, status));
            } else if (i < substr_tag_count + 1) {
                ASSERT(mc_FLE2IndexedEncryptedValueV2_get_substr_metadata(iev, &metadata, i - 1, status));
            } else if (i < suffix_tag_count + substr_tag_count + 1) {
                ASSERT(mc_FLE2IndexedEncryptedValueV2_get_suffix_metadata(iev,
                                                                          &metadata,
                                                                          i - substr_tag_count - 1,
                                                                          status));
            } else {
                ASSERT(mc_FLE2IndexedEncryptedValueV2_get_prefix_metadata(iev,
                                                                          &metadata,
                                                                          i - substr_tag_count - suffix_tag_count - 1,
                                                                          status));
            }
        } else if (test->type == kTypeRange) {
            ASSERT(mc_FLE2IndexedEncryptedValueV2_get_edge(iev, &metadata, i, status));
        } else {
            ASSERT(mc_FLE2IndexedEncryptedValueV2_get_metadata(iev, &metadata, status));
        }
        ASSERT_CMPBUF(metadata.encryptedCount, test->metadata[i].encryptedCount);
        ASSERT_CMPBUF(metadata.tag, test->metadata[i].tag);
        ASSERT_CMPBUF(metadata.encryptedZeros, test->metadata[i].encryptedZeros);
    }

    // All done!
    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    mongocrypt_status_destroy(status);
}

// Synthesize documents using ctx-decrypt workflow.
static void _mc_fle2_iev_v2_test_explicit_ctx(_mongocrypt_tester_t *tester, _mc_fle2_iev_v2_test *test) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    {
        // {v: BinData(ENCRYPTED, payload)}
        bson_t doc;
        bson_init(&doc);
        ASSERT(bson_append_binary(&doc,
                                  "v",
                                  (int)strlen("v"),
                                  BSON_SUBTYPE_ENCRYPTED,
                                  test->payload.data,
                                  test->payload.len));
        mongocrypt_binary_t *bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&doc), doc.len);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, bin), ctx);
        mongocrypt_binary_destroy(bin);
        bson_destroy(&doc);
    }

    // First we need an S_Key.
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);

    _test_ctx_wrap_and_feed_key(ctx, &test->S_KeyId, &test->S_Key, status);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    // Next we need an K_Key.
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);

    _test_ctx_wrap_and_feed_key(ctx, &test->K_KeyId, &test->K_Key, status);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    // Decryption ready.
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));

        bson_t expect_bson;
        bson_init(&expect_bson);
        bson_value_t expect_value;
        ASSERT(_mongocrypt_buffer_to_bson_value(&test->bson_value, test->bson_value_type, &expect_value));
        ASSERT(bson_append_value(&expect_bson, "v", (int)strlen("v"), &expect_value));
        ASSERT(bson_compare(&out_bson, &expect_bson) == 0);
        bson_value_destroy(&expect_value);
        mongocrypt_binary_destroy(out);
        bson_destroy(&expect_bson);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _mc_fle2_iev_v2_validate(_mongocrypt_tester_t *tester, _mc_fle2_iev_v2_test *test) {
    mongocrypt_status_t *status = mongocrypt_status_new();

    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();

    // Parse payload.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_parse(iev, &test->payload, status), status);

    // Assert valid IEV.
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_validate(iev, status), status);

    // Modify each value on the IEV to be invalid and assert failure, then change back to valid value.
    mc_fle_blob_subtype_t temp_fle_blob_subtype = iev->fle_blob_subtype;
    iev->fle_blob_subtype = MC_SUBTYPE_FLE2UnindexedEncryptedValueV2;
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    iev->fle_blob_subtype = temp_fle_blob_subtype;

    uint8_t temp_bson_value_type = iev->bson_value_type;
    iev->bson_value_type = BSON_TYPE_ARRAY;
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    iev->bson_value_type = temp_bson_value_type;

    uint32_t temp_edge_count = iev->edge_count;
    iev->edge_count = 0;
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    iev->edge_count = temp_edge_count;
    if (test->type == kTypeEquality) {
        iev->edge_count = 5;
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        iev->edge_count = temp_edge_count;
    } else if (test->type == kTypeText) {
        uint32_t temp_substr_count = iev->substr_tag_count;
        uint32_t temp_suffix_count = iev->suffix_tag_count;

        iev->substr_tag_count = iev->edge_count;
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        iev->substr_tag_count = temp_substr_count;

        iev->suffix_tag_count = iev->edge_count;
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        iev->suffix_tag_count = temp_suffix_count;
    }

    _mongocrypt_buffer_resize(&iev->ServerEncryptedValue, kMinServerEncryptedValueLen - 1);
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    _mongocrypt_buffer_resize(&iev->ServerEncryptedValue, kMinServerEncryptedValueLen);

    _mongocrypt_buffer_resize(&iev->S_KeyId, UUID_LEN - 1);
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    _mongocrypt_buffer_resize(&iev->S_KeyId, UUID_LEN);

    bool temp_CVD = iev->ClientValueDecoded;
    bool temp_CEVD = iev->ClientEncryptedValueDecoded;
    iev->ClientValueDecoded = true;
    iev->ClientEncryptedValueDecoded = false;
    ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
    iev->ClientValueDecoded = false;
    _mongocrypt_status_reset(status);
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_validate(iev, status), status);
    iev->ClientValueDecoded = temp_CVD;
    iev->ClientEncryptedValueDecoded = temp_CEVD;

    if (iev->ClientEncryptedValueDecoded) {
        uint32_t temp_DSEV_len = iev->DecryptedServerEncryptedValue.len;
        _mongocrypt_buffer_resize(&iev->DecryptedServerEncryptedValue, temp_DSEV_len - 1);
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        _mongocrypt_buffer_resize(&iev->DecryptedServerEncryptedValue, temp_DSEV_len);

        uint32_t temp_CEV_len = iev->ClientEncryptedValue.len;
        _mongocrypt_buffer_resize(&iev->ClientEncryptedValue, temp_CEV_len - 1);
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        _mongocrypt_buffer_resize(&iev->ClientEncryptedValue, temp_CEV_len);

        _mongocrypt_buffer_resize(&iev->K_KeyId, UUID_LEN - 1);
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        _mongocrypt_buffer_resize(&iev->K_KeyId, UUID_LEN);
    }

    if (iev->ClientValueDecoded) {
        uint32_t temp_CV_len = iev->ClientValue.len;
        _mongocrypt_buffer_resize(&iev->ClientValue, temp_CV_len - 1);
        ASSERT(!mc_FLE2IndexedEncryptedValueV2_validate(iev, status));
        _mongocrypt_buffer_resize(&iev->ClientValue, temp_CV_len);
    }

    // IEV should still be valid.
    _mongocrypt_status_reset(status);
    ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValueV2_validate(iev, status), status);

    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    mongocrypt_status_destroy(status);
}

static void test_fle2_iev_v2_test(mongocrypt_t *crypt, _mongocrypt_tester_t *tester, const char *path) {
    TEST_PRINTF("Loading test from %s...\n", path);

    mongocrypt_binary_t *test_bin = TEST_FILE(path);
    if (!test_bin) {
        TEST_ERROR("Failed loading test data file '%s'\n", path);
    }
    if (test_bin->len == 5) {
        TEST_ERROR("Invalid JSON in file '%s'\n", path);
    }

    bson_t test_bson;
    ASSERT(bson_init_static(&test_bson, test_bin->data, test_bin->len));
    ASSERT(bson_validate(&test_bson, BSON_VALIDATE_NONE, NULL));

    _mc_fle2_iev_v2_test test = {.payload = {0}};
    bson_iter_t iter;
    ASSERT(bson_iter_init(&iter, &test_bson));
    ASSERT(_mc_fle2_iev_v2_test_parse(&test, &iter));
    // Run once with given payload, then run with empty payload (self-test).
    _mc_fle2_iev_v2_test_run(crypt, tester, &test);
    _mongocrypt_buffer_cleanup(&test.payload);
    test.payload.data = NULL;
    _mc_fle2_iev_v2_test_run(crypt, tester, &test);

    _mc_fle2_iev_v2_test_explicit_ctx(tester, &test);
    _mc_fle2_iev_v2_validate(tester, &test);
    _mc_fle2_iev_v2_test_destroy(&test);
}

static void test_fle2_iev_v2(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    // Produced by Server test: (FLECrudTest, insertOneV2)
    test_fle2_iev_v2_test(crypt, tester, "test/data/iev-v2/FLECrudTest-insertOneV2.json");
    // Produced by Server test: (FLECrudTest, insertOneRangeV2)
    test_fle2_iev_v2_test(crypt, tester, "test/data/iev-v2/FLECrudTest-insertOneRangeV2.json");
    // Fields are modified from insertOneRangeV2.json, payload was produced by _mc_fle2_iev_v2_test_serialize_payload in
    // this test
    test_fle2_iev_v2_test(crypt, tester, "test/data/iev-v2/FLECrudTest-insertOneText.json");
    // Fields are modified from insertOneText.json, payload was produced by _mc_fle2_iev_v2_test_serialize_payload in
    // this test
    test_fle2_iev_v2_test(crypt, tester, "test/data/iev-v2/FLECrudTest-insertOneTextLarge.json");
    mongocrypt_destroy(crypt);
}

static void test_fle2_iev_v2_parse_invalid_input(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_FLE2IndexedEncryptedValueV2_t *iev = mc_FLE2IndexedEncryptedValueV2_new();

    const uint32_t minValidEqualityLength = 1 + UUID_LEN + 1 + kMinServerEncryptedValueLen + kMetadataLen;
    _mongocrypt_buffer_t input;

    uint8_t *data = (uint8_t *)bson_malloc0(minValidEqualityLength);

    data[0] = MC_SUBTYPE_FLE2IndexedEqualityEncryptedValueV2;

    _mongocrypt_buffer_from_data(&input, data, minValidEqualityLength - 1);
    ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValueV2_parse(iev, &input, status),
                        status,
                        "smaller than minimum length");

    mc_FLE2IndexedEncryptedValueV2_destroy(iev);
    bson_free(data);
    _mongocrypt_buffer_cleanup(&input);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_fle2_iev_v2_payloads(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_fle2_iev_v2);
    INSTALL_TEST(test_fle2_iev_v2_parse_invalid_input);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-iev.c000066400000000000000000000726001521103432300222260ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-fle2-payload-iev-private.h"
#include "test-mongocrypt.h"

static void test_FLE2IndexedEqualityEncryptedValue_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_S_KeyId;
    mc_FLE2IndexedEncryptedValue_t *iev;

    /* Test successful parse. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input,
                                         "07123456781234987612341234567890120243bba14ddf42da823c33569f4689f465a"
                                         "606d2cea84e6b7468167d90ee12e269b9bc8774d41b16eed047cad03ca43276222581"
                                         "0a66a1dce187d8ce044fb3d2a9e9100f8824502a3825e12db71e328f4e4ebb80fac99"
                                         "52661f54a98496381ed7a342c4a9bb22bf60be642ca7cc75c2a181ce99dd03a824a85"
                                         "c6cc5fbd0fdc22a3b0316f5d1934d6b1f2a07be8d890250814c7e6b3e5f20bff1ebd0"
                                         "8638c0faa47a784995f8dfe4c2947b43b4c97b4970539930da449edff2a23ca459653"
                                         "6e7f339da76fc9c7c9d1c09619a77d49");
        _mongocrypt_buffer_copy_from_hex(&expect_S_KeyId, "12345678123498761234123456789012");
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);
        const _mongocrypt_buffer_t *got = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_S_KeyId, *got);
        bson_type_t got_bson_type = mc_FLE2IndexedEncryptedValue_get_original_bson_type(iev, status);
        ASSERT_OR_PRINT(got_bson_type == BSON_TYPE_UTF8, status);
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&expect_S_KeyId);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test too-short input. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, "07123456781234");
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status),
                            status,
                            "expected byte length >= 17 got: 7");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test incorrect fle_blob_subtype */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input,
                                         "06123456781234987612341234567890120243bba14ddf42da823c33569f4689f465a"
                                         "606d2cea84e6b7468167d90ee12e269b9bc8774d41b16eed047cad03ca43276222581"
                                         "0a66a1dce187d8ce044fb3d2a9e9100f8824502a3825e12db71e328f4e4ebb80fac99"
                                         "52661f54a98496381ed7a342c4a9bb22bf60be642ca7cc75c2a181ce99dd03a824a85"
                                         "c6cc5fbd0fdc22a3b0316f5d1934d6b1f2a07be8d890250814c7e6b3e5f20bff1ebd0"
                                         "8638c0faa47a784995f8dfe4c2947b43b4c97b4970539930da449edff2a23ca459653"
                                         "6e7f339da76fc9c7c9d1c09619a77d49");
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status),
                            status,
                            "expected fle_blob_subtype 7 or 9 got: 6");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test double parsing */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input,
                                         "07123456781234987612341234567890120243bba14ddf42da823c33569f4689f465a"
                                         "606d2cea84e6b7468167d90ee12e269b9bc8774d41b16eed047cad03ca43276222581"
                                         "0a66a1dce187d8ce044fb3d2a9e9100f8824502a3825e12db71e328f4e4ebb80fac99"
                                         "52661f54a98496381ed7a342c4a9bb22bf60be642ca7cc75c2a181ce99dd03a824a85"
                                         "c6cc5fbd0fdc22a3b0316f5d1934d6b1f2a07be8d890250814c7e6b3e5f20bff1ebd0"
                                         "8638c0faa47a784995f8dfe4c2947b43b4c97b4970539930da449edff2a23ca459653"
                                         "6e7f339da76fc9c7c9d1c09619a77d49");
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);
        ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status),
                            status,
                            "must not be called twice");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test attempting to get S_KeyId before parsing. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        iev = mc_FLE2IndexedEncryptedValue_new();
        const _mongocrypt_buffer_t *got = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "must be called after mc_FLE2IndexedEncryptedValue_parse");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        mongocrypt_status_destroy(status);
    }
}

static void test_FLE2IndexedEqualityEncryptedValueTokens_init_from_buf(_mongocrypt_tester_t *tester) {
    uint64_t counter = 45;
    _mongocrypt_buffer_t expected_edc_token;
    _mongocrypt_buffer_t expected_esc_token;
    _mongocrypt_buffer_t expected_ecc_token;
    _mongocrypt_buffer_t input_token_set;

    _mongocrypt_buffer_copy_from_hex(&expected_edc_token,
                                     "97C8DFE394D80A4EE335E3F9FDC024D18BE4B92F9444FCA316FF9896D7BF455D");

    _mongocrypt_buffer_copy_from_hex(&expected_esc_token,
                                     "EBB22F74BE0FA4AD863188D3F33AF0B95CB4CA4ED0091E1A43513DB20E9D59AE");

    _mongocrypt_buffer_copy_from_hex(&expected_ecc_token,
                                     "A1DF0BB04C977BD4BC0B487FFFD2E3BBB96078354DE9F204EE5872BB10F01971");

    _mongocrypt_buffer_copy_from_hex(&input_token_set,
                                     "2D00000000000000"
                                     "97C8DFE394D80A4EE335E3F9FDC024D18BE4B92F9444FCA316FF9896D7BF455D"
                                     "EBB22F74BE0FA4AD863188D3F33AF0B95CB4CA4ED0091E1A43513DB20E9D59AE"
                                     "A1DF0BB04C977BD4BC0B487FFFD2E3BBB96078354DE9F204EE5872BB10F01971");

    /* Test function mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer.
     */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2IndexedEqualityEncryptedValueTokens *tokens = mc_FLE2IndexedEqualityEncryptedValueTokens_new();

        ASSERT(mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer(tokens, &input_token_set, status));
        ASSERT_CMPUINT64(counter, ==, tokens->counter);
        ASSERT_CMPBUF(expected_edc_token, tokens->edc);
        ASSERT_CMPBUF(expected_esc_token, tokens->esc);
        ASSERT_CMPBUF(expected_ecc_token, tokens->ecc);

        mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(tokens);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&input_token_set);
    _mongocrypt_buffer_cleanup(&expected_ecc_token);
    _mongocrypt_buffer_cleanup(&expected_esc_token);
    _mongocrypt_buffer_cleanup(&expected_edc_token);
}

static void test_FLE2IndexedEqualityEncryptedValue_decrypt(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t input_with_tokens;
    _mongocrypt_buffer_t expected_edc_token;
    _mongocrypt_buffer_t expected_esc_token;
    _mongocrypt_buffer_t expected_ecc_token;
    _mongocrypt_buffer_t correct_S_Key;
    _mongocrypt_buffer_t correct_K_Key;
    mc_FLE2IndexedEncryptedValue_t *iev;
    _mongocrypt_buffer_t expect_S_KeyId;
    _mongocrypt_buffer_t expect_K_KeyId;
    _mongocrypt_buffer_t expect_client_value;
    mongocrypt_t *crypt;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_copy_from_hex(&input,
                                     "07123456781234987612341234567890120243bba14ddf42da823c33569f4689f465a606"
                                     "d2cea84e6b7468167d90ee12e269b9bc8774d41b16eed047cad03ca432762225810a66a1"
                                     "dce187d8ce044fb3d2a9e9100f8824502a3825e12db71e328f4e4ebb80fac9952661f54a"
                                     "98496381ed7a342c4a9bb22bf60be642ca7cc75c2a181ce99dd03a824a85c6cc5fbd0fdc"
                                     "22a3b0316f5d1934d6b1f2a07be8d890250814c7e6b3e5f20bff1ebd08638c0faa47a784"
                                     "995f8dfe4c2947b43b4c97b4970539930da449edff2a23ca4596536e7f339da76fc9c7c9"
                                     "d1c09619a77d49");

    _mongocrypt_buffer_copy_from_hex(&input_with_tokens,
                                     "07000000000000000000000000000000000297044B8E1B5CF4F9052EDB50236A343597C4"
                                     "18A74352F98357A77E0D4299C04151CBEC24A5D5349A5A5EAA1FE334154FEEB6C8E7BD63"
                                     "6089904F76950B2184D146792CBDF9179FFEDDB7D90FC257BB13DCB3E731182A447E2EF1"
                                     "BE7A2AF13DC9362701BABDE0B5E78CF4A92227D5B5D1E1556E75BAB5B4E9F5CEFEA3BA3E"
                                     "3D5D31D11B20619437A30550EFF5B602357567CF05058E4F84A103293F70302F3A506676"
                                     "42DD0325D194A197");

    _mongocrypt_buffer_copy_from_hex(&expected_edc_token,
                                     "97C8DFE394D80A4EE335E3F9FDC024D18BE4B92F9444FCA316FF9896D7BF455D");

    _mongocrypt_buffer_copy_from_hex(&expected_esc_token,
                                     "EBB22F74BE0FA4AD863188D3F33AF0B95CB4CA4ED0091E1A43513DB20E9D59AE");

    _mongocrypt_buffer_copy_from_hex(&expected_ecc_token,
                                     "A1DF0BB04C977BD4BC0B487FFFD2E3BBB96078354DE9F204EE5872BB10F01971");

    _mongocrypt_buffer_copy_from_hex(&expect_S_KeyId, "12345678123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&expect_K_KeyId, "abcdefab123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&correct_S_Key,
                                     "7dbfebc619aa68a659f64b8e23ccd21644ac326cb74a26840c3d2420176c40ae088294d0"
                                     "0ad6cae9684237b21b754cf503f085c25cd320bf035c3417416e1e6fe3d9219f79586582"
                                     "112740b2add88e1030d91926ae8afc13ee575cfb8bb965b7");
    _mongocrypt_buffer_copy_from_hex(&correct_K_Key,
                                     "a7ddbc4c8be00d51f68d9d8e485f351c8edc8d2206b24d8e0e1816d005fbe520e4891250"
                                     "47d647b0d8684bfbdbf09c304085ed086aba6c2b2b1677ccc91ced8847a733bf5e5682c8"
                                     "4b3ee7969e4a5fe0e0c21e5e3ee190595a55f83147d8de2a");
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&expect_client_value,
                                                      (const uint8_t *)"\x09\x00\x00\x00value123\x00",
                                                      13));

    /* Test success. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);

        const _mongocrypt_buffer_t *got = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_S_KeyId, *got);

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_add_S_Key(crypt->crypto, iev, &correct_S_Key, status), status);

        got = mc_FLE2IndexedEncryptedValue_get_K_KeyId(iev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_K_KeyId, *got);

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_add_K_Key(crypt->crypto, iev, &correct_K_Key, status), status);
        got = mc_FLE2IndexedEncryptedValue_get_ClientValue(iev, status);
        ASSERT_CMPBUF(expect_client_value, *got);
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        mongocrypt_status_destroy(status);
    }

    /* Test success for IEV equality. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        iev = mc_FLE2IndexedEncryptedValue_new();
        mc_FLE2IndexedEqualityEncryptedValueTokens *tokens = mc_FLE2IndexedEqualityEncryptedValueTokens_new();

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input_with_tokens, status), status);

        _mongocrypt_buffer_t TokenKey;
        ASSERT(_mongocrypt_buffer_from_subrange(&TokenKey,
                                                &correct_S_Key,
                                                correct_S_Key.len - MONGOCRYPT_TOKEN_KEY_LEN,
                                                MONGOCRYPT_TOKEN_KEY_LEN));

        mc_ServerDataEncryptionLevel1Token_t *token =
            mc_ServerDataEncryptionLevel1Token_new(crypt->crypto, &TokenKey, status);

        ASSERT(token);

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_decrypt_equality(crypt->crypto, iev, token, tokens, status),
                         status);

        ASSERT_CMPUINT64(123456, ==, tokens->counter);
        ASSERT_CMPBUF(expected_edc_token, tokens->edc);
        ASSERT_CMPBUF(expected_esc_token, tokens->esc);
        ASSERT_CMPBUF(expected_ecc_token, tokens->ecc);

        mc_ServerDataEncryptionLevel1Token_destroy(token);
        mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(tokens);
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        mongocrypt_status_destroy(status);
    }

    /* Test that parse and write work correctly. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        iev = mc_FLE2IndexedEncryptedValue_new();
        mc_FLE2IndexedEqualityEncryptedValueTokens *tokens = mc_FLE2IndexedEqualityEncryptedValueTokens_new();

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input_with_tokens, status), status);

        _mongocrypt_buffer_t TokenKey;
        ASSERT(_mongocrypt_buffer_from_subrange(&TokenKey,
                                                &correct_S_Key,
                                                correct_S_Key.len - MONGOCRYPT_TOKEN_KEY_LEN,
                                                MONGOCRYPT_TOKEN_KEY_LEN));

        mc_ServerDataEncryptionLevel1Token_t *token =
            mc_ServerDataEncryptionLevel1Token_new(crypt->crypto, &TokenKey, status);

        ASSERT(token);

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_decrypt_equality(crypt->crypto, iev, token, tokens, status),
                         status);

        _mongocrypt_buffer_t write_buffer;
        _mongocrypt_buffer_init_size(&write_buffer, input_with_tokens.len);

        const bson_type_t bson_type = mc_FLE2IndexedEncryptedValue_get_original_bson_type(iev, status);
        const _mongocrypt_buffer_t *S_KeyId = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
        const _mongocrypt_buffer_t *ClientEncryptedValue =
            mc_FLE2IndexedEncryptedValue_get_ClientEncryptedValue(iev, status);

        ASSERT(mc_FLE2IndexedEncryptedValue_write(crypt->crypto,
                                                  bson_type,
                                                  S_KeyId,
                                                  ClientEncryptedValue,
                                                  token,
                                                  tokens,
                                                  &write_buffer,
                                                  status));

        mc_FLE2IndexedEncryptedValue_t *iev2;
        iev2 = mc_FLE2IndexedEncryptedValue_new();

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev2, &write_buffer, status), status);

        mc_FLE2IndexedEqualityEncryptedValueTokens *tokens2 = mc_FLE2IndexedEqualityEncryptedValueTokens_new();

        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_decrypt_equality(crypt->crypto, iev2, token, tokens2, status),
                         status);

        ASSERT_CMPUINT64(tokens->counter, ==, tokens2->counter);
        ASSERT_CMPBUF(tokens->edc, tokens2->edc);
        ASSERT_CMPBUF(tokens->esc, tokens2->esc);
        ASSERT_CMPBUF(tokens->ecc, tokens2->ecc);

        _mongocrypt_buffer_cleanup(&write_buffer);
        mc_ServerDataEncryptionLevel1Token_destroy(token);
        mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(tokens);
        mc_FLE2IndexedEqualityEncryptedValueTokens_destroy(tokens2);
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        mc_FLE2IndexedEncryptedValue_destroy(iev2);
        mongocrypt_status_destroy(status);
    }

    /* Test an incorrect S_Key. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t incorrect_S_Key;

        _mongocrypt_buffer_init(&incorrect_S_Key);
        _mongocrypt_buffer_copy_to(&correct_S_Key, &incorrect_S_Key);
        /* The last 32 bytes of S_Key are used to generate
         * ServerDataEncryptionLevel1Token. Change last byte to make S_Key
         * incorrect. */
        incorrect_S_Key.data[incorrect_S_Key.len - 1] = 0;
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);
        /* Since S_Key is used for non-AEAD encryption, decryption does not return
         * an error. The output is garbled. It fails to parse the decrypted Inner
         * struct. */
        ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValue_add_S_Key(crypt->crypto, iev, &incorrect_S_Key, status),
                            status,
                            "expected byte length");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&incorrect_S_Key);
        mongocrypt_status_destroy(status);
    }

    /* Test an incorrect K_Key. */
    {
        _mongocrypt_buffer_t incorrect_K_Key;
        mongocrypt_status_t *status = mongocrypt_status_new();

        _mongocrypt_buffer_init(&incorrect_K_Key);
        _mongocrypt_buffer_copy_to(&correct_K_Key, &incorrect_K_Key);
        /* The second 32 bytes of K_Key is used for the mac key. Modify one byte
         * to get a decryption error. */
        incorrect_K_Key.data[32] = 0;
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_add_S_Key(crypt->crypto, iev, &correct_S_Key, status), status);
        ASSERT_FAILS_STATUS(mc_FLE2IndexedEncryptedValue_add_K_Key(crypt->crypto, iev, &incorrect_K_Key, status),
                            status,
                            "HMAC validation failure");
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&incorrect_K_Key);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&expect_client_value);
    _mongocrypt_buffer_cleanup(&correct_K_Key);
    _mongocrypt_buffer_cleanup(&expect_K_KeyId);
    _mongocrypt_buffer_cleanup(&correct_S_Key);
    _mongocrypt_buffer_cleanup(&expect_S_KeyId);
    _mongocrypt_buffer_cleanup(&expected_ecc_token);
    _mongocrypt_buffer_cleanup(&expected_esc_token);
    _mongocrypt_buffer_cleanup(&expected_edc_token);
    _mongocrypt_buffer_cleanup(&input_with_tokens);
    _mongocrypt_buffer_cleanup(&input);
    mongocrypt_destroy(crypt);
}

static void test_FLE2IndexedRangeEncryptedValue_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_S_KeyId;
    mc_FLE2IndexedEncryptedValue_t *iev;

    /* Test successful parse. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input,
                                         "091234567812349876123412345678901210b0f174222d077efed93219cdb7fcadfa6"
                                         "be2c9d17c114c346253be6a60b7205f1d5f627f1a64594678a15b756a258a0b324d01"
                                         "222b74c7d61089010440fc4f7135e70b1e2dd5faae398edc446a880164c31ee43d01d"
                                         "530f78118d49e799aaf548700fb7e5874c9247f49b913c23214df653588ce81bf802b"
                                         "884e2c988f3b61dd15ed9ee40e80f1eda7f1f6f33ffa1f4d599be97d8719f8fdf6b1f"
                                         "20b6f705d5d3e732eb9327395dbde6c83c44f5a6d3ad1f1e3c6d18b8499eab0184e1b"
                                         "67a4072b377f44b9877225acb4a3ad111e6e469a37a79ef4c063bc404f50b50a09a7f"
                                         "ecded2bb7d1d2bf6e4e7d855be534eea2de750fc2a31dd770de3e86cd1a7f38ab715a"
                                         "8393b82b0f0fe0419fa26156cf1c9d9554344a9a4655f6a7f6d223d4216ee5da18613"
                                         "e1a7735fb2fd1e29cab13584e5cb43f50c618736f6715b3924ef7a8f89a9c0e6a5a05"
                                         "ff8a89872aa5cd3129a025c64345fd69e1299626c5c50ec52646857d8cc80c8c40d9d"
                                         "ea5cf055e9778fe57043bcce3ff6c29da16ee843f5390c3765aaec69618435fe390c5"
                                         "b8ae51b0a5f5f7cbd94e5aad1f6e188402f5fa7a104ccb694cea7feafadccce5e4afb"
                                         "749190e9101f962ab599a0f5f0f379ca3772361afb057b2b2ba7f4d435f65ebe2ce75"
                                         "00047143fd14e6199be078ca83c49b9e8e1387e5b36b723444c80cd18ce1454273f81"
                                         "5f3c7697d34f75ecae102750d50db03f530d382072fd004999cf915982b9ee8582baf"
                                         "e6f9a4a4df0896b8b246fd0f191858af865c2e40ddce6bb27cae92180bc83c3e2f783"
                                         "2518f771711acf2321ea0fabbf0b7bd256b1a3620e86f5170dcf3d5af6abb614cece5"
                                         "16de4536b03cb978546322f5ab55d25980a20aee6f46fefb06f4542637f607ae64688"
                                         "2940060e162e85c876c94e8ef55b072e2937c4e192c0851925b74cc8763e780f19956"
                                         "65e2896e98f3edafd3c81414e95346e3e679497263511a2090e0cd931db5412d19d7e"
                                         "6fb6670925dfd4e41c7a89a40eb44407bbf20f61d7e6b376fca2013201f727b0e71b6"
                                         "569361609f557b584c0c672b420823902a5d3fb965a1ce90887f37eed0fff358189ab"
                                         "f7921bb881deff1db4a0787d85e1875042e7090ea15d858e06f48594672a66cfdfd2a"
                                         "c67e457b639f40f47312cb71a16f7d21d3ef828b439cbdaf6b3d89345bc5aa2a1f736"
                                         "a483fb5efa45288a103bbabc2d7f73eda875621f1c6e2eaeb3c839cbd5b339f87cf36"
                                         "8d4eef413fb7d5ad5969e0cc6c557509e99cfbf7741cd92c8b5a8c26d723ac7d8c420"
                                         "5c9dc14f202e247f4e14af221aa4b31e1a29f61a79cfc9afc7cdf83cf8e005f646b78"
                                         "c3feea7a27be4f8b28a06555a80e85de397c61f15d2140309317b918141556f6912d8"
                                         "9bfe122c605fe780dec62eded3eacbdac1b90de2b280d745ccc73a9513be82f980326"
                                         "0ee7119b86f19aeef270d64241472e48aa0c5165aee80e9e522c81090531aeb957ff7"
                                         "0a445791043a242c1964cbd3801a24631dd6411e8ba56ef4c562d7c9085abf86671ca"
                                         "ce021348e7bb99490c277e4d0cc0feaacc5763bb166c6cfe6fad03585ef268c5730cf"
                                         "cc81a358ba5c90c44cc603776d0e5213f985c5d8e8643e7261bf38abf3bb123c0603d"
                                         "3124e9f4c14466ded55777f94c35a235464a9a352039d8c1a13480915cc03b69eaa90"
                                         "360db7c7a67a1500c179e7d191f73d16852206a1ee3787d4ff37381e4b61a7531cc08"
                                         "918c1176580f71e0ff05235b90d44d027ce0841842aae22aa5777c029b415f6a8eebb"
                                         "adf4fb08e8699ff7f3568dee78245368cd83b630f7bae1f8acc35bdcb0e3c89157d43"
                                         "6797845a41aaf5413f0058af7de89ae1c0346bc6752f365c5bb5af13963558864de20"
                                         "cbd49970d807110abc01df10e1f01bc5ef1727afed36f1be02f02e2c4bcd26d6e10bf"
                                         "e048bf2685e996fbad0e55b977a8382845a3e06202aa080ce594afd130a46df1642dd"
                                         "689ace1eb9fea08fbe92bbf5538c9093a3f5f0c8366c4d7c89b7be2dd58db2a9f4d55"
                                         "ef20e64be540bea405f1fda9e2b38ec96b0673b0a8c974ababd2c2b86abd78c3d1e9a"
                                         "ba8fadc92d4d7a4dade928293a9cc10437509c2e6c1c2d4e5935b7ba2ef0e07d0d282"
                                         "b993a232771b4833a57a09fc0b4cd96ba67989801c7bc6bc3b0de3af20449ca05a82e"
                                         "8638ce306f225ff7a46bc5cff944f1beca387ca555397430a1b64c7586dffec5cd25b"
                                         "31b5ab15cd52f6b7655cbfe79d8bd8ad1327381276ecccf9befb4ea5de1fc90fb08b5"
                                         "abee55bee2c333cbb9d41e28698aa9194659dea552e410589bd703c6032d71da178ce"
                                         "4a8a476ac74086ba27f8d75b185071cab5062a79d55d8a1c12ebecbf0841100dc7514"
                                         "555f789b70b46a0876795a66088c96aecfa0ae4a4068d2d9c1756ecde82db49d06a13"
                                         "c734270c85a4be5be44b7646a36e6ca5f3e9fbf35d6b849901844d9ad9c67c1d93465"
                                         "d16c7fa3f576bf1fbdaa71b20eae7609290c7eda5f9ec31702702fea9b3b53e9a06e0"
                                         "34f5058efd58d3129a78976c86ed47c849aae5a62a1c949f176133bcf7c7e1dd088f3"
                                         "d65b313312f822ca81ee4b9f70e4781459da6172885cb8d1c0ce5564d00aff776f09b"
                                         "de5f47ed030e8893db768c40775db76b29a3cfd37fd8edf7c0c686e8e13ca57a74898"
                                         "11a5bb0ceaf415f4fdd7725e5273cd3dcaaab00f585e877652e3566449535bfb1a2f7"
                                         "daff5a90dc00e0977b639560238cbe5f20c7de4014d1473c83a602c7f86a727e29776"
                                         "fabc87fbfe67550867084d0a34e8e3ac54bdd9134b0fbd46521591f955ccc36b4a5c1"
                                         "5122a7b7d022e0b2c9addbc56f0adda8af808c1515a88dcc83294dc51c36dfde69a40"
                                         "6e1784d6901ca2a2e80ce1b23aceba63c9091a96aab34026766647c36380dba6edc3e"
                                         "71e04df9481272c0793f9c0d22ea7502f53927b875be5141372820dd63bf309182cf8"
                                         "f31809fe4960f7008b43ebd7b6b858273d7946082e00314b06b4dc57f3227a5db42ae"
                                         "967a6b23efe55ed46f100a06252734519f09ed45537fc92f6334f7845ed7cd9ce584a"
                                         "c4f61e6e3767a7695ae28d8f93265771cfe014b6bb89c911dbc64f522830a56d252b9"
                                         "b0923059b9d998fe706d03618863bc40bf6056914e9eed311e9bbc32a06f7d919fd87"
                                         "884faa054491f3dbfcf20cfabbf4d53fdcd51f576793d3c4e78530df23ddc4bfdf7ae"
                                         "92f17bd6c108e9659dd9f76ea604650c6cc04ab29202c15b699a1bc1456210815b5a8"
                                         "ef33d8559c8c8250ab0f55e8c5d77a1017944890e44b59e1887391ffb16922169ef1f"
                                         "820003f92b5747bef6e6fcdcc08397db5a3345c136896c3a38ff7dbc3a2a2d0f15633"
                                         "612d3f7f758c304e9be67758cf6e6e0b402250b5d82");
        _mongocrypt_buffer_copy_from_hex(&expect_S_KeyId, "12345678123498761234123456789012");
        iev = mc_FLE2IndexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2IndexedEncryptedValue_parse(iev, &input, status), status);
        const _mongocrypt_buffer_t *got = mc_FLE2IndexedEncryptedValue_get_S_KeyId(iev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_S_KeyId, *got);
        bson_type_t got_bson_type = mc_FLE2IndexedEncryptedValue_get_original_bson_type(iev, status);
        ASSERT_OR_PRINT(got_bson_type == BSON_TYPE_INT32, status);
        mc_FLE2IndexedEncryptedValue_destroy(iev);
        _mongocrypt_buffer_cleanup(&expect_S_KeyId);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }
}

void _mongocrypt_tester_install_fle2_payloads(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_FLE2IndexedEqualityEncryptedValue_parse);
    INSTALL_TEST(test_FLE2IndexedEqualityEncryptedValueTokens_init_from_buf);
    INSTALL_TEST(test_FLE2IndexedEqualityEncryptedValue_decrypt);
    INSTALL_TEST(test_FLE2IndexedRangeEncryptedValue_parse);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-iup-v2.c000066400000000000000000000334161521103432300225670ustar00rootroot00000000000000/*
 * Copyright 2023-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 "mc-fle-blob-subtype-private.h"
#include "mc-fle2-insert-update-payload-private-v2.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"

#define TEST_IUP_HEX_V2                                                                                                \
    "045f0100000564002000000000d9a58a50d253b4d6dc55504a242a051377a7e33dd6b5065"                                        \
    "50fb1e41097f5a621057300200000000004516951c06f8d5cb5ef5ec42cd7d419b6f4e896"                                        \
    "a0f7d0609626a0cda8ced6990570003000000000993cc8be4fbe7d8b9ab5871c12204c3fb"                                        \
    "2ad0ce29eb5f6b96a224b2bd7b41bb994c3ea2679497e95da93be2cb9ab73c30575001000"                                        \
    "0000044c2352e235314b429c897d833b411b771074000200000005760050000000004c235"                                        \
    "2e235314b429c897d833b411b777e6c20c2aa5e2df89f5ccaceb9a5ed9164a7e2802c9312"                                        \
    "139f5580775e846eb328b5804603d0bde6c12cf5d96b8b6b7a173bb25088a5ba8ce754f4f"                                        \
    "4d57088ba0565002000000000a4c5de2625c5d5eae40076f0cc0f581c8d4784f2a3906035"                                        \
    "e710202432a17d99056c002000000000c3c8980ed11e63ec199104bc9a0889322c9eb80d0"                                        \
    "0eee0b5148dc83f7e78adb7126b00020000000000000000"

static void _test_FLE2InsertUpdatePayloadV2_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    mc_FLE2InsertUpdatePayloadV2_t iup;
    _mongocrypt_buffer_t expect_edcDerivedToken;
    _mongocrypt_buffer_t expect_escDerivedToken;
    _mongocrypt_buffer_t expect_encryptedTokens;
    _mongocrypt_buffer_t expect_indexKeyId;
    bson_type_t expect_valueType = BSON_TYPE_UTF8;
    _mongocrypt_buffer_t expect_value;
    _mongocrypt_buffer_t expect_serverEncryptionToken;
    _mongocrypt_buffer_t expect_serverDerivedFromDataToken;
    _mongocrypt_buffer_t expect_userKeyId;

    _mongocrypt_buffer_copy_from_hex(&expect_edcDerivedToken,
                                     "D9A58A50D253B4D6DC55504A242A051377A7E33DD6B506550FB1E41097F5A621");

    _mongocrypt_buffer_copy_from_hex(&expect_escDerivedToken,
                                     "04516951C06F8D5CB5EF5EC42CD7D419B6F4E896A0F7D0609626A0CDA8CED699");

    _mongocrypt_buffer_copy_from_hex(&expect_encryptedTokens,
                                     "993CC8BE4FBE7D8B9AB5871C12204C3FB2AD0CE29EB5F6B96A224B2BD7B41BB99"
                                     "4C3EA2679497E95DA93BE2CB9AB73C3");

    _mongocrypt_buffer_copy_from_hex(&expect_indexKeyId, "4c2352e235314b429c897d833b411b77");

    _mongocrypt_buffer_copy_from_hex(&expect_value,
                                     "4C2352E235314B429C897D833B411B777E6C20C2AA5E2DF89F5CCACEB9A5ED9"
                                     "164A7E2802C9312139F5580775E846EB328B5804603D0BDE6C12CF5D96B8B6B"
                                     "7A173BB25088A5BA8CE754F4F4D57088BA");

    _mongocrypt_buffer_copy_from_hex(&expect_serverEncryptionToken,
                                     "A4C5DE2625C5D5EAE40076F0CC0F581C8D4784F2A3906035E710202432A17D99");

    _mongocrypt_buffer_copy_from_hex(&expect_serverDerivedFromDataToken,
                                     "C3C8980ED11E63EC199104BC9A0889322C9EB80D00EEE0B5148DC83F7E78ADB7");

    _mongocrypt_buffer_copy_from_hex(&expect_userKeyId, "4c2352e235314b429c897d833b411b77");

    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX_V2);
        mc_FLE2InsertUpdatePayloadV2_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&iup, &input, status), status);

        ASSERT_CMPBUF(expect_edcDerivedToken, iup.edcDerivedToken);
        ASSERT_CMPBUF(expect_escDerivedToken, iup.escDerivedToken);
        ASSERT_CMPBUF(expect_encryptedTokens, iup.encryptedTokens);
        ASSERT_CMPBUF(expect_indexKeyId, iup.indexKeyId);
        ASSERT(expect_valueType == iup.valueType);
        ASSERT_CMPBUF(expect_value, iup.value);
        ASSERT_CMPBUF(expect_serverEncryptionToken, iup.serverEncryptionToken);
        ASSERT_CMPBUF(expect_serverDerivedFromDataToken, iup.serverDerivedFromDataToken);
        ASSERT_CMPBUF(expect_userKeyId, iup.userKeyId);
        mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&expect_edcDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_escDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_encryptedTokens);
    _mongocrypt_buffer_cleanup(&expect_indexKeyId);
    _mongocrypt_buffer_cleanup(&expect_value);
    _mongocrypt_buffer_cleanup(&expect_serverEncryptionToken);
    _mongocrypt_buffer_cleanup(&expect_serverDerivedFromDataToken);
    _mongocrypt_buffer_cleanup(&expect_userKeyId);
}

static void _test_mc_FLE2InsertUpdatePayloadV2_decrypt(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    mc_FLE2InsertUpdatePayloadV2_t iup;
    _mongocrypt_buffer_t expect_plaintext;
    _mongocrypt_buffer_t correct_key;
    mongocrypt_t *crypt;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_copy_from_hex(&correct_key,
                                     "2d4a2ca8e12c47c0c7ba29f878556ff563b1e083a7aae5dc40c7b61a0094f558198b88f7"
                                     "5007e0eea658b4aab6e5a86908e5efeabe9b48c2b290052665f60828c87b19781ed935a0"
                                     "4d4366e104a3b66996d0108ae62c4c675321c6f8c2871436");

    ASSERT(
        _mongocrypt_buffer_copy_from_data_and_size(&expect_plaintext, (const uint8_t *)"\x08\x00\x00\x00shreyas", 12));

    /* Test successful decrypt. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX_V2);
        mc_FLE2InsertUpdatePayloadV2_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&iup, &input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2InsertUpdatePayloadV2_decrypt(crypt->crypto, &iup, &correct_key, status);
        ASSERT_OK_STATUS(got != NULL, status);
        ASSERT_CMPBUF(expect_plaintext, *got);

        mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test incorrect key. */
    {
        _mongocrypt_buffer_t incorrect_key;

        _mongocrypt_buffer_init(&incorrect_key);
        _mongocrypt_buffer_copy_to(&correct_key, &incorrect_key);
        /* The middle 32 bytes of key are used to generate the mac. Change first
         * byte to make user key incorrect. */
        incorrect_key.data[32] = 0;

        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX_V2);
        mc_FLE2InsertUpdatePayloadV2_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&iup, &input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2InsertUpdatePayloadV2_decrypt(crypt->crypto, &iup, &incorrect_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "HMAC validation failure");

        mc_FLE2InsertUpdatePayloadV2_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&incorrect_key);
    }

    _mongocrypt_buffer_cleanup(&expect_plaintext);
    _mongocrypt_buffer_cleanup(&correct_key);
    mongocrypt_destroy(crypt);
}

#undef TEST_IUP_HEX_V2

static void _test_mc_FLE2InsertUpdatePayloadV2_includes_crypto_params(_mongocrypt_tester_t *tester) {
    mc_FLE2InsertUpdatePayloadV2_t payload;
    mc_FLE2InsertUpdatePayloadV2_init(&payload);
    payload.sparsity = OPT_I64(1);
    payload.precision = OPT_I32(2);
    payload.trimFactor = OPT_I32(3);
    bson_value_t indexMin = {.value.v_int32 = 4, .value_type = BSON_TYPE_INT32};
    bson_value_copy(&indexMin, &payload.indexMin);
    bson_value_t indexMax = {.value.v_int32 = 5, .value_type = BSON_TYPE_INT32};
    bson_value_copy(&indexMax, &payload.indexMax);

    // Test crypto params from SERVER-91889 are included in "range" payload.
    {
        bson_t got = BSON_INITIALIZER;
        ASSERT(mc_FLE2InsertUpdatePayloadV2_serializeForRange(&payload, &got));
        _assert_match_bson(&got, TMP_BSON(BSON_STR({"sp" : 1, "pn" : 2, "tf" : 3, "mn" : 4, "mx" : 5})));
        bson_destroy(&got);
    }

    mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
}

static void _test_mc_FLE2InsertUpdatePayloadV2_parses_crypto_params(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *payload = TEST_FILE("test/data/range-sends-cryptoParams/explicit-insert-int32/expected.json");

    _mongocrypt_buffer_t payload_buf;
    // Unwrap the { "v":  } into a `_mongocrypt_buffer_t`.
    {
        bson_t payload_bson;
        ASSERT(_mongocrypt_binary_to_bson(payload, &payload_bson));
        bson_iter_t iter;
        ASSERT(bson_iter_init_find(&iter, &payload_bson, "v"));
        ASSERT(_mongocrypt_buffer_from_binary_iter(&payload_buf, &iter));
    }

    mc_FLE2InsertUpdatePayloadV2_t got;
    mc_FLE2InsertUpdatePayloadV2_init(&got);

    mongocrypt_status_t *status = mongocrypt_status_new();
    ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&got, &payload_buf, status), status);
    mongocrypt_status_destroy(status);

    ASSERT(got.sparsity.set);
    ASSERT_CMPINT64(got.sparsity.value, ==, 3);

    ASSERT(!got.precision.set); // Payload does not include precision.

    ASSERT(got.trimFactor.set);
    ASSERT_CMPINT32(got.trimFactor.value, ==, 4);

    ASSERT(got.indexMin.value_type == BSON_TYPE_INT32);
    ASSERT_CMPINT32(got.indexMin.value.v_int32, ==, 0);

    ASSERT(got.indexMax.value_type == BSON_TYPE_INT32);
    ASSERT_CMPINT32(got.indexMax.value.v_int32, ==, 1234567);

    mc_FLE2InsertUpdatePayloadV2_cleanup(&got);
}

static void _test_mc_FLE2InsertUpdatePayloadV2_parse_errors(_mongocrypt_tester_t *tester) {
    // Test wrong type for 't':
    {
        bson_t *input_bson = TMP_BSON_STR(BSON_STR({
            "d" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}}, //
            "t" : "wrong type!"
        }));
        _mongocrypt_buffer_t input_buf;
        _mongocrypt_buffer_init_size(&input_buf, 1 + input_bson->len);
        input_buf.data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayloadV2;
        memcpy(input_buf.data + 1, bson_get_data(input_bson), input_bson->len);

        mc_FLE2InsertUpdatePayloadV2_t payload;
        mc_FLE2InsertUpdatePayloadV2_init(&payload);
        mongocrypt_status_t *status = mongocrypt_status_new();
        ASSERT_FAILS_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&payload, &input_buf, status),
                            status,
                            "Field 't' expected to hold an int32");
        mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&input_buf);
    }

    // Test negative 'k':
    {
        bson_t *input_bson = TMP_BSON_STR(BSON_STR({"k" : {"$numberLong" : "-1"}}));
        _mongocrypt_buffer_t input_buf;
        _mongocrypt_buffer_init_size(&input_buf, 1 + input_bson->len);
        input_buf.data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayloadV2;
        memcpy(input_buf.data + 1, bson_get_data(input_bson), input_bson->len);

        mc_FLE2InsertUpdatePayloadV2_t payload;
        mc_FLE2InsertUpdatePayloadV2_init(&payload);
        mongocrypt_status_t *status = mongocrypt_status_new();
        ASSERT_FAILS_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&payload, &input_buf, status),
                            status,
                            "contention must be non-negative");
        mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&input_buf);
    }

    // Test negative 'sp':
    {
        bson_t *input_bson = TMP_BSON_STR(BSON_STR({"sp" : {"$numberLong" : "-1"}}));
        _mongocrypt_buffer_t input_buf;
        _mongocrypt_buffer_init_size(&input_buf, 1 + input_bson->len);
        input_buf.data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayloadV2;
        memcpy(input_buf.data + 1, bson_get_data(input_bson), input_bson->len);

        mc_FLE2InsertUpdatePayloadV2_t payload;
        mc_FLE2InsertUpdatePayloadV2_init(&payload);
        mongocrypt_status_t *status = mongocrypt_status_new();
        ASSERT_FAILS_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&payload, &input_buf, status),
                            status,
                            "sparsity must be non-negative");
        mc_FLE2InsertUpdatePayloadV2_cleanup(&payload);
        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&input_buf);
    }
}

void _mongocrypt_tester_install_fle2_payload_iup_v2(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_FLE2InsertUpdatePayloadV2_parse);
    INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_decrypt);
    INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_includes_crypto_params);
    INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_parses_crypto_params);
    INSTALL_TEST(_test_mc_FLE2InsertUpdatePayloadV2_parse_errors);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-iup.c000066400000000000000000000230671521103432300222430ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-fle-blob-subtype-private.h"
#include "mc-fle2-insert-update-payload-private.h"
#include "test-mongocrypt.h"

#define TEST_IUP_HEX                                                                                                   \
    "0471010000056400200000000076fad9a57bfa6aa6686728b4f2dd1b728fed2f1d885c163"                                        \
    "0b33fe6b62da8bac405730020000000001527a3961d1bf73ad0a8cc7a6ddd9cdf0616b166"                                        \
    "2acbbf707e1bc3ee69a3b8120563002000000000b195d639603e5220816eb24d07a6c77e5"                                        \
    "07f727ee9592bf058dda3c3814f78850570005000000000c743d675769ea788d5e5c440db"                                        \
    "240df942cbc23bfc7befa83289e97c6386b3d059d2ea8f3fbc1a8b0652c07b03c79efee1b"                                        \
    "dfe0b8436e2f6e570fe171c64bf52cb5ed6af1ce94b17b896d2ef7269baaa057500100000"                                        \
    "000412345678123498761234123456789012107400020000000576004d00000000abcdefa"                                        \
    "b1234987612341234567890124cd964104381e661fa1fa05c498ead216d56f9147271d68f"                                        \
    "fadf3d6ee22741b7509f33a6e06650cfd58ce309098e3e783a9d4e23c8c17fbfbcd5de81c"                                        \
    "f0565002000000000eb9a73f7912d86a4297e81d2f675af742874e4057e3a890fec651a23"                                        \
    "eee3f3ec00"

static void test_FLE2InsertUpdatePayload_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    mc_FLE2InsertUpdatePayload_t iup;
    _mongocrypt_buffer_t expect_edcDerivedToken;
    _mongocrypt_buffer_t expect_escDerivedToken;
    _mongocrypt_buffer_t expect_eccDerivedToken;
    _mongocrypt_buffer_t expect_encryptedTokens;
    _mongocrypt_buffer_t expect_indexKeyId;
    bson_type_t expect_valueType = BSON_TYPE_UTF8;
    _mongocrypt_buffer_t expect_value;
    _mongocrypt_buffer_t expect_serverEncryptionToken;
    _mongocrypt_buffer_t expect_userKeyId;

    _mongocrypt_buffer_copy_from_hex(&expect_edcDerivedToken,
                                     "76fad9a57bfa6aa6686728b4f2dd1b728fed2f1d885c1630b33fe6b62da8bac4");
    _mongocrypt_buffer_copy_from_hex(&expect_escDerivedToken,
                                     "1527a3961d1bf73ad0a8cc7a6ddd9cdf0616b1662acbbf707e1bc3ee69a3b812");
    _mongocrypt_buffer_copy_from_hex(&expect_eccDerivedToken,
                                     "b195d639603e5220816eb24d07a6c77e507f727ee9592bf058dda3c3814f7885");
    _mongocrypt_buffer_copy_from_hex(&expect_encryptedTokens,
                                     "c743d675769ea788d5e5c440db240df942cbc23bfc7befa83289e97c6386b3d059d2e"
                                     "a8f3fbc1a8b0652c07b03c79efee1bdfe0b8436e2f6e570fe171c64bf52cb5ed6af1c"
                                     "e94b17b896d2ef7269baaa");
    _mongocrypt_buffer_copy_from_hex(&expect_indexKeyId, "12345678123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&expect_value,
                                     "abcdefab1234987612341234567890124cd964104381e661fa1fa05c498ead216d56f"
                                     "9147271d68ffadf3d6ee22741b7509f33a6e06650cfd58ce309098e3e783a9d4e23c8"
                                     "c17fbfbcd5de81cf");
    _mongocrypt_buffer_copy_from_hex(&expect_serverEncryptionToken,
                                     "eb9a73f7912d86a4297e81d2f675af742874e4057e3a890fec651a23eee3f3ec");
    _mongocrypt_buffer_copy_from_hex(&expect_userKeyId, "abcdefab123498761234123456789012");

    /* Test successful parse. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX);
        mc_FLE2InsertUpdatePayload_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayload_parse(&iup, &input, status), status);
        ASSERT_CMPBUF(expect_edcDerivedToken, iup.edcDerivedToken);
        ASSERT_CMPBUF(expect_escDerivedToken, iup.escDerivedToken);
        ASSERT_CMPBUF(expect_eccDerivedToken, iup.eccDerivedToken);
        ASSERT_CMPBUF(expect_encryptedTokens, iup.encryptedTokens);
        ASSERT_CMPBUF(expect_indexKeyId, iup.indexKeyId);
        ASSERT(expect_valueType == iup.valueType);
        ASSERT_CMPBUF(expect_value, iup.value);
        ASSERT_CMPBUF(expect_serverEncryptionToken, iup.serverEncryptionToken);
        ASSERT_CMPBUF(expect_userKeyId, iup.userKeyId);
        mc_FLE2InsertUpdatePayload_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&expect_userKeyId);
    _mongocrypt_buffer_cleanup(&expect_edcDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_escDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_eccDerivedToken);
    _mongocrypt_buffer_cleanup(&expect_encryptedTokens);
    _mongocrypt_buffer_cleanup(&expect_indexKeyId);
    _mongocrypt_buffer_cleanup(&expect_value);
    _mongocrypt_buffer_cleanup(&expect_serverEncryptionToken);
}

static void test_FLE2InsertUpdatePayload_decrypt(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    mc_FLE2InsertUpdatePayload_t iup;
    _mongocrypt_buffer_t expect_plaintext;
    _mongocrypt_buffer_t correct_key;
    mongocrypt_t *crypt;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_copy_from_hex(&correct_key,
                                     "a7ddbc4c8be00d51f68d9d8e485f351c8edc8d2206b24d8e0e1816d005fbe520e4891250"
                                     "47d647b0d8684bfbdbf09c304085ed086aba6c2b2b1677ccc91ced8847a733bf5e5682c8"
                                     "4b3ee7969e4a5fe0e0c21e5e3ee190595a55f83147d8de2a");
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&expect_plaintext,
                                                      (const uint8_t *)"\x09\x00\x00\x00value123\x00",
                                                      13));

    /* Test successful decrypt. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX);
        mc_FLE2InsertUpdatePayload_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayload_parse(&iup, &input, status), status);
        const _mongocrypt_buffer_t *got = mc_FLE2InsertUpdatePayload_decrypt(crypt->crypto, &iup, &correct_key, status);
        ASSERT_OK_STATUS(got != NULL, status);
        ASSERT_CMPBUF(expect_plaintext, *got);

        mc_FLE2InsertUpdatePayload_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test incorrect key. */
    {
        _mongocrypt_buffer_t incorrect_key;

        _mongocrypt_buffer_init(&incorrect_key);
        _mongocrypt_buffer_copy_to(&correct_key, &incorrect_key);
        /* The middle 32 bytes of key are used to generate the mac. Change first
         * byte to make user key incorrect. */
        incorrect_key.data[32] = 0;

        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_IUP_HEX);
        mc_FLE2InsertUpdatePayload_init(&iup);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayload_parse(&iup, &input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2InsertUpdatePayload_decrypt(crypt->crypto, &iup, &incorrect_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "HMAC validation failure");

        mc_FLE2InsertUpdatePayload_cleanup(&iup);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&incorrect_key);
    }

    _mongocrypt_buffer_cleanup(&expect_plaintext);
    _mongocrypt_buffer_cleanup(&correct_key);
    mongocrypt_destroy(crypt);
}

#undef TEST_IUP_HEX

static void test_FLE2InsertUpdatePayload_parse_errors(_mongocrypt_tester_t *tester) {
    bson_t *input_bson = TMP_BSON_STR(BSON_STR({
        "d" : {"$binary" : {"base64" : "AAAA", "subType" : "00"}}, //
        "t" : "wrong type!"
    }));
    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_init_size(&input_buf, 1 + input_bson->len);
    input_buf.data[0] = (uint8_t)MC_SUBTYPE_FLE2InsertUpdatePayload;
    memcpy(input_buf.data + 1, bson_get_data(input_bson), input_bson->len);

    mc_FLE2InsertUpdatePayload_t payload;
    mc_FLE2InsertUpdatePayload_init(&payload);
    mongocrypt_status_t *status = mongocrypt_status_new();
    ASSERT_FAILS_STATUS(mc_FLE2InsertUpdatePayload_parse(&payload, &input_buf, status),
                        status,
                        "Field 't' expected to hold an int32");
    mc_FLE2InsertUpdatePayload_cleanup(&payload);
    mongocrypt_status_destroy(status);
    _mongocrypt_buffer_cleanup(&input_buf);
}

void _mongocrypt_tester_install_fle2_payload_iup(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_FLE2InsertUpdatePayload_parse);
    INSTALL_TEST(test_FLE2InsertUpdatePayload_decrypt);
    INSTALL_TEST(test_FLE2InsertUpdatePayload_parse_errors);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-uev-v2.c000066400000000000000000000362711521103432300225730ustar00rootroot00000000000000/*
 * Copyright 2023-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 "mc-fle2-payload-uev-v2-private.h"
#include "test-mongocrypt.h"

#define TEST_KEY_UUID_HEX "abcdefab123498761234123456789012"
#define TEST_KEY_HEX                                                                                                   \
    "a7ddbc4c8be00d51f68d9d8e485f351c8edc8d2206b24d8e0e1816d005fbe520e4891250"                                         \
    "47d647b0d8684bfbdbf09c304085ed086aba6c2b2b1677ccc91ced8847a733bf5e5682c8"                                         \
    "4b3ee7969e4a5fe0e0c21e5e3ee190595a55f83147d8de2a"
#define TEST_PLAINTEXT "\x09\x00\x00\x00value123\x00"
#define TEST_PLAINTEXT_LEN 13

// prefix = (FLE_TYPE || KEY_UUID || BSON_TYPE)
#define TEST_PREFIX_HEX "10" TEST_KEY_UUID_HEX "02"
// ciphertext = (IV || S || HMAC)
#define TEST_CIPHERTEXT_HEX                                                                                            \
    "abcdefabdeadbeeffeedbacc012345671f7c9cf4b09b2baa7f8752b9fb7a8c77469f00a1f"                                        \
    "b1735ed5b4b941f151bad3d709a1f3555788fef373088d47ceb9677"
// uev = (prefix || ciphertext)
#define TEST_UEV_HEX TEST_PREFIX_HEX TEST_CIPHERTEXT_HEX

// This ciphertext was encrypted with CTR mode and has a valid HMAC. This is for
// testing failure during the block alignment check when decrypting with CBC
// mode.
#define TEST_UEV_CTR_HEX                                                                                               \
    TEST_PREFIX_HEX "abcdefabdeadbeeffeedbacc012345679fc22f7a164b528b1018bb117"                                        \
                    "52b904cb6ee00c837d7e0b4d32b47be3617bc3783507dd676b21d7720"                                        \
                    "58b17794726884d79dd851aa8786bafdd544dab9"

static void test_FLE2UnindexedEncryptedValueV2_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_key_uuid;
    mc_FLE2UnindexedEncryptedValueV2_t *uev;

    /* Test successful parse. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        _mongocrypt_buffer_copy_from_hex(&expect_key_uuid, TEST_KEY_UUID_HEX);

        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status), status);
        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(uev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_key_uuid, *got);
        bson_type_t got_bson_type = mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type(uev, status);
        ASSERT_OR_PRINT(got_bson_type == BSON_TYPE_UTF8, status);
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&expect_key_uuid);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test too-short input. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, "10123456781234");
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status),
                            status,
                            "expected byte length >= 17 got: 7");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test incorrect fle_blob_subtype */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        input.data[0] = 5;
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status),
                            status,
                            "expected fle_blob_subtype=16 got: 5");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test double parsing */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status), status);
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status),
                            status,
                            "must not be called twice");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test attempting to get key_uuid or original_bson_type before parsing. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(uev, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "must be called after mc_FLE2UnindexedEncryptedValueV2_parse");

        mongocrypt_status_destroy(status);
        status = mongocrypt_status_new();

        bson_type_t got_bson_type = mc_FLE2UnindexedEncryptedValueV2_get_original_bson_type(uev, status);
        ASSERT_FAILS_STATUS(got_bson_type != 0, status, "must be called after mc_FLE2UnindexedEncryptedValueV2_parse");

        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        mongocrypt_status_destroy(status);
    }
}

static void test_FLE2UnindexedEncryptedValueV2_decrypt(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t correct_key;
    mc_FLE2UnindexedEncryptedValueV2_t *uev;
    _mongocrypt_buffer_t expect_key_uuid;
    _mongocrypt_buffer_t expect_plaintext;
    mongocrypt_t *crypt;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
    _mongocrypt_buffer_copy_from_hex(&expect_key_uuid, TEST_KEY_UUID_HEX);
    _mongocrypt_buffer_copy_from_hex(&correct_key, TEST_KEY_HEX);
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&expect_plaintext,
                                                      (const uint8_t *)TEST_PLAINTEXT,
                                                      TEST_PLAINTEXT_LEN));

    /* Test success. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status), status);

        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValueV2_get_key_uuid(uev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_key_uuid, *got);

        got = mc_FLE2UnindexedEncryptedValueV2_decrypt(crypt->crypto, uev, &correct_key, status);
        ASSERT_OK_STATUS(got != NULL, status);
        ASSERT_CMPBUF(expect_plaintext, *got);
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        mongocrypt_status_destroy(status);
    }

    /* Test an incorrect key. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t incorrect_key;

        _mongocrypt_buffer_init(&incorrect_key);
        _mongocrypt_buffer_copy_to(&correct_key, &incorrect_key);
        /* The middle 32 bytes of key are used to generate the mac. Change first
         * byte to make S_Key incorrect. */
        incorrect_key.data[32] = 0;
        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2UnindexedEncryptedValueV2_decrypt(crypt->crypto, uev, &incorrect_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "HMAC validation failure");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&incorrect_key);
        mongocrypt_status_destroy(status);
    }

    /* Test empty ciphertext */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t short_input;

        _mongocrypt_buffer_copy_from_hex(&short_input, TEST_PREFIX_HEX);

        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &short_input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2UnindexedEncryptedValueV2_decrypt(crypt->crypto, uev, &correct_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "input ciphertext too small");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&short_input);
        mongocrypt_status_destroy(status);
    }

    /* Test non-block aligned ciphertext */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t bad_input;

        _mongocrypt_buffer_copy_from_hex(&bad_input, TEST_UEV_CTR_HEX);

        uev = mc_FLE2UnindexedEncryptedValueV2_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &bad_input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2UnindexedEncryptedValueV2_decrypt(crypt->crypto, uev, &correct_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "ciphertext length is not a multiple of block size");
        mc_FLE2UnindexedEncryptedValueV2_destroy(uev);
        _mongocrypt_buffer_cleanup(&bad_input);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&expect_plaintext);
    _mongocrypt_buffer_cleanup(&correct_key);
    _mongocrypt_buffer_cleanup(&expect_key_uuid);
    _mongocrypt_buffer_cleanup(&input);
    mongocrypt_destroy(crypt);
}

static void test_FLE2UnindexedEncryptedValueV2_ctx_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    {
        // {v: BinData(ENCRYPTED, payload)}
        _mongocrypt_buffer_t payload;
        _mongocrypt_buffer_copy_from_hex(&payload, TEST_UEV_HEX);
        bson_t doc;
        bson_init(&doc);
        ASSERT(bson_append_binary(&doc, "v", (int)strlen("v"), BSON_SUBTYPE_ENCRYPTED, payload.data, payload.len));
        mongocrypt_binary_t *bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&doc), doc.len);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, bin), ctx);
        mongocrypt_binary_destroy(bin);
        bson_destroy(&doc);
        _mongocrypt_buffer_cleanup(&payload);
    }

    // Decryption key.
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);

    {
        _mongocrypt_buffer_t keyId;
        _mongocrypt_buffer_t key;
        _mongocrypt_buffer_copy_from_hex(&keyId, TEST_KEY_UUID_HEX);
        _mongocrypt_buffer_copy_from_hex(&key, TEST_KEY_HEX);
        _test_ctx_wrap_and_feed_key(ctx, &keyId, &key, status);
        _mongocrypt_buffer_cleanup(&key);
        _mongocrypt_buffer_cleanup(&keyId);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    // Value ready.
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));

        bson_t expect_bson;
        bson_init(&expect_bson);
        bson_value_t expect_value;
        _mongocrypt_buffer_t value_buf = {.data = (uint8_t *)TEST_PLAINTEXT, .len = TEST_PLAINTEXT_LEN};
        ASSERT(_mongocrypt_buffer_to_bson_value(&value_buf, BSON_TYPE_UTF8, &expect_value));
        ASSERT(bson_append_value(&expect_bson, "v", (int)strlen("v"), &expect_value));
        ASSERT(bson_compare(&out_bson, &expect_bson) == 0);
        bson_value_destroy(&expect_value);
        mongocrypt_binary_destroy(out);
        bson_destroy(&expect_bson);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void test_FLE2UnindexedEncryptedValueV2_encrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_status_t *status = mongocrypt_status_new();

    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t ciphertext;
    _mongocrypt_buffer_t key_uuid;
    _mongocrypt_buffer_t key;
    _mongocrypt_buffer_t prefix;
    _mongocrypt_buffer_t serialized_uev;

    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&plaintext, (const uint8_t *)TEST_PLAINTEXT, TEST_PLAINTEXT_LEN));

    /* Test encrypt/decrypt round trip */
    _mongocrypt_buffer_init(&ciphertext);
    _mongocrypt_buffer_copy_from_hex(&key_uuid, TEST_KEY_UUID_HEX);
    _mongocrypt_buffer_copy_from_hex(&key, TEST_KEY_HEX);

    bool res = mc_FLE2UnindexedEncryptedValueV2_encrypt(crypt->crypto,
                                                        &key_uuid,
                                                        BSON_TYPE_UTF8,
                                                        &plaintext,
                                                        &key,
                                                        &ciphertext,
                                                        status);
    ASSERT_OK_STATUS(res, status);

    // build the serialized UEV by combining the prefix and the ciphertext
    _mongocrypt_buffer_copy_from_hex(&prefix, TEST_PREFIX_HEX);
    _mongocrypt_buffer_t bufs[] = {prefix, ciphertext};
    ASSERT(_mongocrypt_buffer_concat(&serialized_uev, bufs, 2));

    // verify the serialized UEV decrypts to the same plaintext
    mc_FLE2UnindexedEncryptedValueV2_t *uev = mc_FLE2UnindexedEncryptedValueV2_new();

    ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValueV2_parse(uev, &serialized_uev, status), status);
    const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValueV2_decrypt(crypt->crypto, uev, &key, status);
    ASSERT_OK_STATUS(got != NULL, status);
    ASSERT_CMPBUF(plaintext, *got);
    mc_FLE2UnindexedEncryptedValueV2_destroy(uev);

    _mongocrypt_buffer_cleanup(&serialized_uev);
    _mongocrypt_buffer_cleanup(&prefix);
    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&key_uuid);
    _mongocrypt_buffer_cleanup(&plaintext);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_fle2_payload_uev_v2(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_FLE2UnindexedEncryptedValueV2_parse);
    INSTALL_TEST(test_FLE2UnindexedEncryptedValueV2_decrypt);
    INSTALL_TEST(test_FLE2UnindexedEncryptedValueV2_ctx_decrypt);
    INSTALL_TEST(test_FLE2UnindexedEncryptedValueV2_encrypt);
}
libmongocrypt-1.19.0/test/test-mc-fle2-payload-uev.c000066400000000000000000000271211521103432300222400ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-fle2-payload-uev-private.h"
#include "test-mongocrypt.h"

#define TEST_KEY_UUID_HEX "abcdefab123498761234123456789012"
#define TEST_KEY_HEX                                                                                                   \
    "a7ddbc4c8be00d51f68d9d8e485f351c8edc8d2206b24d8e0e1816d005fbe520e48912504"                                        \
    "7d647b0d8684bfbdbf09c304085ed086aba6c2b2b1677ccc91ced8847a733bf5e5682c84b"                                        \
    "3ee7969e4a5fe0e0c21e5e3ee190595a55f83147d8de2a"
#define TEST_PLAINTEXT "\x09\x00\x00\x00value123\x00"
#define TEST_PLAINTEXT_LEN 13

// prefix = (FLE_TYPE || KEY_UUID || BSON_TYPE)
#define TEST_PREFIX_HEX "06" TEST_KEY_UUID_HEX "02"
// ciphertext = (IV || S || HMAC)
#define TEST_CIPHERTEXT_HEX                                                                                            \
    "4d069564f5a05e9e3523b98f575acb153b70d6d5f38dc752132c6928aaae8e5928e537a2c"                                        \
    "e407d847434d3d755635f9f80888371e7e1f9e42b9b70a485"
// uev = (prefix || ciphertext)
#define TEST_UEV_HEX TEST_PREFIX_HEX TEST_CIPHERTEXT_HEX

static void test_FLE2UnindexedEncryptedValue_parse(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_key_uuid;
    mc_FLE2UnindexedEncryptedValue_t *uev;

    /* Test successful parse. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        _mongocrypt_buffer_copy_from_hex(&expect_key_uuid, TEST_KEY_UUID_HEX);
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status), status);
        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValue_get_key_uuid(uev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_key_uuid, *got);
        bson_type_t got_bson_type = mc_FLE2UnindexedEncryptedValue_get_original_bson_type(uev, status);
        ASSERT_OR_PRINT(got_bson_type == BSON_TYPE_UTF8, status);
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&expect_key_uuid);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test too-short input. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, "06123456781234");
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status),
                            status,
                            "expected byte length >= 17 got: 7");
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test incorrect fle_blob_subtype */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        input.data[0] = 5;
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status),
                            status,
                            "expected fle_blob_subtype=6 got: 5");
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test double parsing */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status), status);
        ASSERT_FAILS_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status),
                            status,
                            "must not be called twice");
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&input);
        mongocrypt_status_destroy(status);
    }

    /* Test attempting to get key_uuid or original_bson_type before parsing. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        uev = mc_FLE2UnindexedEncryptedValue_new();
        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValue_get_key_uuid(uev, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "must be called after mc_FLE2UnindexedEncryptedValue_parse");

        mongocrypt_status_destroy(status);
        status = mongocrypt_status_new();

        bson_type_t got_bson_type = mc_FLE2UnindexedEncryptedValue_get_original_bson_type(uev, status);
        ASSERT_FAILS_STATUS(got_bson_type != 0, status, "must be called after mc_FLE2UnindexedEncryptedValue_parse");

        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        mongocrypt_status_destroy(status);
    }
}

static void test_FLE2UnindexedEncryptedValue_decrypt(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t correct_key;
    mc_FLE2UnindexedEncryptedValue_t *uev;
    _mongocrypt_buffer_t expect_key_uuid;
    _mongocrypt_buffer_t expect_plaintext;
    mongocrypt_t *crypt;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_copy_from_hex(&input, TEST_UEV_HEX);
    _mongocrypt_buffer_copy_from_hex(&expect_key_uuid, TEST_KEY_UUID_HEX);
    _mongocrypt_buffer_copy_from_hex(&correct_key, TEST_KEY_HEX);
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&expect_plaintext,
                                                      (const uint8_t *)TEST_PLAINTEXT,
                                                      TEST_PLAINTEXT_LEN));

    /* Test success. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status), status);

        const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValue_get_key_uuid(uev, status);
        ASSERT_OR_PRINT(got != NULL, status);
        ASSERT_CMPBUF(expect_key_uuid, *got);

        got = mc_FLE2UnindexedEncryptedValue_decrypt(crypt->crypto, uev, &correct_key, status);
        ASSERT_OK_STATUS(got != NULL, status);
        ASSERT_CMPBUF(expect_plaintext, *got);
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        mongocrypt_status_destroy(status);
    }

    /* Test an incorrect key. */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t incorrect_key;

        _mongocrypt_buffer_init(&incorrect_key);
        _mongocrypt_buffer_copy_to(&correct_key, &incorrect_key);
        /* The middle 32 bytes of key are used to generate the mac. Change first
         * byte to make S_Key incorrect. */
        incorrect_key.data[32] = 0;
        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2UnindexedEncryptedValue_decrypt(crypt->crypto, uev, &incorrect_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "HMAC validation failure");
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&incorrect_key);
        mongocrypt_status_destroy(status);
    }

    /* Test empty ciphertext */
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        _mongocrypt_buffer_t short_input;

        _mongocrypt_buffer_copy_from_hex(&short_input, TEST_PREFIX_HEX);

        uev = mc_FLE2UnindexedEncryptedValue_new();
        ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &short_input, status), status);
        const _mongocrypt_buffer_t *got =
            mc_FLE2UnindexedEncryptedValue_decrypt(crypt->crypto, uev, &correct_key, status);
        ASSERT_FAILS_STATUS(got != NULL, status, "input ciphertext too small");
        mc_FLE2UnindexedEncryptedValue_destroy(uev);
        _mongocrypt_buffer_cleanup(&short_input);
        mongocrypt_status_destroy(status);
    }

    _mongocrypt_buffer_cleanup(&expect_plaintext);
    _mongocrypt_buffer_cleanup(&correct_key);
    _mongocrypt_buffer_cleanup(&expect_key_uuid);
    _mongocrypt_buffer_cleanup(&input);
    mongocrypt_destroy(crypt);
}

static void test_FLE2UnindexedEncryptedValue_encrypt(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_status_t *status = mongocrypt_status_new();

    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t ciphertext;
    _mongocrypt_buffer_t key_uuid;
    _mongocrypt_buffer_t key;
    _mongocrypt_buffer_t prefix;
    _mongocrypt_buffer_t serialized_uev;

    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&plaintext, (const uint8_t *)TEST_PLAINTEXT, TEST_PLAINTEXT_LEN));

    /* Test encrypt/decrypt round trip */
    _mongocrypt_buffer_init(&ciphertext);
    _mongocrypt_buffer_copy_from_hex(&key_uuid, TEST_KEY_UUID_HEX);
    _mongocrypt_buffer_copy_from_hex(&key, TEST_KEY_HEX);

    bool res = mc_FLE2UnindexedEncryptedValue_encrypt(crypt->crypto,
                                                      &key_uuid,
                                                      BSON_TYPE_UTF8,
                                                      &plaintext,
                                                      &key,
                                                      &ciphertext,
                                                      status);
    ASSERT_OK_STATUS(res, status);

    // build the serialized UEV by combining the prefix and the ciphertext
    _mongocrypt_buffer_copy_from_hex(&prefix, TEST_PREFIX_HEX);
    _mongocrypt_buffer_t bufs[] = {prefix, ciphertext};
    ASSERT(_mongocrypt_buffer_concat(&serialized_uev, bufs, 2));

    // verify the serialized UEV decrypts to the same plaintext
    mc_FLE2UnindexedEncryptedValue_t *uev = mc_FLE2UnindexedEncryptedValue_new();

    ASSERT_OK_STATUS(mc_FLE2UnindexedEncryptedValue_parse(uev, &serialized_uev, status), status);
    const _mongocrypt_buffer_t *got = mc_FLE2UnindexedEncryptedValue_decrypt(crypt->crypto, uev, &key, status);
    ASSERT_OK_STATUS(got != NULL, status);
    ASSERT_CMPBUF(plaintext, *got);
    mc_FLE2UnindexedEncryptedValue_destroy(uev);

    _mongocrypt_buffer_cleanup(&serialized_uev);
    _mongocrypt_buffer_cleanup(&prefix);
    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&key_uuid);
    _mongocrypt_buffer_cleanup(&plaintext);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_fle2_payload_uev(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_FLE2UnindexedEncryptedValue_parse);
    INSTALL_TEST(test_FLE2UnindexedEncryptedValue_decrypt);
    INSTALL_TEST(test_FLE2UnindexedEncryptedValue_encrypt);
}
libmongocrypt-1.19.0/test/test-mc-fle2-rfds.c000066400000000000000000000477011521103432300207560ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-fle-blob-subtype-private.h"
#include "mc-fle2-range-operator-private.h"
#include "mc-fle2-rfds-private.h"
#include "test-mongocrypt.h"
#include  // INFINITY

#define RAW_STRING(...) #__VA_ARGS__

static void test_mc_FLE2RangeFindDriverSpec_parse(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *expectError;

        struct {
            const char *field;

            struct {
                bool set;
                int32_t value;
                bool included;
            } lower;

            struct {
                bool set;
                int32_t value;
                bool included;
            } upper;

            bool isAggregateExpression;
        } expect;
    } testcase;

    testcase tests[] = {
        {.desc = "Aggregate Expression",
         .in = RAW_STRING({"$and" : [ {"$gt" : [ "$age", 5 ]}, {"$lt" : [ "$age", 50 ]} ]}),
         .expect = {.field = "$age",
                    .lower = {.set = true, .value = 5, .included = false},
                    .upper = {.set = true, .value = 50, .included = false},
                    .isAggregateExpression = true}},
        {.desc = "Aggregate Expression with inclusive bounds",
         .in = RAW_STRING({"$and" : [ {"$gte" : [ "$age", 5 ]}, {"$lte" : [ "$age", 50 ]} ]}),
         .expect = {.field = "$age",
                    .lower = {.set = true, .value = 5, .included = true},
                    .upper = {.set = true, .value = 50, .included = true},
                    .isAggregateExpression = true}},
        {.desc = "Aggregate Expression with one bound",
         .in = RAW_STRING({"$and" : [ {"$gte" : [ "$age", 5 ]} ]}),
         .expect = {.field = "$age",
                    .lower = {.set = true, .value = 5, .included = true},
                    .upper = {.set = false},
                    .isAggregateExpression = true}},
        {.desc = "Aggregate Expression with unsupported operator",
         .in = RAW_STRING({"$and" : [ {"$foo" : [ "$age", 5 ]}, {"$lte" : [ "$age", 50 ]} ]}),
         .expectError = "expected argument to be document"},
        {.desc = "Aggregate Expression with conflicting operators",
         .in = RAW_STRING({"$and" : [ {"$lt" : [ "$age", 5 ]}, {"$lte" : [ "$age", 50 ]} ]}),
         .expectError = "unexpected duplicate bound"},
        {.desc = "Aggregate Expression with more than two arguments",
         .in = RAW_STRING({"$and" : [ {"$lt" : [ "$age", 5 ]}, {"$gt" : [ "$age", 50 ]}, {"$gt" : [ "$age", 50 ]} ]}),
         .expectError = "unexpected duplicate bound"},
        {.desc = "Aggregate Expression referencing two fields",
         .in = RAW_STRING({"$and" : [ {"$lt" : [ "$age", 5 ]}, {"$gt" : [ "$foo", 50 ]} ]}),
         .expectError = "unexpected field mismatch"},
        {.desc = "Match Expression",
         .in = RAW_STRING({"$and" : [ {"age" : {"$gt" : 5}}, {"age" : {"$lt" : 50}} ]}),
         .expect = {.field = "age",
                    .lower = {.set = true, .value = 5, .included = false},
                    .upper = {.set = true, .value = 50, .included = false},
                    .isAggregateExpression = false}},
        {.desc = "Missing top $and", .in = RAW_STRING({"foo" : "bar"}), .expectError = "error unable to find '$and'"},
        {.desc = "Empty document", .in = RAW_STRING({}), .expectError = "error unable to find '$and'"},
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2RangeFindDriverSpec_t rfds;
        TEST_PRINTF("running subtest: %s\n", test->desc);
        bool ret = mc_FLE2RangeFindDriverSpec_parse(&rfds, TMP_BSON_STR(test->in), status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_STREQUAL(test->expect.field, rfds.field);
            ASSERT_CMPINT(test->expect.lower.set, ==, rfds.lower.set);
            if (test->expect.lower.set) {
                ASSERT_CMPINT32(test->expect.lower.value, ==, bson_iter_int32(&rfds.lower.value));
                ASSERT_CMPINT(test->expect.lower.included, ==, rfds.lower.included);
            }
            ASSERT_CMPINT(test->expect.upper.set, ==, rfds.upper.set);
            if (test->expect.upper.set) {
                ASSERT_CMPINT32(test->expect.upper.value, ==, bson_iter_int32(&rfds.upper.value));
                ASSERT_CMPINT(test->expect.upper.included, ==, rfds.upper.included);
            }
            ASSERT_CMPINT(test->expect.isAggregateExpression, ==, rfds.isAggregateExpression);
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        mongocrypt_status_destroy(status);
    }
}

typedef struct {
    bool isStub;
    bson_iter_t lowerBound;
    bool lbIncluded;
    bson_iter_t upperBound;
    bool ubIncluded;
    mc_FLE2RangeOperator_t firstOp;
    mc_FLE2RangeOperator_t secondOp;
} placeholder_args_t;

static void
addPlaceholders_recursive(bson_iter_t *iter, bson_t *out, _mongocrypt_buffer_t *p1, _mongocrypt_buffer_t *p2) {
    while (bson_iter_next(iter)) {
        const char *key = bson_iter_key(iter);

        if (BSON_ITER_HOLDS_ARRAY(iter)) {
            bson_t child;
            bson_iter_t iter_child;
            ASSERT(BSON_APPEND_ARRAY_UNSAFE_BEGIN(out, key, &child));
            ASSERT(bson_iter_recurse(iter, &iter_child));
            addPlaceholders_recursive(&iter_child, &child, p1, p2);
            ASSERT(bson_append_array_end(out, &child));
            continue;
        }
        if (BSON_ITER_HOLDS_DOCUMENT(iter)) {
            bson_t child;
            bson_iter_t iter_child;
            ASSERT(BSON_APPEND_DOCUMENT_BEGIN(out, key, &child));
            ASSERT(bson_iter_recurse(iter, &iter_child));
            addPlaceholders_recursive(&iter_child, &child, p1, p2);
            ASSERT(bson_append_document_end(out, &child));
            continue;
        }
        if (BSON_ITER_HOLDS_UTF8(iter)) {
            if (strcmp(bson_iter_utf8(iter, NULL), "") == 0) {
                ASSERT(p1->data);
                ASSERT(_mongocrypt_buffer_append(p1, out, key, -1));
                continue;
            }
            if (strcmp(bson_iter_utf8(iter, NULL), "") == 0) {
                ASSERT(p2->data);
                ASSERT(_mongocrypt_buffer_append(p2, out, key, -1));
                continue;
            }
            // Otherwise the value is not a placeholder. Fall through.
        }
        ASSERT(BSON_APPEND_VALUE(out, key, bson_iter_value(iter)));
    }
}

// addPlaceholders replaces values "" and "" in bson
// with p1 and p2.
static void addPlaceholders(bson_t **in, _mongocrypt_buffer_t *p1, _mongocrypt_buffer_t *p2) {
    bson_t out = BSON_INITIALIZER;
    bson_iter_t iter;
    ASSERT(bson_iter_init(&iter, *in));
    addPlaceholders_recursive(&iter, &out, p1, p2);
    bson_destroy(*in);
    bson_steal(*in, &out);
}

// Create a bson_iter_t to a temporary BSON int32 value v.
static bson_iter_t tmp_iter(_mongocrypt_tester_t *tester, int32_t v) {
    bson_t *b = TMP_BSON("{'v': %" PRId32 "}", v);
    bson_iter_t iter;
    ASSERT(bson_iter_init_find(&iter, b, "v"));
    return iter;
}

#define TMP_ITER(v) tmp_iter(tester, v)

static void test_mc_FLE2RangeFindDriverSpec_to_placeholders(_mongocrypt_tester_t *tester) {
    mc_FLE2RangeFindDriverSpec_t spec;
    mc_RangeOpts_t range_opts;
    mongocrypt_status_t *const status = mongocrypt_status_new();
    _mongocrypt_buffer_t user_key_id;
    _mongocrypt_buffer_t index_key_id;
    _mongocrypt_buffer_copy_from_hex(&user_key_id, "0123456789abcdefedcba98765432101");
    _mongocrypt_buffer_copy_from_hex(&index_key_id, "abcdefabcdabcdefedcba98765432101");
    user_key_id.subtype = BSON_SUBTYPE_UUID;
    index_key_id.subtype = BSON_SUBTYPE_UUID;

    int64_t maxContentionFactor = 4;
    int64_t sparsity = 1;
    int32_t indexMin = 5;
    int32_t indexMax = 200;
    int32_t payloadId = 123;

    typedef struct {
        const char *desc;
        const char *in;
        placeholder_args_t p1;
        placeholder_args_t p2;
        const char *expected;
    } testcase_t;

    bson_iter_t negInf, posInf;
    bson_t infDoc = BSON_INITIALIZER;
    // Create iterators to infinity
    {
        BCON_APPEND(&infDoc, "p", BCON_DOUBLE(INFINITY), "n", BCON_DOUBLE(-INFINITY));
        ASSERT(bson_iter_init_find(&posInf, &infDoc, "p"));
        ASSERT(bson_iter_init_find(&negInf, &infDoc, "n"));
    }

    testcase_t tests[] = {
        {.desc = "Match Expression with both inclusive bounds",
         .in = RAW_STRING({"$and" : [ {"age" : {"$gte" : 23}}, {"age" : {"$lte" : 35}} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kGte,
                .secondOp = FLE2RangeOperator_kLte},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kGte, .secondOp = FLE2RangeOperator_kLte},
         .expected =
             RAW_STRING({"$and" : [ {"age" : {"$gte" : ""}}, {"age" : {"$lte" : ""}} ]})},
        {.desc = "Match Expression with one inclusive bound",
         .in = RAW_STRING({"$and" : [ {"age" : {"$gte" : 23}} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = posInf,
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kGte},
         .expected = RAW_STRING({"$and" : [ {"age" : {"$gte" : ""}} ]})},
        {.desc = "Match Expression with both exclusive bounds",
         .in = RAW_STRING({"$and" : [ {"age" : {"$gt" : 23}}, {"age" : {"$lt" : 35}} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = false,
                .upperBound = TMP_ITER(35),
                .ubIncluded = false,
                .firstOp = FLE2RangeOperator_kGt,
                .secondOp = FLE2RangeOperator_kLt},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kGt, .secondOp = FLE2RangeOperator_kLt},
         .expected =
             RAW_STRING({"$and" : [ {"age" : {"$gt" : ""}}, {"age" : {"$lt" : ""}} ]})},
        {.desc = "Match Expression with one exclusive bound",
         .in = RAW_STRING({"$and" : [ {"age" : {"$lt" : 35}} ]}),
         .p1 = {.lowerBound = negInf,
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = false,
                .firstOp = FLE2RangeOperator_kLt},
         .expected = RAW_STRING({"$and" : [ {"age" : {"$lt" : ""}} ]})},
        {.desc = "Match Expression with flipped bounds",
         .in = RAW_STRING({"$and" : [ {"age" : {"$lte" : 35}}, {"age" : {"$gte" : 23}} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kLte,
                .secondOp = FLE2RangeOperator_kGte},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kLte, .secondOp = FLE2RangeOperator_kGte},
         .expected =
             RAW_STRING({"$and" : [ {"age" : {"$lte" : ""}}, {"age" : {"$gte" : ""}} ]})},
        {.desc = "Aggregate Expression with both inclusive bounds",
         .in = RAW_STRING({"$and" : [ {"$gte" : [ "$age", 23 ]}, {"$lte" : [ "$age", 35 ]} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kGte,
                .secondOp = FLE2RangeOperator_kLte},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kGte, .secondOp = FLE2RangeOperator_kLte},
         .expected = RAW_STRING(
             {"$and" : [ {"$gte" : [ "$age", "" ]}, {"$lte" : [ "$age", "" ]} ]})},
        {.desc = "Aggregate Expression with one inclusive bound",
         .in = RAW_STRING({"$and" : [ {"$gte" : [ "$age", 23 ]} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = posInf,
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kGte},
         .expected = RAW_STRING({"$and" : [ {"$gte" : [ "$age", "" ]} ]})},
        {.desc = "Aggregate Expression with both exclusive bounds",
         .in = RAW_STRING({"$and" : [ {"$gt" : [ "$age", 23 ]}, {"$lt" : [ "$age", 35 ]} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = false,
                .upperBound = TMP_ITER(35),
                .ubIncluded = false,
                .firstOp = FLE2RangeOperator_kGt,
                .secondOp = FLE2RangeOperator_kLt},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kGt, .secondOp = FLE2RangeOperator_kLt},
         .expected =
             RAW_STRING({"$and" : [ {"$gt" : [ "$age", "" ]}, {"$lt" : [ "$age", "" ]} ]})},
        {.desc = "Aggregate Expression with one exclusive bound",
         .in = RAW_STRING({"$and" : [ {"$lt" : [ "$age", 35 ]} ]}),
         .p1 = {.lowerBound = negInf,
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = false,
                .firstOp = FLE2RangeOperator_kLt},
         .expected = RAW_STRING({"$and" : [ {"$lt" : [ "$age", "" ]} ]})},
        {.desc = "Aggregate Expression with flipped bounds",
         .in = RAW_STRING({"$and" : [ {"$lte" : [ "$age", 35 ]}, {"$gte" : [ "$age", 23 ]} ]}),
         .p1 = {.lowerBound = TMP_ITER(23),
                .lbIncluded = true,
                .upperBound = TMP_ITER(35),
                .ubIncluded = true,
                .firstOp = FLE2RangeOperator_kLte,
                .secondOp = FLE2RangeOperator_kGte},
         .p2 = {.isStub = true, .firstOp = FLE2RangeOperator_kLte, .secondOp = FLE2RangeOperator_kGte},
         .expected = RAW_STRING(
             {"$and" : [ {"$lte" : [ "$age", "" ]}, {"$gte" : [ "$age", "" ]} ]})}

    };

    bson_t *range_opts_bson =
        TMP_BSON("{'min': %" PRId32 ", 'max': %" PRId32 ", 'sparsity': {'$numberLong': '%" PRId64 "'}}",
                 indexMin,
                 indexMax,
                 sparsity);

    ASSERT_OK_STATUS(mc_RangeOpts_parse(&range_opts, range_opts_bson, status), status);

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase_t *test = tests + i;
        TEST_PRINTF("running subtest: %s : %s\n", test->desc, test->in);
        ASSERT_OK_STATUS(mc_FLE2RangeFindDriverSpec_parse(&spec, TMP_BSON_STR(test->in), status), status);

        // Create the expected document.
        bson_t *expected;
        {
            expected = TMP_BSON_STR(test->expected);

            _mongocrypt_buffer_t p1 = {0}, p2 = {0};

            // Create placeholder arguments with default test values.
            if (test->p1.firstOp) {
                // Placeholder requires firstOp.
                mc_makeRangeFindPlaceholder_args_t p1_args_full = {.isStub = test->p1.isStub,
                                                                   .user_key_id = &user_key_id,
                                                                   .index_key_id = &index_key_id,
                                                                   .lowerBound = test->p1.lowerBound,
                                                                   .lbIncluded = test->p1.lbIncluded,
                                                                   .upperBound = test->p1.upperBound,
                                                                   .ubIncluded = test->p1.ubIncluded,
                                                                   .payloadId = payloadId,
                                                                   .firstOp = test->p1.firstOp,
                                                                   .secondOp = test->p1.secondOp,
                                                                   .indexMin = TMP_ITER(indexMin),
                                                                   .indexMax = TMP_ITER(indexMax),
                                                                   .maxContentionFactor = maxContentionFactor,
                                                                   .sparsity = sparsity};

                ASSERT_OK_STATUS(mc_makeRangeFindPlaceholder(&p1_args_full, &p1, status), status);
            }

            if (test->p2.firstOp) {
                mc_makeRangeFindPlaceholder_args_t p2_args_full = {.isStub = test->p2.isStub,
                                                                   .user_key_id = &user_key_id,
                                                                   .index_key_id = &index_key_id,
                                                                   .lowerBound = test->p2.lowerBound,
                                                                   .lbIncluded = test->p2.lbIncluded,
                                                                   .upperBound = test->p2.upperBound,
                                                                   .ubIncluded = test->p2.ubIncluded,
                                                                   .payloadId = payloadId,
                                                                   .firstOp = test->p2.firstOp,
                                                                   .secondOp = test->p2.secondOp,
                                                                   .indexMin = TMP_ITER(indexMin),
                                                                   .indexMax = TMP_ITER(indexMax),
                                                                   .maxContentionFactor = maxContentionFactor,
                                                                   .sparsity = sparsity};

                ASSERT_OK_STATUS(mc_makeRangeFindPlaceholder(&p2_args_full, &p2, status), status);
            }
            addPlaceholders(&expected, &p1, &p2);
            _mongocrypt_buffer_cleanup(&p2);
            _mongocrypt_buffer_cleanup(&p1);
        }

        bson_t out = BSON_INITIALIZER;
        bool ok = mc_FLE2RangeFindDriverSpec_to_placeholders(&spec,
                                                             &range_opts,
                                                             maxContentionFactor,
                                                             &user_key_id,
                                                             &index_key_id,
                                                             payloadId,
                                                             &out,
                                                             status);
        ASSERT_OK_STATUS(ok, status);
        ASSERT_EQUAL_BSON(expected, &out);
        bson_destroy(&out);
    }

    mc_RangeOpts_cleanup(&range_opts);
    _mongocrypt_buffer_cleanup(&index_key_id);
    _mongocrypt_buffer_cleanup(&user_key_id);
    bson_destroy(&infDoc);
    mongocrypt_status_destroy(status);
}

static void test_mc_getNextPayloadId(_mongocrypt_tester_t *tester) {
    int32_t first = mc_getNextPayloadId();
    int32_t second = mc_getNextPayloadId();
    ASSERT_CMPINT32(first + 1, ==, second);
}

void _mongocrypt_tester_install_mc_FLE2RangeFindDriverSpec(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mc_FLE2RangeFindDriverSpec_parse);
    INSTALL_TEST(test_mc_FLE2RangeFindDriverSpec_to_placeholders);
    INSTALL_TEST(test_mc_getNextPayloadId);
}
libmongocrypt-1.19.0/test/test-mc-fle2-tag-and-encrypted-metadata-block.c000066400000000000000000000103711521103432300261650ustar00rootroot00000000000000#include "mc-fle2-tag-and-encrypted-metadata-block-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define TEST_TAG_AND_ENCRYPTED_METADATA_BLOCK                                                                          \
    "34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b743d2267d98b888e9006c683039c1a1347baf18e50f02704b1" \
    "46bfc018cf41a55752815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13"

static void _test_mc_FLE2TagAndEncryptedMetadataBlock_roundtrip(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect_encryptedCount;
    _mongocrypt_buffer_t expect_tag;
    _mongocrypt_buffer_t expect_encryptedZeros;
    _mongocrypt_buffer_t output;
    mc_FLE2TagAndEncryptedMetadataBlock_t metadata;

    _mongocrypt_buffer_copy_from_hex(&expect_encryptedCount,
                                     "34bc1b32ce6404f3e39c97fdb86b077d70c86896fa329faf3029a14012fe4b74");
    _mongocrypt_buffer_copy_from_hex(&expect_tag, "3d2267d98b888e9006c683039c1a1347baf18e50f02704b146bfc018cf41a557");
    _mongocrypt_buffer_copy_from_hex(&expect_encryptedZeros,
                                     "52815f53d5795c66ad0ed2f2bde7f6471da0b7effca4fe213695f2a16ab9be13");

    _mongocrypt_buffer_copy_from_hex(&input, TEST_TAG_AND_ENCRYPTED_METADATA_BLOCK);

    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_FLE2TagAndEncryptedMetadataBlock_init(&metadata);

    // Parse into metadata struct
    ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_parse(&metadata, &input, status), status);

    // Check values
    ASSERT_CMPBUF(expect_encryptedCount, metadata.encryptedCount);
    ASSERT_CMPBUF(expect_tag, metadata.tag);
    ASSERT_CMPBUF(expect_encryptedZeros, metadata.encryptedZeros);

    // Serialize back into buffer
    _mongocrypt_buffer_init_size(&output, input.len);
    ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_serialize(&metadata, &output, status), status);

    // Check that unparsed input is the same as serialized output
    ASSERT_CMPBUF(input, output);

    mongocrypt_status_destroy(status);
    mc_FLE2TagAndEncryptedMetadataBlock_cleanup(&metadata);
    _mongocrypt_buffer_cleanup(&expect_encryptedCount);
    _mongocrypt_buffer_cleanup(&expect_tag);
    _mongocrypt_buffer_cleanup(&expect_encryptedZeros);
    _mongocrypt_buffer_cleanup(&input);
    _mongocrypt_buffer_cleanup(&output);
}

static void _test_mc_FLE2TagAndEncryptedMetadataBlock_validate(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_copy_from_hex(&input, TEST_TAG_AND_ENCRYPTED_METADATA_BLOCK);

    mongocrypt_status_t *status = mongocrypt_status_new();

    mc_FLE2TagAndEncryptedMetadataBlock_t metadata;
    mc_FLE2TagAndEncryptedMetadataBlock_init(&metadata);

    // Parse into metadata struct.
    ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_parse(&metadata, &input, status), status);

    ASSERT_OK_STATUS(mc_FLE2TagAndEncryptedMetadataBlock_validate(&metadata, status), status);

    // Modify each value on the metadata block to be invalid and assert failure, then change back to valid value.
    _mongocrypt_buffer_resize(&metadata.encryptedCount, kFieldLen - 1);
    ASSERT(!mc_FLE2TagAndEncryptedMetadataBlock_validate(&metadata, status));
    _mongocrypt_buffer_resize(&metadata.encryptedCount, kFieldLen);

    _mongocrypt_buffer_resize(&metadata.tag, kFieldLen - 1);
    ASSERT(!mc_FLE2TagAndEncryptedMetadataBlock_validate(&metadata, status));
    _mongocrypt_buffer_resize(&metadata.tag, kFieldLen);

    _mongocrypt_buffer_resize(&metadata.encryptedZeros, kFieldLen - 1);
    ASSERT(!mc_FLE2TagAndEncryptedMetadataBlock_validate(&metadata, status));
    _mongocrypt_buffer_resize(&metadata.encryptedZeros, kFieldLen);

    // Metadata block should be valid.
    ASSERT(mc_FLE2TagAndEncryptedMetadataBlock_validate(&metadata, status));
    _mongocrypt_buffer_cleanup(&input);
    mc_FLE2TagAndEncryptedMetadataBlock_cleanup(&metadata);
    mongocrypt_status_destroy(status);
}

#undef TEST_TAG_AND_ENCRYPTED_METADATA_BLOCK

void _mongocrypt_tester_install_fle2_tag_and_encrypted_metadata_block(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mc_FLE2TagAndEncryptedMetadataBlock_roundtrip);
    INSTALL_TEST(_test_mc_FLE2TagAndEncryptedMetadataBlock_validate);
}
libmongocrypt-1.19.0/test/test-mc-range-edge-generation.c000066400000000000000000000455661521103432300233300ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

#include "mc-optional-private.h"
#include "mc-range-edge-generation-private.h"

#include  // DBL_MIN

#define MAX_INT32_EDGES 33

typedef struct {
    int32_t value;
    mc_optional_int32_t min;
    mc_optional_int32_t max;
    size_t sparsity;
    // expectEdges includes a trailing NULL pointer.
    const char *expectEdges[MAX_INT32_EDGES + 1];
    const char *expectError;
    int32_t trimFactor;
} Int32Test;

#undef MAX_INT32_EDGES

static void print_edges_compared(mc_edges_t *edgesGot, const char *const *edgesExpected) {
    TEST_STDERR_PRINTF("edges got ... begin\n");
    for (size_t i = 0; i < mc_edges_len(edgesGot); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_edges_get(edgesGot, i));
    }
    TEST_STDERR_PRINTF("edges got ... end\n");

    TEST_STDERR_PRINTF("edges expected ... begin\n");
    const char *const *iter = edgesExpected;
    while (*iter != NULL) {
        TEST_STDERR_PRINTF("  %s\n", *iter);
        iter++;
    }
    TEST_STDERR_PRINTF("edges expected ... end\n");
}

static void _test_getEdgesInt32(_mongocrypt_tester_t *tester) {
    static const Int32Test tests[] = {
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectEdges = {"root", "010", "0", "01"}},
        {.value = 2, .min = OPT_I32_C(0), .max = OPT_I32_C(7), .sparsity = 2, .expectEdges = {"root", "010", "01"}},
        {.value = 1, .sparsity = 0, .expectError = "sparsity must be 1 or larger"},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 1,
         .expectEdges = {"010", "0", "01"}},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 2,
         .expectEdges = {"010", "01"}},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 3,
         .expectError =
             "trimFactor must be less than the number of bits (3) used to represent an element of the domain"},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = -1,
         .expectError = "trimFactor must be >= 0"},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 2,
         .trimFactor = 0,
         .expectEdges = {"root", "010", "01"}},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 2,
         .trimFactor = 1,
         .expectEdges = {"010", "01"}},
        {.value = 2,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 2,
         .trimFactor = 2,
         .expectEdges = {"010", "01"}},
#include "data/range-edge-generation/edges_int32.cstruct"
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        mongocrypt_status_t *const status = mongocrypt_status_new();
        const Int32Test *test = tests + i;
        mc_getEdgesInt32_args_t args = {.value = test->value,
                                        .min = test->min,
                                        .max = test->max,
                                        .sparsity = test->sparsity,
                                        .trimFactor = OPT_I32(test->trimFactor)};
        mc_edges_t *got = mc_getEdgesInt32(args, status);
        if (test->expectError != NULL) {
            ASSERT_OR_PRINT_MSG(NULL == got, "expected error, got success");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_edges_len(got);
        size_t numExpected = 0;
        while (test->expectEdges[numExpected] != NULL) {
            ++numExpected;
        }

        if (numExpected != numGot) {
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("got %zu edges, expected %zu edges\n", numGot, numExpected);
        }

        for (size_t gotI = 0; gotI < numGot; gotI++) {
            const char *edgeGot = mc_edges_get(got, gotI);
            const char *edgeExpected = test->expectEdges[gotI];
            if (0 == strcmp(edgeGot, edgeExpected)) {
                continue;
            }
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("edge mismatch at index %zu. %s != %s\n", gotI, edgeGot, edgeExpected);
        }
        mc_edges_destroy(got);
        mongocrypt_status_destroy(status);
    }
}

#define MAX_INT64_EDGES 65

typedef struct {
    int64_t value;
    mc_optional_int64_t min;
    mc_optional_int64_t max;
    size_t sparsity;
    // expectEdges includes a trailing NULL pointer.
    const char *expectEdges[MAX_INT64_EDGES + 1];
    const char *expectError;
    uint32_t trimFactor;
} Int64Test;

#undef MAX_INT64_EDGES

static void _test_getEdgesInt64(_mongocrypt_tester_t *tester) {
    static const Int64Test tests[] = {
        {.value = INT64_C(2),
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectEdges = {"root", "010", "0", "01"}},
        {.value = INT64_C(2),
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 2,
         .expectEdges = {"root", "010", "01"}},
        {.value = INT64_C(2),
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .trimFactor = 1,
         .expectEdges = {"010", "0", "01"}},
        {.value = INT64_C(2),
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .trimFactor = 2,
         .expectEdges = {"010", "01"}},
        {.value = INT64_C(2),
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .trimFactor = 3,
         .expectError =
             "trimFactor must be less than the number of bits (3) used to represent an element of the domain"},
        {.value = 1, .sparsity = 0, .expectError = "sparsity must be 1 or larger"},
#include "data/range-edge-generation/edges_int64.cstruct"
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        mongocrypt_status_t *const status = mongocrypt_status_new();
        const Int64Test *test = tests + i;
        mc_getEdgesInt64_args_t args = {.value = test->value,
                                        .min = test->min,
                                        .max = test->max,
                                        .sparsity = test->sparsity,
                                        .trimFactor = OPT_I32(test->trimFactor)};
        mc_edges_t *got = mc_getEdgesInt64(args, status);
        if (test->expectError != NULL) {
            ASSERT_OR_PRINT_MSG(NULL == got, "expected error, got success");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_edges_len(got);
        size_t numExpected = 0;
        while (test->expectEdges[numExpected] != NULL) {
            ++numExpected;
        }

        if (numExpected != numGot) {
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("got %zu edges, expected %zu edges\n", numGot, numExpected);
        }

        for (size_t gotI = 0; gotI < numGot; gotI++) {
            const char *edgeGot = mc_edges_get(got, gotI);
            const char *edgeExpected = test->expectEdges[gotI];
            if (0 == strcmp(edgeGot, edgeExpected)) {
                continue;
            }
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("edge mismatch at index %zu. %s != %s\n", gotI, edgeGot, edgeExpected);
        }
        mc_edges_destroy(got);
        mongocrypt_status_destroy(status);
    }
}

#define MAX_DOUBLE_EDGES 65

typedef struct {
    double value;
    mc_optional_double_t min; // Unused. Kept for consistency with server test data.
    mc_optional_double_t max; // Unused. Kept for consistency with server test data.
    size_t sparsity;
    // expectEdges includes a trailing NULL pointer.
    const char *expectEdges[MAX_DOUBLE_EDGES + 1];
    const char *expectError;
} DoubleTest;

#undef MAX_DOUBLE_EDGES

static void _test_getEdgesDouble(_mongocrypt_tester_t *tester) {
    static const DoubleTest tests[] = {
#include "data/range-edge-generation/edges_double.cstruct"
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        mongocrypt_status_t *const status = mongocrypt_status_new();
        const DoubleTest *test = tests + i;
        const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
        mc_getEdgesDouble_args_t args = {.value = test->value,
                                         .sparsity = test->sparsity,
                                         .trimFactor = OPT_I32(trimFactor)};
        mc_edges_t *got = mc_getEdgesDouble(args, status);

        if (test->expectError != NULL) {
            if (NULL != got) {
                TEST_ERROR("test %zu expected error, got success", i);
            }
            ASSERT_STATUS_CONTAINS(status, test->expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_edges_len(got);
        size_t numExpected = 0;
        while (test->expectEdges[numExpected] != NULL) {
            ++numExpected;
        }

        if (numExpected != numGot) {
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("test %zu got %zu edges, expected %zu edges\n", i, numGot, numExpected);
        }

        for (size_t gotI = 0; gotI < numGot; gotI++) {
            const char *edgeGot = mc_edges_get(got, gotI);
            const char *edgeExpected = test->expectEdges[gotI];
            if (0 == strcmp(edgeGot, edgeExpected)) {
                continue;
            }
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("test %zu got edge mismatch at index %zu. %s != %s\n", i, gotI, edgeGot, edgeExpected);
        }
        mc_edges_destroy(got);
        mongocrypt_status_destroy(status);
    }
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
#define MAX_DEC128_EDGES 129

typedef struct {
    mc_dec128 value;
    mc_optional_dec128_t min;
    mc_optional_dec128_t max;
    int sparsity;
    // expectEdges includes a trailing NULL pointer.
    const char *expectEdges[MAX_DEC128_EDGES + 1];
    const char *expectError;
} Decimal128Test;

#undef MAX_DEC128_EDGES

static void _test_getEdgesDecimal128(_mongocrypt_tester_t *tester) {
    Decimal128Test tests[] = {
#include "data/range-edge-generation/edges_decimal128.cstruct"
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        const Decimal128Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();
        const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
        mc_getEdgesDecimal128_args_t args = {.value = test->value,
                                             // Some edges specify min/max values, but we don't use them (yet)
                                             //  .min = test->min,
                                             //  .max = test->max,
                                             .sparsity = (size_t)test->sparsity,
                                             .trimFactor = OPT_I32(trimFactor)};
        mc_edges_t *got = mc_getEdgesDecimal128(args, status);

        if (test->expectError != NULL) {
            if (NULL != got) {
                TEST_ERROR("test %zu expected error, got success", i);
            }
            ASSERT_STATUS_CONTAINS(status, test->expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_edges_len(got);
        size_t numExpected = 0;
        while (test->expectEdges[numExpected] != NULL) {
            ++numExpected;
        }

        if (numExpected != numGot) {
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("test %zu got %zu edges, expected %zu edges\n", i, numGot, numExpected);
        }
        for (size_t gotI = 0; gotI < numGot; gotI++) {
            const char *edgeGot = mc_edges_get(got, gotI);
            const char *edgeExpected = test->expectEdges[gotI];
            if (0 == strcmp(edgeGot, edgeExpected)) {
                continue;
            }
            print_edges_compared(got, test->expectEdges);
            TEST_ERROR("test %zu got edge mismatch at index %zu. (actual) '%s' "
                       "!= '%s' (expected)\n",
                       i,
                       gotI,
                       edgeGot,
                       edgeExpected);
        }
        mc_edges_destroy(got);
        mongocrypt_status_destroy(status);
    }
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static void _test_count_leading_zeros(_mongocrypt_tester_t *tester) {
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u64(UINT64_C(0)), ==, 64);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u64(UINT64_C(1)), ==, 63);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u64(UINT64_MAX), ==, 0);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u64((~UINT64_C(0)) >> 1), ==, 1);

    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u32(UINT32_C(0)), ==, 32);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u32(UINT32_C(1)), ==, 31);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u32(UINT32_MAX), ==, 0);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u32((~UINT32_C(0)) >> 1), ==, 1);

    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u128(MLIB_INT128(0)), ==, 128);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u128(MLIB_INT128(8)), ==, 124);
    ASSERT_CMPSIZE_T(mc_count_leading_zeros_u128((mlib_int128)MLIB_INT128_FROM_PARTS(0, 8)), ==, 60);
}

typedef struct {
    uint32_t in;
    const char *expect;
} bitstring_u32_test;

typedef struct {
    uint64_t in;
    const char *expect;
} bitstring_u64_test;

typedef struct {
    mlib_int128 in;
    const char *expect;
} bitstring_u128_test;

static void _test_convert_to_bitstring(_mongocrypt_tester_t *tester) {
    // Test uint32_t.
    {
        bitstring_u32_test tests[] = {{.in = 0, .expect = "00000000000000000000000000000000"},
                                      {.in = 1, .expect = "00000000000000000000000000000001"},
                                      {.in = 123, .expect = "00000000000000000000000001111011"},
                                      {.in = UINT32_MAX, .expect = "11111111111111111111111111111111"},
                                      {.in = UINT32_MAX - 1u, .expect = "11111111111111111111111111111110"}};
        for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
            bitstring_u32_test *test = tests + i;
            mc_bitstring got = mc_convert_to_bitstring_u32(test->in);
            ASSERT_STREQUAL(test->expect, got.str);
        }
    }
    // Test uint64_t.
    {
        bitstring_u64_test tests[] = {{.in = 0,
                                       .expect = "0000000000000000000000000000000000000000000000000000000000"
                                                 "000000"},
                                      {.in = 1,
                                       .expect = "0000000000000000000000000000000000000000000000000000000000"
                                                 "000001"},
                                      {.in = 123,
                                       .expect = "0000000000000000000000000000000000000000000000000000000001"
                                                 "111011"},
                                      {.in = UINT64_MAX,
                                       .expect = "1111111111111111111111111111111111111111111111111111111111"
                                                 "111111"},
                                      {.in = UINT64_MAX - 1u,
                                       .expect = "1111111111111111111111111111111111111111111111111111111111"
                                                 "111110"}};
        for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
            bitstring_u64_test *test = tests + i;
            mc_bitstring got = mc_convert_to_bitstring_u64(test->in);
            ASSERT_STREQUAL(test->expect, got.str);
        }
    }
    // Tests for u128
    {
        bitstring_u128_test tests[] = {
            {
                .in = MLIB_INT128(0),
                .expect = "00000000000000000000000000000000000000000000000000000000"
                          "00000000000000000000000000000000000000000000000000000000"
                          "0000000000000000",
            },
            {
                .in = MLIB_INT128(1),
                .expect = "00000000000000000000000000000000000000000000000000000000"
                          "00000000000000000000000000000000000000000000000000000000"
                          "0000000000000001",
            },
            {
                .in = MLIB_INT128(256),
                .expect = "00000000000000000000000000000000000000000000000000000000"
                          "00000000000000000000000000000000000000000000000000000000"
                          "0000000100000000",
            },
            {
                .in = mlib_int128_from_string("0b1011010010001011101010010100101010010101010101001010010100100"
                                              "101010101010010001010011010110110100110010101010010101001111010"
                                              "1011",
                                              NULL),
                .expect = "10110100100010111010100101001010100101010101010010100101"
                          "00100101010101010010001010011010110110100110010101010010"
                          "1010011110101011",
            },
        };
        for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
            bitstring_u128_test *test = tests + i;
            mc_bitstring got = mc_convert_to_bitstring_u128(test->in);
            ASSERT_STREQUAL(test->expect, got.str);
        }
    }
}

void _mongocrypt_tester_install_range_edge_generation(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_getEdgesInt32);
    INSTALL_TEST(_test_getEdgesInt64);
    INSTALL_TEST(_test_getEdgesDouble);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_getEdgesDecimal128);
#endif
    INSTALL_TEST(_test_count_leading_zeros);
    INSTALL_TEST(_test_convert_to_bitstring);
}
libmongocrypt-1.19.0/test/test-mc-range-encoding.c000066400000000000000000001716101521103432300220470ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

#include "mc-range-encoding-private.h"

#include  // DBL_MAX
#include   // INFINITY, NAN
#include 

typedef struct {
    mc_getTypeInfo32_args_t args;
    mc_OSTType_Int32 expect;
    const char *expectError;
} Int32Test;

static void _test_RangeTest_Encode_Int32(_mongocrypt_tester_t *tester) {
    Int32Test tests[] = {
        /* Test cases copied from server Int32_NoBounds test ... begin */
        {.args = {.value = INT32_C(2147483647)},
         .expect = {.value = UINT32_C(4294967295), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = 1}, .expect = {.value = UINT32_C(2147483649), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = 0}, .expect = {.value = UINT32_C(2147483648), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = -1}, .expect = {.value = UINT32_C(2147483647), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = -2}, .expect = {.value = UINT32_C(2147483646), .min = 0, .max = UINT32_MAX}},
        {.args = {.value = INT32_C(-2147483647)}, .expect = {.value = 1, .min = 0, .max = UINT32_MAX}},
        {.args = {.value = INT32_MIN}, .expect = {.value = 0, .min = 0, .max = UINT32_MAX}},
        /* Test cases copied from server Int32_NoBounds test ... end */
        /* Test cases copied from server Int32_Bounds test .. begin */
        {.args = {.value = 1, .min = OPT_I32(1), .max = OPT_I32(3)}, .expect = {.value = 0, 0, .max = 2}},
        {.args = {.value = 0, .min = OPT_I32(0), .max = OPT_I32(1)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -1, .min = OPT_I32(-1), .max = OPT_I32(0)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -2, .min = OPT_I32(-2), .max = OPT_I32(0)}, .expect = {.value = 0, .min = 0, .max = 2}},
        {.args = {.value = INT32_C(-2147483647), .min = OPT_I32(INT32_MIN), .max = OPT_I32(1)},
         .expect = {.value = 1, .min = 0, .max = UINT32_C(2147483649)}},
        {.args = {.value = INT32_MIN, .min = OPT_I32(INT32_MIN), .max = OPT_I32(0)},
         .expect = {.value = 0, .min = 0, .max = UINT32_C(2147483648)}},
        {.args = {.value = 0, .min = OPT_I32(INT32_MIN), .max = OPT_I32(1)},
         .expect = {.value = UINT32_C(2147483648), .min = 0, .max = UINT32_C(2147483649)}},
        {.args = {.value = 1, .min = OPT_I32(INT32_MIN), .max = OPT_I32(2)},
         .expect = {.value = UINT32_C(2147483649), .min = 0, .max = UINT32_C(2147483650)}},
        {.args = {.value = INT32_C(2147483647), .min = OPT_I32(-2147483647), .max = OPT_I32(2147483647)},
         .expect = {.value = UINT32_C(4294967294), .min = 0, .max = UINT32_C(4294967294)}},
        {.args = {.value = INT32_C(2147483647), .min = OPT_I32(INT32_MIN), .max = OPT_I32(2147483647)},
         .expect = {.value = UINT32_C(4294967295), .min = 0, .max = UINT32_C(4294967295)}},
        {.args = {.value = 15, .min = OPT_I32(10), .max = OPT_I32(26)}, .expect = {.value = 5, .min = 0, .max = 16}},
        {.args = {.value = 15, .min = OPT_I32(-10), .max = OPT_I32(55)}, .expect = {.value = 25, .min = 0, .max = 65}},
        /* Test cases copied from server Int32_Bounds test ... end */
        /* Test cases copied from server Int32_Errors test ... begin */
        {.args = {.value = 1, .max = OPT_I32(2)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I32(0)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I32(2), .max = OPT_I32(1)},
         .expectError = "The minimum value must be less than the maximum value"},
        {.args = {.value = 1, .min = OPT_I32(2), .max = OPT_I32(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I32(2), .max = OPT_I32(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I32(INT32_MIN), .max = OPT_I32(INT32_MIN)},
         .expectError = "The minimum value must be less than the maximum value"},
        /* Test cases copied from server Int32_Errors test ... end */
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Int32Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        // Print a description of the test case.
        TEST_PRINTF("_test_RangeTest_Encode_Int32: value=%" PRId32, test->args.value);
        if (test->args.min.set) {
            TEST_PRINTF(" min=%" PRId32, test->args.min.value);
        }
        if (test->args.max.set) {
            TEST_PRINTF(" max=%" PRId32, test->args.max.value);
        }
        TEST_PRINTF("\n");
        mc_OSTType_Int32 got;
        const bool ok = mc_getTypeInfo32(test->args, &got, status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT32(got.value, ==, test->expect.value);
            ASSERT_CMPUINT32(got.min, ==, test->expect.min);
            ASSERT_CMPUINT32(got.max, ==, test->expect.max);
        }
        mongocrypt_status_destroy(status);
    }
}

typedef struct {
    mc_getTypeInfo64_args_t args;
    mc_OSTType_Int64 expect;
    const char *expectError;
} Int64Test;

static void _test_RangeTest_Encode_Int64(_mongocrypt_tester_t *tester) {
    Int64Test tests[] = {
        /* Test cases copied from server Int64_NoBounds test ... begin */
        {.args = {.value = INT64_C(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551615), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = 1}, .expect = {.value = UINT64_C(9223372036854775809), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = 0}, .expect = {.value = UINT64_C(9223372036854775808), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = -1}, .expect = {.value = UINT64_C(9223372036854775807), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = -2}, .expect = {.value = UINT64_C(9223372036854775806), .min = 0, .max = UINT64_MAX}},
        {.args = {.value = INT64_C(-9223372036854775807)}, .expect = {.value = 1, .min = 0, .max = UINT64_MAX}},
        {.args = {.value = INT64_MIN}, .expect = {.value = 0, .min = 0, .max = UINT64_MAX}},
        /* Test cases copied from server Int64_NoBounds test ... end */
        /* Test cases copied from server Int64_Bounds test ... begin */
        {.args = {.value = 1, .min = OPT_I64(1), .max = OPT_I64(2)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = 0, .min = OPT_I64(0), .max = OPT_I64(1)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -1, .min = OPT_I64(-1), .max = OPT_I64(0)}, .expect = {.value = 0, .min = 0, .max = 1}},
        {.args = {.value = -2, .min = OPT_I64(-2), .max = OPT_I64(0)}, .expect = {.value = 0, .min = 0, .max = 2}},
        {.args = {.value = INT64_C(-9223372036854775807), .min = OPT_I64(INT64_MIN), .max = OPT_I64(1)},
         .expect = {.value = 1, .min = 0, .max = UINT64_C(9223372036854775809)}},
        {.args = {.value = INT64_MIN, .min = OPT_I64(INT64_MIN), .max = OPT_I64(0)},
         .expect = {.value = 0, .min = 0, .max = UINT64_C(9223372036854775808)}},
        {.args = {.value = 0, .min = OPT_I64(INT64_MIN), .max = OPT_I64(37)},
         .expect = {.value = UINT64_C(9223372036854775808), .min = 0, .max = UINT64_C(9223372036854775845)}},
        {.args = {.value = 1, .min = OPT_I64(INT64_MIN), .max = OPT_I64(42)},
         .expect = {.value = UINT64_C(9223372036854775809), .min = 0, .max = UINT64_C(9223372036854775850)}},
        {.args = {.value = INT64_C(9223372036854775807),
                  .min = OPT_I64(-9223372036854775807),
                  .max = OPT_I64(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551614), .min = 0, .max = UINT64_C(18446744073709551614)}},
        {.args = {.value = INT64_C(9223372036854775807),
                  .min = OPT_I64(INT64_MIN),
                  .max = OPT_I64(9223372036854775807)},
         .expect = {.value = UINT64_C(18446744073709551615), .min = 0, .max = UINT64_C(18446744073709551615)}},
        {.args = {.value = 15, .min = OPT_I64(10), .max = OPT_I64(26)}, .expect = {.value = 5, .min = 0, .max = 16}},
        {.args = {.value = 15, .min = OPT_I64(-10), .max = OPT_I64(55)}, .expect = {.value = 25, .min = 0, .max = 65}},
        /* Test cases copied from server Int64_Bounds test ... end */
        /* Test cases copied from server Int64_Errors test ... begin */
        {.args = {.value = 1, .max = OPT_I64(2)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I64(0)},
         .expectError = "Must specify both a lower and upper bound or no bounds."},
        {.args = {.value = 1, .min = OPT_I64(2), .max = OPT_I64(1)},
         .expectError = "The minimum value must be less than the maximum value"},
        {.args = {.value = 1, .min = OPT_I64(2), .max = OPT_I64(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I64(2), .max = OPT_I64(3)},
         .expectError = "Value must be greater than or equal to the minimum "
                        "value and less than or equal to the maximum value"},
        {.args = {.value = 4, .min = OPT_I64(INT64_MIN), .max = OPT_I64(INT64_MIN)},
         .expectError = "The minimum value must be less than the maximum value"},
        /* Test cases copied from server Int64_Errors test ... end */
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Int64Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        // Print a description of the test case.
        TEST_PRINTF("_test_RangeTest_Encode_Int64: value=%" PRId64, test->args.value);
        if (test->args.min.set) {
            TEST_PRINTF(" min=%" PRId64, test->args.min.value);
        }
        if (test->args.max.set) {
            TEST_PRINTF(" max=%" PRId64, test->args.max.value);
        }
        TEST_PRINTF("\n");
        mc_OSTType_Int64 got;
        const bool ok = mc_getTypeInfo64(test->args, &got, status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT64(got.value, ==, test->expect.value);
            ASSERT_CMPUINT64(got.min, ==, test->expect.min);
            ASSERT_CMPUINT64(got.max, ==, test->expect.max);
        }
        mongocrypt_status_destroy(status);
    }
}

#define INT_64_MAX_DOUBLE (double)18446744073709551615ull

static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) {
#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out)                                               \
    if (1) {                                                                                                           \
        uint32_t bits_out = 0;                                                                                         \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal, min: %f, max: %f, prc: %" PRId32, lb, ub, prc);                 \
        bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status);                                    \
        ASSERT_OK_STATUS(mongocrypt_status_ok(status), status);                                                        \
        ASSERT(result == expected);                                                                                    \
        ASSERT_CMPINT32(expected_bits_out, ==, bits_out);                                                              \
        mongocrypt_status_destroy(status);                                                                             \
    } else                                                                                                             \
        ((void)0)

#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error)                                                              \
    if (1) {                                                                                                           \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal errors, min: %f, max: %f, prc: %" PRId32, lb, ub, prc);          \
        uint32_t bits_out = 0;                                                                                         \
        bool result = mc_canUsePrecisionModeDouble(lb, ub, prc, &bits_out, status);                                    \
        ASSERT_OR_PRINT_MSG(!result, "expected error, but got none");                                                  \
        ASSERT_STATUS_CONTAINS(status, error);                                                                         \
        mongocrypt_status_destroy(status);                                                                             \
    } else                                                                                                             \
        ((void)0)

    CAN_USE_PRECISION_MODE(1.0, 16.0, INT32_C(0), true, 4);
    CAN_USE_PRECISION_MODE(0.0, 16.0, INT32_C(0), true, 5);
    // 2^53 + 1 is where double starts to lose precision, so we need to ensure that we get the
    // correct value for max_bits out.
    CAN_USE_PRECISION_MODE_ERRORS(1.0,
                                  9007199254740992.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");
    CAN_USE_PRECISION_MODE_ERRORS(0.0,
                                  9007199254740992.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");

    CAN_USE_PRECISION_MODE(2.718281, 314.159265, 6, true, 29);

    CAN_USE_PRECISION_MODE_ERRORS(-1000000000.0,
                                  9223372036844775424.0,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute");

    CAN_USE_PRECISION_MODE_ERRORS(2.710000,
                                  314.150000,
                                  INT32_C(2),
                                  "Invalid upper bound for double precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bound for double precision. Fractional digits");

    CAN_USE_PRECISION_MODE_ERRORS((double)9007199254740992,
                                  INT_64_MAX_DOUBLE,
                                  INT32_C(0),
                                  "Invalid upper bound for double precision. Absolute scaled value");
    CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE,
                                  1.0,
                                  INT32_C(0),
                                  "Invalid lower bound for double precision. Absolute scaled value");
    CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0,
                                  92233720368547.0,
                                  INT32_C(5),
                                  "Invalid upper bound for double precision. Absolute");

#undef CAN_USE_PRECISION_MODE
#undef CAN_USE_PRECISION_MODE_ERRORS
}

typedef struct {
    double value;
    mc_optional_double_t min;
    mc_optional_double_t max;
    mc_optional_int32_t precision;
    uint64_t expect;
    mc_optional_uint64_t expectMax;
    const char *expectError;
    bool use_range_v1; // By default, use range v2.
} DoubleTest;

// Smallest and Largest integer that fits in a double before precision is lost
#define DOUBLE_MIN_SAFE_INT -9007199254740992 // -2^53
#define DOUBLE_MAX_SAFE_INT 9007199254740992  // 2^53

static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) {
    DoubleTest tests[] = {/* Test cases copied from server Double_Bounds test ... begin */
                          // Larger numbers map to larger uint64
                          {.value = -1111, .expect = UINT64_C(4570770991734587392)},
                          {.value = -111, .expect = UINT64_C(4585860689314185216)},
                          {.value = -11, .expect = UINT64_C(4600989969312382976)},
                          {.value = -10, .expect = UINT64_C(4601552919265804288)},
                          {.value = -3, .expect = UINT64_C(4609434218613702656)},
                          {.value = -2, .expect = UINT64_C(4611686018427387904)},

                          {.value = -1, .expect = UINT64_C(4616189618054758400)},
                          {.value = 1, .expect = UINT64_C(13830554455654793216)},
                          {.value = 22, .expect = UINT64_C(13850257704024539136)},
                          {.value = 333, .expect = UINT64_C(13867937850999177216)},

                          // Larger exponents map to larger uint64
                          {.value = 33E56, .expect = UINT64_C(14690973652625833878)},
                          {.value = 22E57, .expect = UINT64_C(14703137697061005818)},
                          {.value = 11E58, .expect = UINT64_C(14713688953586463292)},

                          // Smaller exponents map to smaller uint64
                          {.value = 1E-6, .expect = UINT64_C(13740701229962882445)},
                          {.value = 1E-7, .expect = UINT64_C(13725520251343122248)},
                          {.value = 1E-8, .expect = UINT64_C(13710498295186492474)},
                          {.value = 1E-56, .expect = UINT64_C(12992711961033031890)},
                          {.value = 1E-57, .expect = UINT64_C(12977434315086142017)},
                          {.value = 1E-58, .expect = UINT64_C(12962510038552207822)},

                          // Smaller negative exponents map to smaller uint64
                          {.value = -1E-06, .expect = UINT64_C(4706042843746669171)},
                          {.value = -1E-07, .expect = UINT64_C(4721223822366429368)},
                          {.value = -1E-08, .expect = UINT64_C(4736245778523059142)},
                          {.value = -1E-56, .expect = UINT64_C(5454032112676519726)},
                          {.value = -1E-57, .expect = UINT64_C(5469309758623409599)},
                          {.value = -1E-58, .expect = UINT64_C(5484234035157343794)},

                          // Larger exponents map to larger uint64
                          {.value = -33E+56, .expect = UINT64_C(3755770421083717738)},
                          {.value = -22E+57, .expect = UINT64_C(3743606376648545798)},
                          {.value = -11E+58, .expect = UINT64_C(3733055120123088324)},

                          {.value = 0, .expect = UINT64_C(9223372036854775808)},
                          {.value = -0.0, .expect = UINT64_C(9223372036854775808)},
                          /* Test cases copied from server Double_Bounds test ... end */
                          /* Test cases copied from server Double_Errors test ... begin */
                          {.value = INFINITY, .expectError = "Infinity and NaN double values are not supported."},
                          {.value = NAN, .expectError = "Infinity and NaN double values are not supported."},
                          /* Test cases copied from server Double_Errors test ... end */

                          /* Test cases copied from Double_Bounds_Precision ... begin */
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(1),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = UINT64_C(1000031),
                           .expectMax = OPT_U64_C(2097151)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(2),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 10000314,
                           .expectMax = OPT_U64_C(33554431)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(3),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 100003141,
                           .expectMax = OPT_U64_C(268435455)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(4),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 1000031415,
                           .expectMax = OPT_U64_C(2147483647)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(5),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 10000314159,
                           .expectMax = OPT_U64_C(34359738367)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(6),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 100003141592,
                           .expectMax = OPT_U64_C(274877906943)},
                          {.value = 3.141592653589,
                           .precision = OPT_I32_C(7),
                           .min = OPT_DOUBLE_C(-100000),
                           .max = OPT_DOUBLE_C(100000),
                           .expect = 1000031415926,
                           .expectMax = OPT_U64_C(2199023255551)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1),
                           .precision = OPT_I32_C(3),
                           .expect = 1000,
                           .expectMax = OPT_U64_C(4095)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1E5),
                           .precision = OPT_I32_C(3),
                           .expect = 100000000,
                           .expectMax = OPT_U64_C(134217727)},
                          {.value = -1E-33,
                           .max = OPT_DOUBLE_C(1),
                           .min = OPT_DOUBLE_C(-1E5),
                           .precision = OPT_I32_C(3),
                           .expect = 100000000,
                           .expectMax = OPT_U64_C(134217727)},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 0,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(0),
                           .expect = 3,
                           .expectMax = OPT_U64_C(7)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(1),
                           .expect = 31,
                           .expectMax = OPT_U64_C(63)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(2),
                           .expect = 314,
                           .expectMax = OPT_U64_C(1023)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(3),
                           .expect = 3141,
                           .expectMax = OPT_U64_C(8191)},
                          {.value = 3.141592653589,
                           .max = OPT_DOUBLE_C(5),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(16),
                           .expectError = "Invalid upper bound for double precision"},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(-1),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(3),
                           .expect = 5000,
                           .expectMax = OPT_U64_C(16383)},
                          {.value = 1E100,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E100,
                           .max = OPT_DOUBLE_C(DBL_MAX),
                           .min = OPT_DOUBLE_C(-DBL_MAX),
                           .precision = OPT_I32_C(3),
                           // Applying min/max/precision result in a domain needing >= 64 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E9,
                           .max = OPT_DOUBLE_C(1E10),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(3),
                           .expect = 1000000000000,
                           .expectMax = OPT_U64_C(17592186044415)},
                          {.value = 1E9,
                           .max = OPT_DOUBLE_C(1E10),
                           .min = OPT_DOUBLE_C(0),
                           .precision = OPT_I32_C(0),
                           .expect = 1000000000,
                           .expectMax = OPT_U64_C(17179869183)},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(10),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(0),
                           .expect = 5,
                           .expectMax = OPT_U64_C(31)},
                          {.value = -5,
                           .max = OPT_DOUBLE_C(10),
                           .min = OPT_DOUBLE_C(-10),
                           .precision = OPT_I32_C(2),
                           .expect = 500,
                           .expectMax = OPT_U64_C(4095)},
                          {.value = 1E-30,
                           .max = OPT_DOUBLE_C(10E-30),
                           .min = OPT_DOUBLE_C(1E-30),
                           .precision = OPT_I32_C(35),
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          {.value = 1E-30,
                           .max = OPT_DOUBLE_C(10E-30),
                           .min = OPT_DOUBLE_C(1E-30),
                           .precision = OPT_I32_C(35),
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision."},
                          /* Test max and min integer bounds for doubles */
                          {.value = DOUBLE_MIN_SAFE_INT,
                           .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT),
                           .min = OPT_DOUBLE_C(DOUBLE_MIN_SAFE_INT),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "Invalid upper bound for double precision. Absolute"},
                          {.value = 900719925474099.6,
                           .max = OPT_DOUBLE_C(900719925474100.0),
                           .min = OPT_DOUBLE_C(900719925474099.0),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           .expectMax = OPT_U64_C(1)},
                          // Domain size is small but min/max * 10^precision loses precision.
                          {.value = 900719925474099.6,
                           .max = OPT_DOUBLE_C(900719925474100.0),
                           .min = OPT_DOUBLE_C(900719925474099.0),
                           .precision = OPT_I32_C(1),
                           .expectError = "Invalid upper bound for double precision. Absolute"},
                          {.value = -900719925474099.6,
                           .max = OPT_DOUBLE_C(-900719925474099.0),
                           .min = OPT_DOUBLE_C(-900719925474100.0),
                           .precision = OPT_I32_C(1),
                           .expectError = "Invalid lower bound for double precision. Absolute"},
                          // 2^52
                          // The expected values increase by 4503599627370496 * 2^(i-52) + j
                          // i.e. the gaps between integers as the exponent increases since doubles lose precision as
                          // the exponent increases
                          {.value = 0,
                           .max = OPT_DOUBLE_C(4503599627370496),
                           .min = OPT_DOUBLE_C(-4503599627370496),
                           .precision = OPT_I32_C(0),
                           .expect = 0,
                           // Applying min/max/precision result in a domain needing >= 53 bits to represent.
                           // For range v2, expect an error.
                           .expectError = "The domain of double values specified by the min"},
                          /* Test cases copied from Double_Bounds_Precision ... end */
                          {.value = -1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(200),
                           .precision = OPT_I32_C(1),
                           .expectError = "greater than or equal to the minimum value"},
                          {.value = -1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(201),
                           .precision = OPT_I32_C(1),
                           .expectError = "less than or equal to the maximum value"},
                          {// Expect error due to precision exceeding INT32_MAX.
                           .value = 1,
                           .min = OPT_DOUBLE_C(1),
                           .max = OPT_DOUBLE_C(2),
                           .precision = OPT_I32_C(-1),
                           .expectError = "Precision must be non-negative"},
                          {// Expect error due to precision exceeding max finite double.
                           // The largest double value is 1.7976931348623157x10^308. 10^309 results in infinity.
                           .value = 1,
                           .min = OPT_DOUBLE_C(0),
                           .max = OPT_DOUBLE_C(1),
                           .precision = OPT_I32_C(309),
                           .expectError = "Precision is too large"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        DoubleTest *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        if (test->min.set && test->max.set && test->precision.set) {
            TEST_PRINTF("_test_RangeTest_Encode_Double: value=%f, min=%f, max=%f, "
                        "precision=%" PRId32 "\n",
                        test->value,
                        test->min.value,
                        test->max.value,
                        test->precision.value);
        } else {
            TEST_PRINTF("_test_RangeTest_Encode_Double: value=%f\n", test->value);
        }

        mc_OSTType_Double got;
        const bool ok = mc_getTypeInfoDouble((mc_getTypeInfoDouble_args_t){.value = test->value,
                                                                           .min = test->min,
                                                                           .max = test->max,
                                                                           .precision = test->precision},
                                             &got,
                                             status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);
            ASSERT_CMPUINT64(got.value, ==, test->expect);
            ASSERT_CMPUINT64(got.min, ==, 0);
            ASSERT_CMPUINT64(got.max, ==, test->expectMax.set ? test->expectMax.value : UINT64_MAX);
        }
        mongocrypt_status_destroy(status);
    }
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
typedef struct {
    mc_dec128 value;
    mc_optional_dec128_t min;
    mc_optional_dec128_t max;
    mc_optional_int32_t precision;
    mlib_int128 expect;
    const char *expectError;
    bool use_range_v1; // By default, use range v2.
} Decimal128Test;

static void _test_canUsePrecisionModeDecimal(_mongocrypt_tester_t *tester) {
#define CAN_USE_PRECISION_MODE(lb, ub, prc, expected, expected_bits_out)                                               \
    {                                                                                                                  \
        uint32_t bits_out = 0;                                                                                         \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal, min: %s, max: %s, prc: %" PRIu32,                               \
                    mc_dec128_to_string(lb).str,                                                                       \
                    mc_dec128_to_string(ub).str,                                                                       \
                    prc);                                                                                              \
        bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status);                                   \
        ASSERT_OK_STATUS(mongocrypt_status_ok(status), status);                                                        \
        ASSERT(result == expected);                                                                                    \
        ASSERT_CMPINT32(expected_bits_out, ==, bits_out);                                                              \
        mongocrypt_status_destroy(status);                                                                             \
    }

#define CAN_USE_PRECISION_MODE_ERRORS(lb, ub, prc, error)                                                              \
    {                                                                                                                  \
        mongocrypt_status_t *const status = mongocrypt_status_new();                                                   \
        TEST_PRINTF("_test_canUsePrecisionModeDecimal errors, min: %s, max: %s, prc: %" PRIu32,                        \
                    mc_dec128_to_string(lb).str,                                                                       \
                    mc_dec128_to_string(ub).str,                                                                       \
                    prc);                                                                                              \
        uint32_t bits_out = 0;                                                                                         \
        bool result = mc_canUsePrecisionModeDecimal(lb, ub, prc, &bits_out, status);                                   \
        ASSERT_OR_PRINT_MSG(!result, "expected error, but got none");                                                  \
        ASSERT_STATUS_CONTAINS(status, error);                                                                         \
        mongocrypt_status_destroy(status);                                                                             \
    }

    CAN_USE_PRECISION_MODE(MC_DEC128(1), MC_DEC128(16), 0, true, 4);
    CAN_USE_PRECISION_MODE(MC_DEC128(0), MC_DEC128(16), 0, true, 5);

    // It is unclear where Decimal128 looses precision, so we choose an arbitrarily large value
    // and make sure that max_bits is correct for that boundary.
    CAN_USE_PRECISION_MODE(MC_DEC128(1), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 108);
    CAN_USE_PRECISION_MODE(MC_DEC128(0), mc_dec128_from_string("324518553658426726783156020576256"), 0, true, 109);

    CAN_USE_PRECISION_MODE(mc_dec128_from_string("-100000000000000000000000000000000"),
                           mc_dec128_from_string("170141183460469231731687303715880000000"),
                           0,
                           false,
                           128);

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"),
                                  mc_dec128_from_string("4607431769000000.129834923"),
                                  4,
                                  "Invalid upper bound for Decimal128 precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("788545.12392843"),
                                  mc_dec128_from_string("7885451.2"),
                                  4,
                                  "Invalid lower bound for Decimal128 precision. Fractional digits");
    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("324518553658426726783156020576256"),
                                  mc_dec128_from_string("340282366920938463463374607431768211455"),
                                  10,
                                  "Invalid upper bound for Decimal128 precision. Absolute scaled");

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-340282366920938463463374607431768211455"),
                                  mc_dec128_from_string("-3245185536584267267831560"),
                                  10,
                                  "Invalid lower bound for Decimal128 precision. Absolute scaled");

    CAN_USE_PRECISION_MODE_ERRORS(mc_dec128_from_string("-17014118346046923173168730371588.0000000"),
                                  mc_dec128_from_string("17014118346046923173168730371588.0000000"),
                                  7,
                                  "Invalid value for precision");

    CAN_USE_PRECISION_MODE_ERRORS(MC_DEC128(788545.000000),
                                  mc_dec128_from_string("340282366920938463463374607431769000000.000000"),
                                  0,
                                  "Invalid upper bound for Decimal128 precision. Absolute scaled");

#undef CAN_USE_PRECISION_MODE
#undef CAN_USE_PRECISION_MODE_ERRORS
}

static void _test_RangeTest_Encode_Decimal128(_mongocrypt_tester_t *tester) {
    Decimal128Test tests[] = {
#define CASE(Value, ExpectStr)                                                                                         \
    (Decimal128Test){.value = mc_dec128_from_string(#Value), .expect = mlib_int128_from_string(ExpectStr, NULL)}
        /* Test cases copied from server Decimal128_Bounds test ... begin */
        // Larger numbers map to larger int128
        CASE(-1234567890E7, "108549948892579231731687303715884111887"),
        CASE(-1234567890E6, "108559948892579231731687303715884111886"),
        CASE(-1234567890E5, "108569948892579231731687303715884111885"),
        CASE(-1234567890E4, "108579948892579231731687303715884111884"),
        CASE(-1234567890E3, "108589948892579231731687303715884111883"),
        CASE(-1234567890E2, "108599948892579231731687303715884111882"),
        CASE(-1234567890E1, "108609948892579231731687303715884111881"),
        CASE(-123456789012345, "108569948892579108281687303715884111885"),
        CASE(-12345678901234, "108579948892579108331687303715884111884"),
        CASE(-1234567890123, "108589948892579108731687303715884111883"),
        CASE(-123456789012, "108599948892579111731687303715884111882"),
        CASE(-12345678901, "108609948892579131731687303715884111881"),
        CASE(-1234567890, "108619948892579231731687303715884111880"),
        CASE(-99999999, "108631183460569231731687303715884111878"),
        CASE(-8888888, "108642294572469231731687303715884111877"),
        CASE(-777777, "108653405690469231731687303715884111876"),
        CASE(-66666, "108664516860469231731687303715884111875"),
        CASE(-5555, "108675628460469231731687303715884111874"),
        CASE(-444, "108686743460469231731687303715884111873"),
        CASE(-334, "108687843460469231731687303715884111873"),
        CASE(-333, "108687853460469231731687303715884111873"),
        CASE(-44, "108696783460469231731687303715884111872"),
        CASE(-33, "108697883460469231731687303715884111872"),
        CASE(-22, "108698983460469231731687303715884111872"),
        CASE(-5, "108706183460469231731687303715884111871"),
        CASE(-4, "108707183460469231731687303715884111871"),
        CASE(-3, "108708183460469231731687303715884111871"),
        CASE(-2, "108709183460469231731687303715884111871"),
        CASE(-1, "108710183460469231731687303715884111871"),
        CASE(0, "170141183460469231731687303715884105728"),
        CASE(1, "231572183460469231731687303715884099585"),
        CASE(2, "231573183460469231731687303715884099585"),
        CASE(3, "231574183460469231731687303715884099585"),
        CASE(4, "231575183460469231731687303715884099585"),
        CASE(5, "231576183460469231731687303715884099585"),
        CASE(22, "231583383460469231731687303715884099584"),
        CASE(33, "231584483460469231731687303715884099584"),
        CASE(44, "231585583460469231731687303715884099584"),
        CASE(333, "231594513460469231731687303715884099583"),
        CASE(334, "231594523460469231731687303715884099583"),
        CASE(444, "231595623460469231731687303715884099583"),
        CASE(5555, "231606738460469231731687303715884099582"),
        CASE(66666, "231617850060469231731687303715884099581"),
        CASE(777777, "231628961230469231731687303715884099580"),
        CASE(8888888, "231640072348469231731687303715884099579"),
        CASE(33E56, "232144483460469231731687303715884099528"),
        CASE(22E57, "232153383460469231731687303715884099527"),
        CASE(11E58, "232162283460469231731687303715884099526"),

        // Smaller exponents map to smaller int128
        CASE(1E-6, "231512183460469231731687303715884099591"),
        CASE(1E-7, "231502183460469231731687303715884099592"),
        CASE(1E-8, "231492183460469231731687303715884099593"),
        CASE(1E-56, "231012183460469231731687303715884099641"),
        CASE(1E-57, "231002183460469231731687303715884099642"),
        CASE(1E-58, "230992183460469231731687303715884099643"),

        // Smaller negative exponents map to smaller int128
        CASE(-1E-6, "108770183460469231731687303715884111865"),
        CASE(-1E-7, "108780183460469231731687303715884111864"),
        CASE(-1E-8, "108790183460469231731687303715884111863"),
        CASE(-1E-56, "109270183460469231731687303715884111815"),
        CASE(-1E-57, "109280183460469231731687303715884111814"),
        CASE(-1E-58, "109290183460469231731687303715884111813"),

        // Larger exponents map to larger int128
        CASE(-33E56, "108137883460469231731687303715884111928"),
        CASE(-22E57, "108128983460469231731687303715884111929"),
        CASE(-11E58, "108120083460469231731687303715884111930"),

        (Decimal128Test){.value = MC_DEC128_LARGEST_POSITIVE,
                         .expect = mlib_int128_from_string("293021183460469231731687303715884093440", NULL)},
        (Decimal128Test){.value = MC_DEC128_SMALLEST_POSITIVE,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105729", NULL)},
        (Decimal128Test){.value = MC_DEC128_LARGEST_NEGATIVE,
                         .expect = mlib_int128_from_string("47261183460469231731687303715884118016", NULL)},
        (Decimal128Test){.value = MC_DEC128_SMALLEST_NEGATIVE,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105727", NULL)},
        (Decimal128Test){.value = MC_DEC128_NORMALIZED_ZERO,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105728", NULL)},
        (Decimal128Test){.value = MC_DEC128_NEGATIVE_EXPONENT_ZERO,
                         .expect = mlib_int128_from_string("170141183460469231731687303715884105728", NULL)},
    /* Test cases copied from server Decimal128_Bounds test ... end */

#define ERROR_CASE(Value, Min, Max, Precision, ErrorString)                                                            \
    (Decimal128Test){                                                                                                  \
        .value = Value,                                                                                                \
        .min = Min,                                                                                                    \
        .max = Max,                                                                                                    \
        .precision = Precision,                                                                                        \
        .expectError = ErrorString,                                                                                    \
    }

        ERROR_CASE(MC_DEC128_C(1),
                   OPT_NULLOPT,
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_I32(5),
                   "min, max, and precision must all be set or must all be unset"),
        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(0)),
                   OPT_NULLOPT,
                   OPT_I32(5),
                   "min, max, and precision must all be set or must all be unset"),
        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(1)),
                   OPT_I32(5),
                   "The minimum value must be less than the maximum value"),

        ERROR_CASE(MC_DEC128_C(1),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(3)),
                   OPT_I32(5),
                   "Value must be greater than or equal to the minimum value "
                   "and less than or equal to the maximum value"),
        ERROR_CASE(MC_DEC128_C(4),
                   OPT_MC_DEC128(MC_DEC128_C(2)),
                   OPT_MC_DEC128(MC_DEC128_C(3)),
                   OPT_I32(5),
                   "Value must be greater than or equal to the minimum value "
                   "and less than or equal to the maximum value"),

        ERROR_CASE(MC_DEC128_POSITIVE_INFINITY,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),
        ERROR_CASE(MC_DEC128_NEGATIVE_INFINITY,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

        ERROR_CASE(MC_DEC128_POSITIVE_NAN,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

        ERROR_CASE(MC_DEC128_NEGATIVE_NAN,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   OPT_NULLOPT,
                   "Infinity and Nan Decimal128 values are not supported."),

/* Test cases copied from Decimal128_Bounds_Precision ... begin */
#define ASSERT_EIBP(Value, Precision, Expect)                                                                          \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(Value),                                                                         \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(MC_DEC128_C(100000)),                                                                     \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128(Expect),                                                                                 \
    }
        ASSERT_EIBP("3.141592653589E-1", 10, 1000003141592653),
        ASSERT_EIBP("31.41592653589E-2", 10, 1000003141592653),
        ASSERT_EIBP("314.1592653589E-3", 10, 1000003141592653),
        ASSERT_EIBP("3141.592653589E-4", 10, 1000003141592653),
        ASSERT_EIBP("31415.92653589E-5", 10, 1000003141592653),
        ASSERT_EIBP("314159.2653589E-6", 10, 1000003141592653),
        ASSERT_EIBP("3141592.653589E-7", 10, 1000003141592653),
        ASSERT_EIBP("31415926.53589E-8", 10, 1000003141592653),
#undef ASSERT_EIBP

#define ASSERT_EIBPL(Value, Precision, Expect)                                                                         \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(Value),                                                                         \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(mc_dec128_from_string("1E22")),                                                           \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = mlib_int128_from_string(Expect, NULL),                                                               \
    }

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 5, "31415926535897942384626433"),
        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 6, "314159265358979423846264338"),

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 7, "3141592653589794238462643383"),

        ASSERT_EIBPL("3.1415926535897932384626433832795E20", 8, "31415926535897942384626433832"),
#undef ASSERT_EIBPL

#define ASSERT_EIBP(Value, Precision, Expect)                                                                          \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Value),                                                                        \
        .min = OPT_MC_DEC128(MC_DEC128_C(-100000)),                                                                    \
        .max = OPT_MC_DEC128(MC_DEC128_C(100000)),                                                                     \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128_CAST(Expect),                                                                            \
    }
        ASSERT_EIBP(3.141592653589, 1, 1000031),
        ASSERT_EIBP(3.141592653589, 2, 10000314),
        ASSERT_EIBP(3.141592653589, 3, 100003141),
        ASSERT_EIBP(3.141592653589, 4, 1000031415),
        ASSERT_EIBP(3.141592653589, 5, 10000314159),
        ASSERT_EIBP(3.141592653589, 6, 100003141592),
        ASSERT_EIBP(3.141592653589, 7, 1000031415926),
#undef ASSERT_EIBP

#define ASSERT_EIBB(Val, Max, Min, Precision, Expect)                                                                  \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Val),                                                                          \
        .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                                                             \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)),                                                             \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = MLIB_INT128_CAST(Expect),                                                                            \
    }

// ASSERT_EIBB_OVERFLOW defines cases where applying min/max/precision result in a domain needing >= 128 bits.
// For range v1, expect precision to be ignored.
// For range v2, expect an error.
#define ASSERT_EIBB_OVERFLOW(Val, Max, Min, Precision, Expect)                                                         \
    (Decimal128Test){                                                                                                  \
        .value = mc_dec128_from_string(#Val),                                                                          \
        .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                                                             \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)),                                                             \
        .precision = OPT_I32(Precision),                                                                               \
        .expect = Expect,                                                                                              \
        .use_range_v1 = true,                                                                                          \
    },                                                                                                                 \
        (Decimal128Test) {                                                                                             \
        .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                       \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_I32(Precision),                            \
        .expectError = "The domain of decimal values specified by the min, max, and precision "                        \
                       "cannot be represented in fewer than 128 bits"                                                  \
    }

#define ASSERT_EIBB_ERROR(Val, Max, Min, Precision, Expect, Error)                                                     \
    (Decimal128Test) {                                                                                                 \
        .value = mc_dec128_from_string(#Val), .min = OPT_MC_DEC128(mc_dec128_from_string(#Min)),                       \
        .max = OPT_MC_DEC128(mc_dec128_from_string(#Max)), .precision = OPT_I32(Precision), .expectError = Error       \
    }

        ASSERT_EIBB(0, 1, -1, 3, 1000),
        ASSERT_EIBB(0, 1, -1E5, 3, 100000000),

        ASSERT_EIBB(-1E-33, 1, -1E5, 3, 100000000),

        ASSERT_EIBB_ERROR(0,
                          MC_DEC128_LARGEST_POSITIVE,
                          MC_DEC128_LARGEST_NEGATIVE,
                          3,
                          mlib_int128_from_string("170141183460469231731687303715884105728", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB_ERROR(0,
                          DBL_MAX,
                          DBL_MIN,
                          3,
                          mlib_int128_from_string("170141183460469231731687303715884105728", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB(3.141592653589, 5, 0, 0, 3),
        ASSERT_EIBB(3.141592653589, 5, 0, 1, 31),

        ASSERT_EIBB(3.141592653589, 5, 0, 2, 314),

        ASSERT_EIBB(3.141592653589, 5, 0, 3, 3141),
        ASSERT_EIBB(3.141592653589, 5, 0, 16, 31415926535890000),

        ASSERT_EIBB(-5, -1, -10, 3, 5000),

        ASSERT_EIBB_ERROR(1E100,
                          DBL_MAX,
                          DBL_MIN,
                          3,
                          mlib_int128_from_string("232572183460469231731687303715884099485", NULL),
                          "Invalid upper bound for Decimal128 precision. Max is infinite."),

        ASSERT_EIBB(1E9, 1E10, 0, 3, 1000000000000),
        ASSERT_EIBB(1E9, 1E10, 0, 0, 1000000000),

        ASSERT_EIBB(-5, 10, -10, 0, 5),
        ASSERT_EIBB(-5, 10, -10, 2, 500),

        ASSERT_EIBB(5E-30, 10E-30, 1E-30, 35, 400000),

        // Test a range that requires > 64 bits.
        ASSERT_EIBB(5, 18446744073709551616, .1, 1, 49),

#undef ASSERT_EIBB
#undef ASSERT_EIBB_OVERFLOW
#undef ASSERT_EIBB_ERROR

        /* Test cases copied from Decimal128_Bounds_Precision ... end */

        {// Expect error due to precision exceeding INT32_MAX.
         .min = OPT_MC_DEC128(MC_DEC128_C(1)),
         .max = OPT_MC_DEC128(MC_DEC128_C(2)),
         .precision = OPT_I32(-1),
         .expectError = "Precision must be non-negative"},
        {// Expect error due to precision exceeding max finite Decimal128.
         // The largest decimal128 value is 9.99999...x10^6144. 10^6145 results in infinity.
         .min = OPT_MC_DEC128(MC_DEC128_C(0)),
         .max = OPT_MC_DEC128(MC_DEC128_C(1)),
         .precision = OPT_I32(6145),
         .expectError = "Precision is too large"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        Decimal128Test *test = tests + i;
        mongocrypt_status_t *const status = mongocrypt_status_new();

        if (test->min.set && test->max.set && test->precision.set) {
            TEST_PRINTF("_test_RangeTest_Encode_Decimal128: value=%s, min=%s, max=%s, "
                        "precision=%" PRIu32 "\n",
                        mc_dec128_to_string(test->value).str,
                        mc_dec128_to_string(test->min.value).str,
                        mc_dec128_to_string(test->max.value).str,
                        test->precision.value);
        } else {
            TEST_PRINTF("_test_RangeTest_Encode_Decimal128: value=%s\n", mc_dec128_to_string(test->value).str);
        }
        mc_OSTType_Decimal128 got;
        const bool ok = mc_getTypeInfoDecimal128(
            (mc_getTypeInfoDecimal128_args_t){
                .value = test->value,
                .min = test->min,
                .max = test->max,
                .precision = test->precision,
            },
            &got,
            status);
        if (test->expectError) {
            ASSERT_OR_PRINT_MSG(!ok, "expected error, but got none");
            ASSERT_STATUS_CONTAINS(status, test->expectError);
        } else {
            ASSERT_OK_STATUS(ok, status);

            ASSERT_CMPINT128_EQ(got.value, test->expect);
            ASSERT_CMPINT128_EQ(got.min, MLIB_INT128(0));
        }
        mongocrypt_status_destroy(status);
    }
}

#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

void _mongocrypt_tester_install_range_encoding(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_RangeTest_Encode_Int32);
    INSTALL_TEST(_test_RangeTest_Encode_Int64);
    INSTALL_TEST(_test_canUsePrecisionModeDouble);
    INSTALL_TEST(_test_RangeTest_Encode_Double);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_canUsePrecisionModeDecimal);
    INSTALL_TEST(_test_RangeTest_Encode_Decimal128);
#endif
}
libmongocrypt-1.19.0/test/test-mc-range-mincover.c000066400000000000000000000602261521103432300221030ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

#include "mc-array-private.h"
#include "mc-optional-private.h"
#include "mc-range-encoding-private.h"
#include "mc-range-mincover-private.h"

enum {
    /// Why this number? The Decimal128 tests generate thousands of test strings,
    /// but we can't set this arbitrarily high, since we'll bump up on stack
    /// overflow on MSVC. This is large enough to capture all strings in all
    /// Decimal128 tests without overflowing the stack.
    MAX_MINCOVER_STRINGS = 4500
};

typedef struct {
    int32_t lowerBound;
    bool includeLowerBound;
    int32_t upperBound;
    bool includeUpperBound;
    mc_optional_int32_t min;
    mc_optional_int32_t max;
    size_t sparsity;
    uint32_t trimFactor;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Int32Test;

typedef struct {
    int64_t lowerBound;
    bool includeLowerBound;
    int64_t upperBound;
    bool includeUpperBound;
    mc_optional_int64_t min;
    mc_optional_int64_t max;
    size_t sparsity;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Int64Test;

typedef struct {
    double lowerBound;
    bool includeLowerBound;
    double upperBound;
    bool includeUpperBound;
    size_t sparsity;
    mc_optional_double_t min;
    mc_optional_double_t max;
    mc_optional_int32_t precision;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} DoubleTest;

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
typedef struct {
    mc_dec128 lowerBound;
    bool includeLowerBound;
    mc_dec128 upperBound;
    bool includeUpperBound;
    size_t sparsity;
    mc_optional_dec128_t min;
    mc_optional_dec128_t max;
    mc_optional_int32_t precision;
    const char *expectMincoverStrings[MAX_MINCOVER_STRINGS];
    const char *expectError;
} Decimal128Test;
#endif

typedef struct _test_getMincover_args {
    mc_mincover_t *(*getMincover)(void *tests, size_t idx, mongocrypt_status_t *status);
    const char *(*expectError)(void *tests, size_t idx, mongocrypt_status_t *status);
    const char *const *(*expectMincoverStrings)(void *tests, size_t idx);
    void (*dump)(void *tests, size_t idx, mc_mincover_t *got);
} _test_getMincover_args;

static mc_mincover_t *_test_getMincover32(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    Int32Test *test = (Int32Test *)tests + idx;

    return mc_getMincoverInt32((mc_getMincoverInt32_args_t){.lowerBound = test->lowerBound,
                                                            .includeLowerBound = test->includeLowerBound,
                                                            .upperBound = test->upperBound,
                                                            .includeUpperBound = test->includeUpperBound,
                                                            .min = test->min,
                                                            .max = test->max,
                                                            .sparsity = test->sparsity,
                                                            .trimFactor = OPT_I32(test->trimFactor)},
                               status);
}

static mc_mincover_t *_test_getMincover64(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    Int64Test *const test = (Int64Test *)tests + idx;

    return mc_getMincoverInt64((mc_getMincoverInt64_args_t){.lowerBound = test->lowerBound,
                                                            .includeLowerBound = test->includeLowerBound,
                                                            .upperBound = test->upperBound,
                                                            .includeUpperBound = test->includeUpperBound,
                                                            .min = test->min,
                                                            .max = test->max,
                                                            .sparsity = test->sparsity,
                                                            .trimFactor = OPT_I32(trimFactor)},
                               status);
}

static mc_mincover_t *_test_getMincoverDouble_helper(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    DoubleTest *const test = (DoubleTest *)tests + idx;

    return mc_getMincoverDouble(
        (mc_getMincoverDouble_args_t){.lowerBound = test->lowerBound,
                                      .includeLowerBound = test->includeLowerBound,
                                      .upperBound = test->upperBound,
                                      .includeUpperBound = test->includeUpperBound,
                                      .sparsity = test->sparsity,
                                      .min = test->precision.set ? test->min : (mc_optional_double_t){0},
                                      .max = test->precision.set ? test->max : (mc_optional_double_t){0},
                                      .precision = test->precision,
                                      .trimFactor = OPT_I32(trimFactor)},
        status);
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static mc_mincover_t *_test_getMincoverDecimal128_helper(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);

    Decimal128Test *const test = (Decimal128Test *)tests + idx;

    const uint32_t trimFactor = 0; // At present, all test cases expect trimFactor=0.
    return mc_getMincoverDecimal128(
        (mc_getMincoverDecimal128_args_t){.lowerBound = test->lowerBound,
                                          .includeLowerBound = test->includeLowerBound,
                                          .upperBound = test->upperBound,
                                          .includeUpperBound = test->includeUpperBound,
                                          .sparsity = test->sparsity,
                                          .min = test->precision.set ? test->min : (mc_optional_dec128_t){0},
                                          .max = test->precision.set ? test->max : (mc_optional_dec128_t){0},
                                          .precision = test->precision,
                                          .trimFactor = OPT_I32(trimFactor)},
        status);
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static const char *_test_expectError32(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    return ((Int32Test *)tests + idx)->expectError;
}

static const char *_test_expectError64(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    return ((Int64Test *)tests + idx)->expectError;
}

static const char *_test_expectErrorDouble(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    DoubleTest *test = ((DoubleTest *)tests + idx);
    if (test->min.set && test->max.set && test->precision.set) {
        // Expect an error for tests including an invalid min/max/precision.
        uint32_t ignored;
        if (!mc_canUsePrecisionModeDouble(test->min.value, test->max.value, test->precision.value, &ignored, status)) {
            if (!mongocrypt_status_ok(status)) {
                return mongocrypt_status_message(status, NULL);
            }

            return "The domain of double values specified by the min, max, and precision cannot be represented in "
                   "fewer than 64 bits";
        }
    }
    return test->expectError;
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static const char *_test_expectErrorDecimal128(void *tests, size_t idx, mongocrypt_status_t *status) {
    BSON_ASSERT_PARAM(tests);
    BSON_ASSERT_PARAM(status);
    Decimal128Test *test = ((Decimal128Test *)tests + idx);
    if (test->min.set && test->max.set && test->precision.set) {
        // Expect an error for tests including an invalid min/max/precision.
        uint32_t ignored;
        if (!mc_canUsePrecisionModeDecimal(test->min.value, test->max.value, test->precision.value, &ignored, status)) {
            if (!mongocrypt_status_ok(status)) {
                return mongocrypt_status_message(status, NULL);
            }

            return "The domain of decimal values specified by the min, max, and precision cannot be represented in "
                   "fewer than 128 bits";
        }
    }
    return test->expectError;
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static const char *const *_test_expectMincover32(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Int32Test *)tests + idx)->expectMincoverStrings;
}

static const char *const *_test_expectMincover64(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Int64Test *)tests + idx)->expectMincoverStrings;
}

static const char *const *_test_expectMincoverDouble(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((DoubleTest *)tests + idx)->expectMincoverStrings;
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static const char *const *_test_expectMincoverDecimal128(void *tests, size_t idx) {
    BSON_ASSERT_PARAM(tests);
    return ((Decimal128Test *)tests + idx)->expectMincoverStrings;
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static void _test_dump_32(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Int32Test *const test = (Int32Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%" PRId32 " (%s) upperBound=%" PRId32 " (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%" PRId32, test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%" PRId32, test->max.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

static void _test_dump_64(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Int64Test *const test = (Int64Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%" PRId64 " (%s) upperBound=%" PRId64 " (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%" PRId64, test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%" PRId64, test->max.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

static void _test_dump_Double(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    DoubleTest *const test = (DoubleTest *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%f (%s) upperBound=%f (%s)",
                       test->lowerBound,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       test->upperBound,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%f", test->min.value);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%f", test->max.value);
    }
    if (test->precision.set) {
        TEST_STDERR_PRINTF(" precision=%" PRId32, test->precision.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static void _test_dump_Decimal128(void *tests, size_t idx, mc_mincover_t *got) {
    BSON_ASSERT_PARAM(tests);
    Decimal128Test *const test = (Decimal128Test *)tests + idx;
    TEST_STDERR_PRINTF("testcase: lowerBound=%s (%s) upperBound=%s (%s)",
                       mc_dec128_to_string(test->lowerBound).str,
                       test->includeLowerBound ? "inclusive" : "exclusive",
                       mc_dec128_to_string(test->upperBound).str,
                       test->includeUpperBound ? "inclusive" : "exclusive");
    if (test->min.set) {
        TEST_STDERR_PRINTF(" min=%s", mc_dec128_to_string(test->min.value).str);
    }
    if (test->max.set) {
        TEST_STDERR_PRINTF(" max=%s", mc_dec128_to_string(test->max.value).str);
    }
    if (test->precision.set) {
        TEST_STDERR_PRINTF(" precision=%" PRIu32, test->precision.value);
    }
    TEST_STDERR_PRINTF(" sparsity=%zu\n", test->sparsity);
    TEST_STDERR_PRINTF("mincover expected ... begin\n");
    for (const char **p = test->expectMincoverStrings; *p; ++p) {
        TEST_STDERR_PRINTF("  %s\n", *p);
    }
    TEST_STDERR_PRINTF("mincover expected ... end\n");
    TEST_STDERR_PRINTF("mincover got ... begin\n");
    for (size_t i = 0; i < mc_mincover_len(got); i++) {
        TEST_STDERR_PRINTF("  %s\n", mc_mincover_get(got, i));
    }
    TEST_STDERR_PRINTF("mincover got ... end\n");
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static void _test_getMincover_impl(void *tests, size_t num_tests, _test_getMincover_args args) {
    BSON_ASSERT_PARAM(tests);

    for (size_t i = 0; i < num_tests; i++) {
        mongocrypt_status_t *const status = mongocrypt_status_new();
        mc_mincover_t *got = args.getMincover(tests, i, status);
        const char *expectError = args.expectError(tests, i, status);
        if (expectError) {
            ASSERT_OR_PRINT_MSG(NULL == got, "expected error, got success");
            ASSERT_STATUS_CONTAINS(status, expectError);
            mongocrypt_status_destroy(status);
            continue;
        }
        ASSERT_OK_STATUS(got != NULL, status);

        size_t numGot = mc_mincover_len(got);
        const char *const *expectStrings = args.expectMincoverStrings(tests, i);

        const char *const *exp_iter = expectStrings;
        size_t nthItem = 0;
        for (; *exp_iter; ++nthItem, ++exp_iter) {
            if (nthItem > numGot) {
                // List length mismatch. Keep scanning, though. We'll use the
                // numbers later
                continue;
            }
            const char *gotItem = mc_mincover_get(got, nthItem);
            const char *expectItem = *exp_iter;

            if (0 == strcmp(gotItem, expectItem)) {
                // This one matches, Keep going.
                continue;
            }
            args.dump(tests, nthItem, got);
            TEST_ERROR("test %zu: mincover mismatch at index %zu:\n"
                       "      Got: %s\n"
                       " Expected: %s\n",
                       i,
                       nthItem,
                       gotItem,
                       expectItem);
        }

        if (nthItem != numGot) {
            args.dump(tests, i, got);
            TEST_ERROR("test %zu: Got the wrong number of mincover items. Expected %zu "
                       "items, but got %zu\n",
                       i,
                       nthItem,
                       numGot);
        }

        mc_mincover_destroy(got);
        mongocrypt_status_destroy(status);
    }
}

static void _test_getMincoverInt32(_mongocrypt_tester_t *tester) {
    static Int32Test tests[] = {
        {.lowerBound = 1,
         .includeLowerBound = false,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"01"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = false,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "010"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "01"}},
        {.lowerBound = 3,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"011"}},
        {.lowerBound = 4,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectError = "must be less than or equal to"},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 8,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectError = "less than or equal to the maximum value"},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"root"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 1,
         .expectMincoverStrings = {"0", "1"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 2,
         .expectMincoverStrings = {"00", "01", "10", "11"}},
        {.lowerBound = 0,
         .includeLowerBound = true,
         .upperBound = 7,
         .includeUpperBound = true,
         .min = OPT_I32_C(0),
         .max = OPT_I32_C(7),
         .sparsity = 1,
         .trimFactor = 3,
         .expectError =
             "Trim factor must be less than the number of bits (3) used to represent an element of the domain"},

#include "./data/range-min-cover/mincover_int32.cstruct"

    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincover32,
                                                    .expectMincoverStrings = _test_expectMincover32,
                                                    .expectError = _test_expectError32,
                                                    .dump = _test_dump_32});
}

static void _test_getMincoverInt64(_mongocrypt_tester_t *tester) {
    static Int64Test tests[] = {
        {.lowerBound = 1,
         .includeLowerBound = false,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"01"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = false,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "010"}},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"001", "01"}},
        {.lowerBound = 3,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectMincoverStrings = {"011"}},
        {.lowerBound = 4,
         .includeLowerBound = true,
         .upperBound = 3,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectError = "must be less than or equal to"},
        {.lowerBound = 1,
         .includeLowerBound = true,
         .upperBound = 8,
         .includeUpperBound = true,
         .min = OPT_I64_C(0),
         .max = OPT_I64_C(7),
         .sparsity = 1,
         .expectError = "less than or equal to the maximum value"},

#include "./data/range-min-cover/mincover_int64.cstruct"

    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincover64,
                                                    .expectMincoverStrings = _test_expectMincover64,
                                                    .expectError = _test_expectError64,
                                                    .dump = _test_dump_64});
}

static void _test_getMincoverDouble(_mongocrypt_tester_t *tester) {
    static DoubleTest tests[] = {
#include "./data/range-min-cover/mincover_double.cstruct"
#include "./data/range-min-cover/mincover_double_precision.cstruct"
    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincoverDouble_helper,
                                                    .expectMincoverStrings = _test_expectMincoverDouble,
                                                    .expectError = _test_expectErrorDouble,
                                                    .dump = _test_dump_Double});
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
static void _test_getMincoverDecimal128(_mongocrypt_tester_t *tester) {
    Decimal128Test tests[] = {
#include "./data/range-min-cover/mincover_decimal128.cstruct"
#include "./data/range-min-cover/mincover_decimal128_precision.cstruct"
    };

    _test_getMincover_impl(tests,
                           sizeof(tests) / sizeof(tests[0]),
                           (_test_getMincover_args){.getMincover = _test_getMincoverDecimal128_helper,
                                                    .expectMincoverStrings = _test_expectMincoverDecimal128,
                                                    .expectError = _test_expectErrorDecimal128,
                                                    .dump = _test_dump_Decimal128});
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

void _mongocrypt_tester_install_range_mincover(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_getMincoverInt32);
    INSTALL_TEST(_test_getMincoverInt64);
    INSTALL_TEST(_test_getMincoverDouble);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_getMincoverDecimal128);
#endif
}
libmongocrypt-1.19.0/test/test-mc-rangeopts.c000066400000000000000000000373611521103432300211750ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-range-encoding-private.h"
#include "mc-rangeopts-private.h"
#include "test-mongocrypt.h"

#define RAW_STRING(...) #__VA_ARGS__

static void test_mc_RangeOpts_parse(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *expectError;
        mc_optional_int32_t expectMin;
        mc_optional_int32_t expectMax;
        int64_t expectSparsity;
        mc_optional_uint32_t expectPrecision;
        mc_optional_int32_t expectTrimFactor;
    } testcase;

    testcase tests[] = {
        {.desc = "Works",
         .in = RAW_STRING({"min" : 123, "max" : 456, "sparsity" : {"$numberLong" : "1"}}),
         .expectSparsity = 1,
         .expectMin = OPT_I32_C(123),
         .expectMax = OPT_I32_C(456)},
        {.desc = "Errors if precision is set with int min/max",
         .in = RAW_STRING({"min" : 123, "max" : 456, "precision" : 2, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "expected 'precision' to be set with double or decimal128 index"},
        {.desc = "Errors on extra fields",
         .in = RAW_STRING({"min" : 123, "max" : 456, "sparsity" : {"$numberLong" : "1"}, "foo" : 1}),
         .expectError = "Unrecognized field: 'foo'"},
        {.desc = "Errors if min/max types mismatch",
         .in = RAW_STRING({"min" : 123, "max" : 456.0, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "expected 'min' and 'max' to be same type"},
        {
            .desc = "Does not require min/max",
            .in = RAW_STRING({"sparsity" : {"$numberLong" : "1"}}),
            .expectSparsity = 1,
        },
        {.desc = "Requires precision for double when min/max is set",
         .in = RAW_STRING({"min" : 0.0, "max" : 1.0, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "expected 'precision'"},
        {.desc = "Requires min/max for double when precision is set",
         .in = RAW_STRING({"precision" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "setting precision requires min"},
        {.desc = "Requires precision for double when only min is set",
         .in = RAW_STRING({"min" : 0.0, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "expected 'precision'"},
        {.desc = "Works when trim factor is set and Range V2 is enabled",
         .in = RAW_STRING({"trimFactor" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .expectSparsity = 1,
         .expectTrimFactor = OPT_I32(1)},
        {.desc = "Does not require sparsity",
         .in = RAW_STRING({"min" : 123, "max" : 456}),
         .expectSparsity = mc_FLERangeSparsityDefault,
         .expectMin = OPT_I32_C(123),
         .expectMax = OPT_I32_C(456)},
        {.desc = "Errors on negative trim factor",
         .in = RAW_STRING({"trimFactor" : -1, "sparsity" : {"$numberLong" : "1"}}),
         .expectError = "'trimFactor' must be non-negative"},
        {.desc = "Errors on negative sparsity",
         .in = RAW_STRING({"sparsity" : {"$numberLong" : "-1"}}),
         .expectError = "sparsity must be non-negative"},
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_RangeOpts_t ro;
        TEST_PRINTF("running test_mc_RangeOpts_parse subtest: %s\n", test->desc);
        bool ret = mc_RangeOpts_parse(&ro, TMP_BSON_STR(test->in), status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_CMPINT(test->expectMin.set, ==, ro.min.set);
            if (test->expectMin.set) {
                ASSERT_CMPINT32(test->expectMin.value, ==, bson_iter_int32(&ro.min.value));
            }
            ASSERT_CMPINT(test->expectMax.set, ==, ro.max.set);
            if (test->expectMax.set) {
                ASSERT_CMPINT32(test->expectMax.value, ==, bson_iter_int32(&ro.max.value));
            }
            ASSERT_CMPINT64(test->expectSparsity, ==, ro.sparsity);
            ASSERT_CMPINT(test->expectPrecision.set, ==, ro.precision.set);
            ASSERT_CMPINT(test->expectPrecision.value, ==, ro.precision.value);
            ASSERT_CMPINT(test->expectTrimFactor.set, ==, ro.trimFactor.set);
            ASSERT_CMPINT(test->expectTrimFactor.value, ==, ro.trimFactor.value);
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        mc_RangeOpts_cleanup(&ro);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_RangeOpts_to_FLE2RangeInsertSpec(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *v;
        const char *expectError;
        const char *expect;
    } testcase;

    testcase tests[] = {
        {.desc = "Works",
         .in = RAW_STRING({"min" : 123, "max" : 456, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 789}),
         .expect = RAW_STRING({"v" : {"v" : 789, "min" : 123, "max" : 456}})},
        {.desc = "Works with precision",
         .in = RAW_STRING({"min" : 123.0, "max" : 456.0, "precision" : 2, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 789.0}),
         .expect = RAW_STRING({"v" : {"v" : 789.0, "min" : 123.0, "max" : 456.0, "precision" : 2}})},
        {.desc = "Errors with missing 'v'",
         .in = RAW_STRING({"min" : 123, "max" : 456, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"foo" : "bar"}),
         .expectError = "Unable to find 'v'"},
        // Tests of trim factor
        {.desc = "tf = 0 works",
         .in = RAW_STRING({"trimFactor" : 0, "min" : 0, "max" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expect = RAW_STRING({"v" : {"v" : 0, "min" : 0, "max" : 1, "trimFactor" : 0}})},
        {.desc = "tf = 1 fails when domain size is 2 = 2^1",
         .in = RAW_STRING({"trimFactor" : 1, "min" : 0, "max" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expectError = "Trim factor (1) must be less than the total number of bits (1) used to represent any element "
                        "in the domain."},
        {.desc = "tf = 1 works when domain size is 3 > 2^1",
         .in = RAW_STRING({"trimFactor" : 1, "min" : 0, "max" : 2, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expect = RAW_STRING({"v" : {"v" : 0, "min" : 0, "max" : 2, "trimFactor" : 1}})},
        {.desc = "tf = 2 fails when domain size is 3 <= 2^2",
         .in = RAW_STRING({"trimFactor" : 2, "min" : 0, "max" : 2, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expectError = "Trim factor (2) must be less than the total number of bits (2) used to represent any element "
                        "in the domain."},

        // min = INT32_MIN, max = INT32_MAX
        {.desc = "tf = 31 works for unbounded int32 (domain size = 2^32)",
         .in = RAW_STRING(
             {"trimFactor" : 31, "min" : -2147483648, "max" : 2147483647, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expect = RAW_STRING({"v" : {"v" : 0, "min" : -2147483648, "max" : 2147483647, "trimFactor" : 31}})},
        {.desc = "tf = 32 fails for unbounded int32 (domain size = 2^32)",
         .in = RAW_STRING(
             {"trimFactor" : 32, "min" : -2147483648, "max" : 2147483647, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0}),
         .expectError = "Trim factor (32) must be less than the total number of bits (32) used to represent any "
                        "element in the domain."},

        // min = INT64_MIN, max = INT64_MAX
        {.desc = "tf = 63 works for int64 with no min/max (domain size = 2^64)",
         .in = RAW_STRING({
             "trimFactor" : 63,
             "min" : -9223372036854775808,
             "max" : 9223372036854775807,
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$numberLong" : "0"}}),
         .expect = RAW_STRING({
             "v" : {
                 "v" : {"$numberLong" : "0"},
                 "min" : {"$numberLong" : "-9223372036854775808"},
                 "max" : {"$numberLong" : "9223372036854775807"},
                 "trimFactor" : 63
             }
         })},
        {.desc = "tf = 64 fails for int64 with no min/max (domain size = 2^64)",
         .in = RAW_STRING({
             "trimFactor" : 64,
             "min" : -9223372036854775808,
             "max" : 9223372036854775807,
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$numberLong" : "0"}}),
         .expectError = "Trim factor (64) must be less than the total number of bits (64) used to represent any "
                        "element in the domain."},

        {.desc = "tf = 63 works for date with no min/max (domain size = 2^64)",
         .in = RAW_STRING({
             "trimFactor" : 63,
             "min" : {"$date" : {"$numberLong" : "-9223372036854775808"}},
             "max" : {"$date" : {"$numberLong" : "9223372036854775807"}},
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$date" : {"$numberLong" : "0"}}}),
         .expect = RAW_STRING({
             "v" : {
                 "v" : {"$date" : {"$numberLong" : "0"}},
                 "min" : {"$date" : {"$numberLong" : "-9223372036854775808"}},
                 "max" : {"$date" : {"$numberLong" : "9223372036854775807"}},
                 "trimFactor" : 63
             }
         })},
        {.desc = "tf = 64 fails for date with no min/max (domain size = 2^64)",
         .in = RAW_STRING({
             "trimFactor" : 64,
             "min" : {"$date" : {"$numberLong" : "-9223372036854775808"}},
             "max" : {"$date" : {"$numberLong" : "9223372036854775807"}},
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$date" : {"$numberLong" : "0"}}}),
         .expectError = "Trim factor (64) must be less than the total number of bits (64) used to represent any "
                        "element in the domain."},

        {.desc = "tf bound check passes correctly for double with min, max, precision set (tf = 9, 2^9 < domain size < "
                 "2^10)",
         .in = RAW_STRING(
             {"trimFactor" : 9, "min" : 0.0, "max" : 100.0, "precision" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0.0}),
         .expect = RAW_STRING({"v" : {"v" : 0.0, "min" : 0.0, "max" : 100.0, "precision" : 1, "trimFactor" : 9}})},
        {.desc = "tf bound check fails correctly for double with min, max, precision set (tf = 10, domain size < 2^10)",
         .in = RAW_STRING(
             {"trimFactor" : 10, "min" : 0.0, "max" : 100.0, "precision" : 1, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0.0}),
         .expectError = "Trim factor (10) must be less than the total number of bits (10) used to represent any "
                        "element in the domain."},

        {.desc = "tf = 63 works for unbounded double (domain size = 2^64)",
         .in = RAW_STRING({"trimFactor" : 63, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0.0}),
         // note - when min and max are unset, they are added into the insert spec.
         .expect = RAW_STRING({
             "v" : {
                 "v" : 0.0,
                 "min" : {"$numberDouble" : "-1.7976931348623157081e+308"},
                 "max" : {"$numberDouble" : "1.7976931348623157081e+308"},
                 "trimFactor" : 63
             }
         })},
        {.desc = "tf = 64 fails for unbounded double (domain size = 2^64))",
         .in = RAW_STRING({"trimFactor" : 64, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : 0.0}),
         .expectError = "Trim factor (64) must be less than the total number of bits (64) used to represent any "
                        "element in the domain."},

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
        {.desc = "tf bound check passes correctly for decimal with min, max, precision set (tf = 9, 2^9 < domain size "
                 "< 2^10)",
         .in = RAW_STRING({
             "trimFactor" : 9,
             "min" : {"$numberDecimal" : "0"},
             "max" : {"$numberDecimal" : "100"},
             "precision" : 1,
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$numberDecimal" : "0"}}),
         .expect = RAW_STRING({
             "v" : {
                 "v" : {"$numberDecimal" : "0"},
                 "min" : {"$numberDecimal" : "0"},
                 "max" : {"$numberDecimal" : "100"},
                 "precision" : 1,
                 "trimFactor" : 9
             }
         })},
        {.desc =
             "tf bound check fails correctly for decimal with min, max, precision set (tf = 10, domain size < 2^10)",
         .in = RAW_STRING({
             "trimFactor" : 10,
             "min" : {"$numberDecimal" : "0"},
             "max" : {"$numberDecimal" : "100"},
             "precision" : 1,
             "sparsity" : {"$numberLong" : "1"}
         }),
         .v = RAW_STRING({"v" : {"$numberDecimal" : "0"}}),
         .expectError = "Trim factor (10) must be less than the total number of bits (10) used to represent any "
                        "element in the domain."},

        {.desc = "tf = 127 works for unbounded decimal (domain size = 2^128)",
         .in = RAW_STRING({"trimFactor" : 127, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : {"$numberDecimal" : "0"}}),
         .expect = RAW_STRING({
             "v" : {
                 "v" : {"$numberDecimal" : "0"},
                 "min" : {"$numberDecimal" : "-9.999999999999999999999999999999999E+6144"},
                 "max" : {"$numberDecimal" : "9.999999999999999999999999999999999E+6144"},
                 "trimFactor" : 127
             }
         })},
        {.desc = "tf = 128 fails for unbounded decimal (domain size = 2^128)",
         .in = RAW_STRING({"trimFactor" : 128, "sparsity" : {"$numberLong" : "1"}}),
         .v = RAW_STRING({"v" : {"$numberDecimal" : "0"}}),
         .expectError = "Trim factor (128) must be less than the total number of bits (128) used to represent any "
                        "element in the domain."},
#endif
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_RangeOpts_t ro;
        TEST_PRINTF("running test_mc_RangeOpts_to_FLE2RangeInsertSpec subtest: %s\n", test->desc);
        ASSERT_OK_STATUS(mc_RangeOpts_parse(&ro, TMP_BSON_STR(test->in), status), status);
        bson_t out = BSON_INITIALIZER;
        bool ret = mc_RangeOpts_to_FLE2RangeInsertSpec(&ro, TMP_BSON_STR(test->v), &out, status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_EQUAL_BSON(TMP_BSON_STR(test->expect), &out);
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        bson_destroy(&out);
        mc_RangeOpts_cleanup(&ro);
        mongocrypt_status_destroy(status);
    }
}

void _mongocrypt_tester_install_mc_RangeOpts(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mc_RangeOpts_parse);
    INSTALL_TEST(test_mc_RangeOpts_to_FLE2RangeInsertSpec);
}
libmongocrypt-1.19.0/test/test-mc-reader.c000066400000000000000000000144161521103432300204310ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-reader-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

static void _test_mc_reader(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_copy_from_hex(&input_buf, "ABCD");

    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &input_buf, __func__);

    uint8_t value;
    ASSERT_OK_STATUS(mc_reader_read_u8(&reader, &value, status), status);
    ASSERT_CMPUINT(value, ==, 0xAB);
    ASSERT_CMPUINT64(mc_reader_get_consumed_length(&reader), ==, 1);
    ASSERT_CMPUINT64(mc_reader_get_remaining_length(&reader), ==, 1);

    ASSERT_OK_STATUS(mc_reader_read_u8(&reader, &value, status), status);
    ASSERT_CMPUINT(value, ==, 0xCD);
    ASSERT_CMPUINT64(mc_reader_get_consumed_length(&reader), ==, 2);
    ASSERT_CMPUINT64(mc_reader_get_remaining_length(&reader), ==, 0);

    ASSERT_FAILS_STATUS(mc_reader_read_u8(&reader, &value, status), status, "expected byte length >= 3 got: 2");

    _mongocrypt_buffer_cleanup(&input_buf);
    mongocrypt_status_destroy(status);
}

static void _test_mc_reader_uuid(_mongocrypt_tester_t *tester) {
    const uint8_t expected_bytes[] =
        {0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12};
    uint64_t expected_len = sizeof(expected_bytes);

    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_copy_from_hex(&input_buf, "12345678901234567890123456789012");

    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &input_buf, __func__);

    _mongocrypt_buffer_t value;
    ASSERT_OK_STATUS(mc_reader_read_uuid_buffer(&reader, &value, status), status);
    ASSERT(value.subtype == BSON_SUBTYPE_UUID);

    ASSERT_CMPBYTES(expected_bytes, (size_t)expected_len, value.data, value.len);

    uint8_t ui;
    ASSERT_FAILS_STATUS(mc_reader_read_u8(&reader, &ui, status), status, "expected byte length >= 17 got: 16");

    _mongocrypt_buffer_cleanup(&input_buf);
    _mongocrypt_buffer_cleanup(&value);
    mongocrypt_status_destroy(status);
}

static void _test_mc_reader_prfblock(_mongocrypt_tester_t *tester) {
    const uint8_t expected_bytes[] = {0x12, 0x34, 0x56,
                                      0x78, // 4
                                      0x12, 0x34, 0x56,
                                      0x78, // 8
                                      0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56,
                                      0x78, // 16
                                      0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
                                      0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78};
    uint64_t expected_len = sizeof(expected_bytes);

    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_copy_from_hex(&input_buf,
                                     "12345678123456781234567812345678"
                                     "12345678123456781234567812345678");

    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &input_buf, __func__);

    _mongocrypt_buffer_t value;
    ASSERT_OK_STATUS(mc_reader_read_prfblock_buffer(&reader, &value, status), status);
    ASSERT(value.subtype == BSON_SUBTYPE_ENCRYPTED);

    ASSERT_CMPBYTES(expected_bytes, (size_t)expected_len, value.data, value.len);

    uint8_t ui;
    ASSERT_FAILS_STATUS(mc_reader_read_u8(&reader, &ui, status), status, "expected byte length >= 33 got: 32");

    _mongocrypt_buffer_cleanup(&input_buf);
    _mongocrypt_buffer_cleanup(&value);
    mongocrypt_status_destroy(status);
}

static void _test_mc_reader_ints(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_copy_from_hex(&input_buf, "001122330011223344556677");

    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &input_buf, __func__);

    uint32_t u32;
    ASSERT_OK_STATUS(mc_reader_read_u32(&reader, &u32, status), status);
    ASSERT_CMPUINT32(u32, ==, 0x33221100);

    uint64_t u64;
    ASSERT_OK_STATUS(mc_reader_read_u64(&reader, &u64, status), status);
    ASSERT_CMPUINT64(u64, ==, 0x7766554433221100ULL);

    _mongocrypt_buffer_cleanup(&input_buf);
    mongocrypt_status_destroy(status);
}

static void _test_mc_reader_bytes(_mongocrypt_tester_t *tester) {
    const uint8_t expected_bytes[] =
        {0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12};
    uint64_t expected_len = sizeof(expected_bytes);

    _mongocrypt_buffer_t input_buf;
    _mongocrypt_buffer_copy_from_hex(&input_buf, "12345678901234567890123456789012");

    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &input_buf, __func__);

    const uint8_t *ptr;
    const uint64_t len = 4;
    ASSERT_OK_STATUS(mc_reader_read_bytes(&reader, (const uint8_t **)&ptr, len, status), status);
    ASSERT_CMPBYTES(expected_bytes, 4, ptr, (size_t)len);

    _mongocrypt_buffer_t value_buf;
    ASSERT_OK_STATUS(mc_reader_read_buffer_to_end(&reader, &value_buf, status), status);
    ASSERT_CMPBYTES(expected_bytes + 4u, (size_t)expected_len - 4u, value_buf.data, value_buf.len);

    _mongocrypt_buffer_cleanup(&input_buf);
    _mongocrypt_buffer_cleanup(&value_buf);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_mc_reader(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mc_reader);
    INSTALL_TEST(_test_mc_reader_uuid);
    INSTALL_TEST(_test_mc_reader_prfblock);
    INSTALL_TEST(_test_mc_reader_ints);
    INSTALL_TEST(_test_mc_reader_bytes);
}
libmongocrypt-1.19.0/test/test-mc-schema-broker.c000066400000000000000000001544121521103432300217120ustar00rootroot00000000000000/*
 * Copyright 2018-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 "mc-schema-broker-private.h"

#include "test-mongocrypt.h"

static void test_mc_schema_broker_request(_mongocrypt_tester_t *tester) {
    // Can request.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);

        // Check listCollections filter:
        bson_t filter = BSON_INITIALIZER;
        ASSERT_OK_STATUS(mc_schema_broker_append_listCollections_filter(sb, &filter, status), status);
        ASSERT_EQUAL_BSON(TMP_BSON(BSON_STR({"name" : "coll"})), &filter);
        bson_destroy(&filter);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can request two collections.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        ASSERT(!mc_schema_broker_has_multiple_requests(sb));
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll1", status), status);
        ASSERT(!mc_schema_broker_has_multiple_requests(sb));
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_has_multiple_requests(sb));

        // Check listCollections filter:
        bson_t filter = BSON_INITIALIZER;
        ASSERT_OK_STATUS(mc_schema_broker_append_listCollections_filter(sb, &filter, status), status);
        ASSERT_EQUAL_BSON(TMP_BSON(BSON_STR({"name" : {"$in" : [ "coll1", "coll2" ]}})), &filter);
        bson_destroy(&filter);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Duplicates are ignored.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        ASSERT(!mc_schema_broker_has_multiple_requests(sb));
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll1", status), status);
        ASSERT(!mc_schema_broker_has_multiple_requests(sb));
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll1", status), status);
        ASSERT(!mc_schema_broker_has_multiple_requests(sb));

        // Check listCollections filter:
        bson_t filter = BSON_INITIALIZER;
        ASSERT_OK_STATUS(mc_schema_broker_append_listCollections_filter(sb, &filter, status), status);
        ASSERT_EQUAL_BSON(TMP_BSON(BSON_STR({"name" : "coll1"})), &filter);
        bson_destroy(&filter);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if requesting two collections on different databases.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db1", "coll1", status), status);
        ASSERT_FAILS_STATUS(mc_schema_broker_request(sb, "db2", "coll2", status),
                            status,
                            "Cannot request schemas for different databases");

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Does not include satisfied collections in listCollections filter.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll1", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);

        // Satisfy db.coll1:
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, TMP_BSON(BSON_STR({"db.coll1" : {}})), status),
                         status);

        // Check listCollections filter:
        bson_t filter = BSON_INITIALIZER;
        ASSERT_OK_STATUS(mc_schema_broker_append_listCollections_filter(sb, &filter, status), status);
        ASSERT_EQUAL_BSON(TMP_BSON(BSON_STR({"name" : "coll2"})), &filter);
        bson_destroy(&filter);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_satisfy_from_collInfo(_mongocrypt_tester_t *tester) {
    bson_t *collinfo_jsonSchema = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-jsonSchema.json");

    // Can satisfy with collinfo containing $jsonSchema.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        // Check that collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "db.coll", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(collinfo_jsonSchema, cached_collinfo);
            bson_destroy(cached_collinfo);
        }

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can satisfy with collinfo containing encryptedFields.
    {
        bson_t *collinfo_encryptedFields = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-encryptedFields.json");
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_encryptedFields, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        // Check that collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "db.coll", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(collinfo_encryptedFields, cached_collinfo);
            bson_destroy(cached_collinfo);
        }

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can satisfy with collinfo containing no schema.
    {
        bson_t *collinfo_noSchema = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-noSchema.json");
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_noSchema, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        // Check that collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "db.coll", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(collinfo_noSchema, cached_collinfo);
            bson_destroy(cached_collinfo);
        }

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if attempting to satisfy a non-requested collection.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "different", status), status);
        ASSERT_FAILS_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status),
                            status,
                            "got unexpected collinfo result");

        // Check that collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "db.coll", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(collinfo_jsonSchema, cached_collinfo);
            bson_destroy(cached_collinfo);
        }

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if attempting to satisfy an already satisfied collection.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        ASSERT_FAILS_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status),
                            status,
                            "unexpected duplicate");

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if attempting to satisfy with an empty document.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_FAILS_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, TMP_BSON("{}"), &cache, status),
                            status,
                            "failed to find 'name'");
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if attempting to satisfy with a view.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        bson_t *collinfo_view = TEST_FILE_AS_BSON("./test/data/collection-info-view.json");

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "v", status), status);
        ASSERT_FAILS_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_view, &cache, status),
                            status,
                            "cannot auto encrypt a view");
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Accepts a collinfo with siblings, like: {"$jsonSchema": {...}, "sibling": {...}}
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        bson_t *collinfo_siblings = TEST_FILE_AS_BSON("./test/data/collinfo-siblings.json");

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "test", "test", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_siblings, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        // Check that collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "test.test", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(collinfo_siblings, cached_collinfo);
            bson_destroy(cached_collinfo);
        }
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_satisfy_from_cache(_mongocrypt_tester_t *tester) {
    bson_t *collinfo = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-jsonSchema.json");

    // Can satisfy.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();

        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);
        ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "db.coll", collinfo, status), status);

        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_cache(sb, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
        _mongocrypt_cache_cleanup(&cache);
    }

    // Can satisfy with empty entry.
    {
        // An empty entry is cached when there is none on the server (e.g. the collection was not created on the server)
        mongocrypt_status_t *status = mongocrypt_status_new();

        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);
        ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "db.coll", TMP_BSON("{}"), status), status);

        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_cache(sb, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
        _mongocrypt_cache_cleanup(&cache);
    }

    // Ignores if no entry.
    {
        // An empty entry is cached when there is none on the server (e.g. the collection was not created on the server)
        mongocrypt_status_t *status = mongocrypt_status_new();

        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);
        ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "db.coll2", TMP_BSON("{}"), status), status);

        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_cache(sb, &cache, status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb)); // db.coll still not satisfied.

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
        _mongocrypt_cache_cleanup(&cache);
    }

    // Ignores already satisfied entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();

        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);
        ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "db.coll", collinfo, status), status);

        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        // Satisfy again. No error.
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_cache(sb, &cache, status), status);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
        _mongocrypt_cache_cleanup(&cache);
    }
}

static void test_mc_schema_broker_satisfy_from_schemaMap(_mongocrypt_tester_t *tester) {
    bson_t *schemaMap = TEST_FILE_AS_BSON("./test/data/schema-broker/schemaMap.json");

    // Can satisfy.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can satisfy multiple.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Does not satisfy with non-matching entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, TMP_BSON("{'db.foo': {}}"), status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb)); // Still not satisfied.

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can satisfy with empty entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, TMP_BSON("{'db.coll': {}}"), status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Ignores already satisfied entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        // Satisfy again. No error.
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_satisfy_from_encryptedFieldsMap(_mongocrypt_tester_t *tester) {
    bson_t *encryptedFieldsMap = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFieldsMap.json");

    // Can satisfy.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can satisfy multiple.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Does not satisfy with non-matching entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, TMP_BSON("{'db.foo': {}}"), status),
                         status);
        ASSERT(mc_schema_broker_need_more_schemas(sb)); // Still not satisfied.

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Ignores already satisfied entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        // Satisfy again. No error.
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Fails to satisfy with empty entry.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_FAILS_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, TMP_BSON("{'db.coll': {}}"), status),
                            status,
                            "unable to find 'fields'");

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_satisfy_remaining_with_empty_schemas(_mongocrypt_tester_t *tester) {
    // Can satisfy.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        // Check that empty collinfo is cached.
        {
            bson_t *cached_collinfo;
            ASSERT(_mongocrypt_cache_get(&cache, "db.coll", (void **)&cached_collinfo));
            ASSERT(cached_collinfo);
            ASSERT_EQUAL_BSON(TMP_BSON("{}"), cached_collinfo);
            bson_destroy(cached_collinfo);
        }

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_add_schemas_to_cmd(_mongocrypt_tester_t *tester) {
    bson_t *schemaMap = TEST_FILE_AS_BSON("./test/data/schema-broker/schemaMap.json");
    bson_t *jsonSchema = TEST_FILE_AS_BSON("./test/data/schema-broker/jsonSchema.json");
    bson_t *jsonSchema2 = TEST_FILE_AS_BSON("./test/data/schema-broker/jsonSchema2.json");
    bson_t *collinfo_jsonSchema = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-jsonSchema.json");
    bson_t *encryptedFields = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFields.json");
    bson_t *encryptedFields2 = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFields2.json");
    bson_t *encryptedFieldsMap = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFieldsMap.json");
    bson_t *collinfo_encryptedFields2 = TEST_FILE_AS_BSON("./test/data/schema-broker/collinfo-encryptedFields2.json");

    // Adds one JSON schema as jsonSchema.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect =
            TMP_BSONF(BSON_STR({"find" : "coll", "jsonSchema" : MC_BSON, "isRemoteSchema" : false}), jsonSchema);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds multiple JSON schemas as csfleEncryptionSchemas.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect = TMP_BSONF(BSON_STR({
                                       "find" : "coll",
                                       "csfleEncryptionSchemas" : {
                                           "db.coll" : {"jsonSchema" : MC_BSON, "isRemoteSchema" : false},
                                           "db.coll2" : {"jsonSchema" : MC_BSON, "isRemoteSchema" : false}
                                       }
                                   }),
                                   jsonSchema,
                                   jsonSchema2);

        ASSERT_EQUAL_BSON(expect, cmd);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds an empty 'jsonSchema' when no schema is present.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect = TMP_BSON(BSON_STR({"find" : "coll", "jsonSchema" : {}, "isRemoteSchema" : true}));
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds empty JSON schema in 'csfleEncryptionSchemas' when one collection has a JSON schema and other does not.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        // Satisfy db.coll with a schema:
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        // Satisfy db.coll2 with empty schema.
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect = TMP_BSONF(BSON_STR({
                                       "find" : "coll",
                                       "csfleEncryptionSchemas" : {
                                           "db.coll" : {"jsonSchema" : MC_BSON, "isRemoteSchema" : true},
                                           "db.coll2" : {"jsonSchema" : {}, "isRemoteSchema" : false}
                                       }
                                   }),
                                   jsonSchema);
        ASSERT_EQUAL_BSON(expect, cmd);

        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds empty JSON schemas within 'csfleEncryptionSchemas' when no schema is present on any collection.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect = TMP_BSON(BSON_STR({
            "find" : "coll",
            "csfleEncryptionSchemas" : {
                "db.coll" : {"jsonSchema" : {}, "isRemoteSchema" : false},
                "db.coll2" : {"jsonSchema" : {}, "isRemoteSchema" : false}
            }
        }));
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Errors if mixing JSON schema with QE schema and server does not support mixing.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        // Satisfy db.coll with a JSON schema:
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        // Satisfy db.coll2 with an encryptedFields:
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_encryptedFields2, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_FAILS_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                            status,
                            "'coll2' has an encryptedFields configured, but collection 'coll' has a JSON schema");
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Can mix JSON schema with QE schema if server supports mixing.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        mc_schema_broker_support_mixing_schemas(sb);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        // Satisfy db.coll with a CSFLE schema (JSON schema):
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        // Satisfy db.coll2 with a QE schema (encryptedFields):
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_encryptedFields2, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        // Expect command has both 'encryptionInformation' and 'csfleEncryptionSchemas':
        bson_t *expect =
            TMP_BSONF(BSON_STR({
                          "find" : "coll",
                          "encryptionInformation" : {"type" : 1, "schema" : {"db.coll2" : MC_BSON}},
                          "csfleEncryptionSchemas" : {"db.coll" : {"jsonSchema" : MC_BSON, "isRemoteSchema" : true}}
                      }),
                      encryptedFields2,
                      jsonSchema);
        ASSERT_EQUAL_BSON(expect, cmd);
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // When schemas are mixed, empty schemas are included in encryptionInformation, not csfleEncryptionSchemas.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();
        _mongocrypt_cache_t cache;
        _mongocrypt_cache_collinfo_init(&cache);

        mc_schema_broker_support_mixing_schemas(sb);

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll3", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        // Satisfy db.coll with a CSFLE schema (JSON schema):
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_jsonSchema, &cache, status), status);
        // Satisfy db.coll2 with a QE schema (encryptedFields):
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_collinfo(sb, collinfo_encryptedFields2, &cache, status), status);
        // Satisfy db.coll3 with an empty schema
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, &cache, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        // Expect db.coll3 is only included in encryptionInformation, not csfleEncryptionSchemas:
        bson_t *expect =
            TMP_BSONF(BSON_STR({
                          "find" : "coll",
                          "encryptionInformation" : {
                              "type" : 1,
                              "schema" : {
                                  "db.coll2" : MC_BSON,
                                  "db.coll3" : {
                                      "escCollection" : "enxcol_.coll3.esc",
                                      "ecocCollection" : "enxcol_.coll3.ecoc",
                                      "fields" : []
                                  }
                              }
                          },
                          "csfleEncryptionSchemas" : {"db.coll" : {"jsonSchema" : MC_BSON, "isRemoteSchema" : true}}
                      }),
                      encryptedFields2,
                      jsonSchema);
        ASSERT_EQUAL_BSON(expect, cmd);
        _mongocrypt_cache_cleanup(&cache);
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds one QE schema with `encryptionInformation`.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSONF(
            BSON_STR({"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}}),
            encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds multiple QE schemas with `encryptionInformation`.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect =
            TMP_BSONF(BSON_STR({
                          "find" : "coll",
                          "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON, "db.coll2" : MC_BSON}}
                      }),
                      encryptedFields,
                      encryptedFields2);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Does not add CSFLE schemas when command is targeted for mongod/mongos.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_schemaMap(sb, schemaMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds QE schema into nsInfo for bulkWrite.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"bulkWrite" : "coll", "nsInfo" : [ {"ns" : "db.coll"} ]}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSONF(
            BSON_STR({
                "bulkWrite" : "coll",
                "nsInfo" :
                    [ {"ns" : "db.coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}} ]
            }),
            encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds empty encryptedFields when no collections have schemas and using the `bulkWrite` command.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"bulkWrite" : "coll", "nsInfo" : [ {"ns" : "db.coll"} ]}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSON(BSON_STR({
            "bulkWrite" : "coll",
            "nsInfo" : [ {
                "ns" : "db.coll",
                "encryptionInformation" : {
                    "type" : {"$numberInt" : "1"},
                    "schema" : {
                        "db.coll" : {
                            "escCollection" : "enxcol_.coll.esc",
                            "ecocCollection" : "enxcol_.coll.ecoc",
                            "fields" : []
                        }
                    }
                }
            } ]
        }));
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds at top-level for "explain" to mongocryptd.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_MONGOCRYPTD, status),
                         status);
        bson_t *expect = TMP_BSONF(BSON_STR({
                                       "explain" : {"find" : "coll"},
                                       "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}
                                   }),
                                   encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds nested in "explain" for mongod.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSONF(
            BSON_STR({
                "explain" : {"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}}
            }),
            encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds nested in "explain" for crypt_shared.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"explain" : {"find" : "coll"}}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_CRYPT_SHARED, status),
                         status);
        bson_t *expect = TMP_BSONF(
            BSON_STR({
                "explain" : {"find" : "coll", "encryptionInformation" : {"type" : 1, "schema" : {"db.coll" : MC_BSON}}}
            }),
            encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Adds empty QE schema in 'encryptionInformation' when one collection has a QE schema and other does not.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "noschema", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSONF(BSON_STR({
                                       "find" : "coll",
                                       "encryptionInformation" : {
                                           "type" : 1,
                                           "schema" : {
                                               "db.coll" : MC_BSON,
                                               "db.noschema" : {
                                                   "escCollection" : "enxcol_.noschema.esc",
                                                   "ecocCollection" : "enxcol_.noschema.ecoc",
                                                   "fields" : []
                                               }
                                           }
                                       }
                                   }),
                                   encryptedFields);
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Applies default state collections for QE schema passed in encryptedFieldsMap.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(
            mc_schema_broker_satisfy_from_encryptedFieldsMap(sb,
                                                             TMP_BSON(BSON_STR({"db.coll" : {"fields" : []}})),
                                                             status),
            status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        bson_t *cmd = TMP_BSON(BSON_STR({"find" : "coll"}));
        ASSERT_OK_STATUS(mc_schema_broker_add_schemas_to_cmd(sb, NULL, cmd, MC_CMD_SCHEMAS_FOR_SERVER, status), status);
        bson_t *expect = TMP_BSONF(BSON_STR({
            "find" : "coll",
            "encryptionInformation" : {
                "type" : 1,
                "schema" : {
                    "db.coll" :
                        {"fields" : [], "escCollection" : "enxcol_.coll.esc", "ecocCollection" : "enxcol_.coll.ecoc"}
                }
            }
        }));
        ASSERT_EQUAL_BSON(expect, cmd);

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    bson_destroy(encryptedFieldsMap);
}

static void test_mc_schema_broker_get_encryptedFields(_mongocrypt_tester_t *tester) {
    bson_t *encryptedFieldsMap = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFieldsMap.json");

    // Can find.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        const mc_EncryptedFieldConfig_t *efc = mc_schema_broker_get_encryptedFields(sb, "coll", status);
        ASSERT_OK_STATUS(efc, status);
        // Check one field of returned mc_EncryptedFieldConfig_t.
        ASSERT_STREQUAL(efc->fields->path, "encryptedIndexed");
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Reports error if not found.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));

        const mc_EncryptedFieldConfig_t *efc = mc_schema_broker_get_encryptedFields(sb, "does-not-exist", status);
        ASSERT_FAILS_STATUS(efc, status, "Expected encryptedFields");
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_satisfy_from_create_or_collMod(_mongocrypt_tester_t *tester) {
    bson_t *cmd = TEST_FILE_AS_BSON("./test/data/schema-broker/create-with-jsonSchema.json");

    // Can satisfy.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_create_or_collMod(sb, cmd, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Ignores schema for an unrequested collection.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll2", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_create_or_collMod(sb, cmd, status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb)); // Request for db.coll2 is still not satisfied.
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Ignores schema for an already-satisfied collection.
    {
        bson_t *encryptedFieldsMap = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFieldsMap.json");
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT(mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_create_or_collMod(sb, cmd, status), status);
        ASSERT(!mc_schema_broker_need_more_schemas(sb));
        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_schema_broker_has_any_qe_schemas(_mongocrypt_tester_t *tester) {
    bson_t *encryptedFieldsMap = TEST_FILE_AS_BSON("./test/data/schema-broker/encryptedFieldsMap.json");

    // Works.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_from_encryptedFieldsMap(sb, encryptedFieldsMap, status), status);
        ASSERT(mc_schema_broker_has_any_qe_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }

    // Returns false if no QE schema.
    {
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_schema_broker_t *sb = mc_schema_broker_new();

        ASSERT_OK_STATUS(mc_schema_broker_request(sb, "db", "coll", status), status);
        ASSERT_OK_STATUS(mc_schema_broker_satisfy_remaining_with_empty_schemas(sb, NULL, status), status);
        ASSERT(!mc_schema_broker_has_any_qe_schemas(sb));

        mc_schema_broker_destroy(sb);
        mongocrypt_status_destroy(status);
    }
}

void _mongocrypt_tester_install_mc_schema_broker(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mc_schema_broker_request);
    INSTALL_TEST(test_mc_schema_broker_satisfy_from_collInfo);
    INSTALL_TEST(test_mc_schema_broker_satisfy_from_cache);
    INSTALL_TEST(test_mc_schema_broker_satisfy_from_schemaMap);
    INSTALL_TEST(test_mc_schema_broker_satisfy_from_encryptedFieldsMap);
    INSTALL_TEST(test_mc_schema_broker_satisfy_remaining_with_empty_schemas);
    INSTALL_TEST(test_mc_schema_broker_add_schemas_to_cmd);
    INSTALL_TEST(test_mc_schema_broker_get_encryptedFields);
    INSTALL_TEST(test_mc_schema_broker_satisfy_from_create_or_collMod);
    INSTALL_TEST(test_mc_schema_broker_has_any_qe_schemas);
}
libmongocrypt-1.19.0/test/test-mc-text-search-str-encode.c000066400000000000000000002103351521103432300234550ustar00rootroot00000000000000/*
 * Copyright 2024-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 "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#include "mc-fle2-encryption-placeholder-private.h"
#include "mc-str-encode-string-sets-private.h"
#include "mc-text-search-str-encode-private.h"
#include "unicode/fold.h"
#include 
#include 

static uint32_t get_utf8_codepoint_length(const char *buf, uint32_t len) {
    const char *cur = buf;
    const char *end = buf + len;
    uint32_t codepoint_len = 0;
    while (cur < end) {
        cur = bson_utf8_next_char(cur);
        codepoint_len++;
    }
    return codepoint_len;
}

static uint32_t calculate_padded_length(uint32_t byte_len) {
    const uint32_t bson_overhead = 5;
    // Calculate length with CBC padding:
    uint32_t encrypted_len = _mcFLE2v2AEADAlgorithm()->get_ciphertext_len(byte_len + bson_overhead, NULL)
                           - MONGOCRYPT_IV_LEN - MONGOCRYPT_HMAC_LEN;
    return encrypted_len - bson_overhead;
}

static void test_nofold_suffix_prefix_case(_mongocrypt_tester_t *tester,
                                           const char *str,
                                           uint32_t lb,
                                           uint32_t ub,
                                           bool casef,
                                           bool diacf,
                                           int foldable_codepoints) {
    TEST_PRINTF("Testing nofold suffix/prefix case: str=\"%s\", lb=%u, ub=%u, casef=%d, diacf=%d\n",
                str,
                lb,
                ub,
                casef,
                diacf);
    uint32_t byte_len = (uint32_t)strlen(str);
    uint32_t unfolded_codepoint_len = byte_len == 0 ? 1 : get_utf8_codepoint_length(str, byte_len);
    uint32_t folded_codepoint_len = byte_len == 0 ? 0 : unfolded_codepoint_len - foldable_codepoints;
    uint32_t padded_len = calculate_padded_length(byte_len);
    uint32_t max_affix_len = BSON_MIN(ub, folded_codepoint_len);
    uint32_t n_real_affixes = max_affix_len >= lb ? max_affix_len - lb + 1 : 0;
    uint32_t n_affixes = BSON_MIN(ub, padded_len) - lb + 1;
    uint32_t n_padding = n_affixes - n_real_affixes;

    mc_str_encode_sets_t *sets;
    mongocrypt_status_t *status = mongocrypt_status_new();
    for (int suffix = 0; suffix <= 1; suffix++) {
        if (suffix) {
            mc_FLE2TextSearchInsertSpec_t spec = {.v = str,
                                                  .len = byte_len,
                                                  .suffix = {{lb, ub}, true},
                                                  .casef = casef,
                                                  .diacf = diacf};
            sets = mc_text_search_str_encode(&spec, status);
        } else {
            mc_FLE2TextSearchInsertSpec_t spec = {.v = str,
                                                  .len = byte_len,
                                                  .prefix = {{lb, ub}, true},
                                                  .casef = casef,
                                                  .diacf = diacf};
            sets = mc_text_search_str_encode(&spec, status);
        }
        ASSERT_OR_PRINT(sets, status);
        ASSERT_CMPUINT32(sets->base_string->codepoint_len, ==, folded_codepoint_len + 1);
        if (!casef && !diacf) {
            ASSERT_CMPUINT32(sets->base_string->buf.len, ==, byte_len + 1);
            ASSERT_CMPINT(0, ==, memcmp(sets->base_string->buf.data, str, byte_len));
        }
        ASSERT_CMPUINT8(sets->base_string->buf.data[sets->base_string->buf.len - 1], ==, (uint8_t)0xFF);
        ASSERT(sets->substring_set == NULL);
        ASSERT_CMPUINT32(sets->exact.len, ==, sets->base_string->buf.len - 1);
        ASSERT_CMPINT(0, ==, memcmp(sets->exact.data, sets->base_string->buf.data, sets->exact.len));

        if (lb > padded_len) {
            ASSERT(sets->suffix_set == NULL);
            ASSERT(sets->prefix_set == NULL);
            ASSERT_CMPUINT32(sets->msize, ==, 1 /* for exact string */);
            goto CONTINUE;
        }

        ASSERT_CMPUINT32(sets->msize, ==, n_affixes + 1 /* for exact string */);

        TEST_PRINTF("Expecting: n_real_affixes: %u, n_affixes: %u, n_padding: %u\n",
                    n_real_affixes,
                    n_affixes,
                    n_padding);

        mc_affix_set_t *set;
        if (suffix) {
            ASSERT(sets->prefix_set == NULL);
            set = sets->suffix_set;
        } else {
            ASSERT(sets->suffix_set == NULL);
            set = sets->prefix_set;
        }
        ASSERT(set != NULL);

        mc_affix_set_iter_t it;
        mc_affix_set_iter_init(&it, set);
        const char *affix;

        uint32_t idx = 0;
        uint32_t affix_len = 0;
        uint32_t affix_count = 0;
        uint32_t total_real_affix_count = 0;
        while (mc_affix_set_iter_next(&it, &affix, &affix_len, &affix_count)) {
            // Since all substrings are just views on the base string, we can use pointer math to find our start and
            // end indices.
            TEST_PRINTF("Affix starting %lld, ending %lld, count %u\n",
                        (long long)((uint8_t *)affix - sets->base_string->buf.data),
                        (long long)((uint8_t *)affix - sets->base_string->buf.data + affix_len),
                        affix_count);
            if (affix_len == sets->base_string->buf.len) {
                // This is padding, so there should be no more entries due to how we ordered them
                ASSERT(!mc_affix_set_iter_next(&it, NULL, NULL, NULL));
                break;
            }

            ASSERT_CMPUINT32(affix_len, <=, sets->base_string->buf.len - 1);
            ASSERT_CMPUINT32(0, <, affix_len);

            // We happen to always order from smallest to largest in the suffix/prefix algorithm, which makes our
            // life slightly easier when testing.
            if (suffix) {
                uint32_t start_offset = sets->base_string->codepoint_offsets[folded_codepoint_len - (lb + idx)];
                ASSERT_CMPPTR((uint8_t *)affix, ==, sets->base_string->buf.data + start_offset);
                ASSERT_CMPUINT32(affix_len,
                                 ==,
                                 sets->base_string->codepoint_offsets[folded_codepoint_len] - start_offset);
            } else {
                uint32_t end_offset = sets->base_string->codepoint_offsets[lb + idx];
                ASSERT_CMPPTR((uint8_t *)affix, ==, sets->base_string->buf.data);
                ASSERT_CMPUINT32(affix_len, ==, end_offset);
            }
            // The count should always be 1, except for padding.
            ASSERT_CMPUINT32(1, ==, affix_count);
            total_real_affix_count++;
            idx++;
        }
        ASSERT_CMPUINT32(total_real_affix_count, ==, n_real_affixes);
        if (affix_len == sets->base_string->buf.len) {
            // Padding
            ASSERT_CMPPTR((uint8_t *)affix, ==, sets->base_string->buf.data);
            ASSERT_CMPUINT32(affix_count, ==, n_padding);
        } else {
            // No padding found
            ASSERT_CMPUINT32(n_padding, ==, 0);
        }
    CONTINUE:
        mc_str_encode_sets_destroy(sets);
    }
    mongocrypt_status_destroy(status);
}

static uint32_t calc_number_of_substrings(uint32_t len, uint32_t lb, uint32_t ub) {
    uint32_t ret = 0;
    // Calculate the long way to make sure our math in calc_number_of_substrings is correct
    for (uint32_t i = 0; i < len; i++) {
        uint32_t max_sublen = BSON_MIN(ub, len - i);
        uint32_t n_substrings = max_sublen < lb ? 0 : max_sublen - lb + 1;
        ret += n_substrings;
    }
    return ret;
}

static uint32_t calc_unique_substrings(const mc_utf8_string_with_bad_char_t *str, uint32_t lb, uint32_t ub) {
    uint32_t len = str->codepoint_len - 1; // eliminate last 0xff CP
    if (len < lb) {
        return 0;
    }
    // Bruteforce to make sure our hashset is working as expected.
    uint8_t *idx_is_dupe = bson_malloc0(len);
    uint32_t dupes = 0;
    for (uint32_t ss_len = lb; ss_len <= BSON_MIN(len, ub); ss_len++) {
        for (uint32_t i = 0; i < len - ss_len; i++) {
            // Already checked
            if (idx_is_dupe[i]) {
                continue;
            }
            for (uint32_t j = i + 1; j <= len - ss_len; j++) {
                // Already counted
                if (idx_is_dupe[j]) {
                    continue;
                }
                uint32_t i_start_byte = str->codepoint_offsets[i];
                uint32_t i_end_byte = str->codepoint_offsets[i + ss_len];
                uint32_t j_start_byte = str->codepoint_offsets[j];
                uint32_t j_end_byte = str->codepoint_offsets[j + ss_len];
                if (i_end_byte - i_start_byte == j_end_byte - j_start_byte
                    && memcmp(&str->buf.data[i_start_byte], &str->buf.data[j_start_byte], i_end_byte - i_start_byte)
                           == 0) {
                    idx_is_dupe[j] = 1;
                    dupes++;
                }
            }
        }
        memset(idx_is_dupe, 0, len);
    }
    bson_free(idx_is_dupe);
    return calc_number_of_substrings(len, lb, ub) - dupes;
}

static void test_nofold_substring_case(_mongocrypt_tester_t *tester,
                                       const char *str,
                                       uint32_t lb,
                                       uint32_t ub,
                                       uint32_t mlen,
                                       bool casef,
                                       bool diacf,
                                       int foldable_codepoints) {
    TEST_PRINTF("Testing nofold substring case: str=\"%s\", lb=%u, ub=%u, mlen=%u, casef=%d, diacf=%d\n",
                str,
                lb,
                ub,
                mlen,
                casef,
                diacf);
    uint32_t byte_len = (uint32_t)strlen(str);
    uint32_t unfolded_codepoint_len = byte_len == 0 ? 1 : get_utf8_codepoint_length(str, byte_len);
    uint32_t folded_codepoint_len = byte_len == 0 ? 0 : unfolded_codepoint_len - foldable_codepoints;
    uint32_t padded_len = calculate_padded_length(byte_len);
    uint32_t n_substrings = calc_number_of_substrings(BSON_MIN(padded_len, mlen), lb, ub);

    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_str_encode_sets_t *sets;
    mc_FLE2TextSearchInsertSpec_t spec = {.v = str,
                                          .len = byte_len,
                                          .substr = {{mlen, lb, ub}, true},
                                          .casef = casef,
                                          .diacf = diacf};
    sets = mc_text_search_str_encode(&spec, status);
    if (unfolded_codepoint_len > mlen) {
        ASSERT_FAILS_STATUS(sets, status, "longer than the maximum length");
        mongocrypt_status_destroy(status);
        return;
    }
    ASSERT_OR_PRINT(sets, status);
    mongocrypt_status_destroy(status);
    ASSERT_CMPUINT32(sets->base_string->codepoint_len, ==, folded_codepoint_len + 1);
    if (!casef && !diacf) {
        ASSERT_CMPUINT32(sets->base_string->buf.len, ==, byte_len + 1);
        ASSERT_CMPINT(0, ==, memcmp(sets->base_string->buf.data, str, byte_len));
    }

    ASSERT_CMPUINT8(sets->base_string->buf.data[sets->base_string->buf.len - 1], ==, (uint8_t)0xFF);
    ASSERT(sets->suffix_set == NULL);
    ASSERT(sets->prefix_set == NULL);
    ASSERT_CMPUINT32(sets->exact.len, ==, sets->base_string->buf.len - 1);
    ASSERT_CMPINT(0, ==, memcmp(sets->exact.data, sets->base_string->buf.data, sets->base_string->buf.len - 1));

    if (lb > padded_len) {
        ASSERT(sets->substring_set == NULL);
        ASSERT_CMPUINT32(sets->msize, ==, 1 /* for exact string */);
        goto cleanup;
    } else {
        ASSERT(sets->substring_set != NULL);
    }

    ASSERT_CMPUINT32(sets->msize, ==, n_substrings + 1 /* for exact string */);

    uint32_t n_real_substrings = calc_unique_substrings(sets->base_string, lb, ub);
    uint32_t n_padding = n_substrings - n_real_substrings;

    TEST_PRINTF("Expecting: n_real_substrings: %u, n_substrings: %u, n_padding: %u\n",
                n_real_substrings,
                n_substrings,
                n_padding);

    mc_substring_set_t *set = sets->substring_set;
    mc_substring_set_iter_t it;
    mc_substring_set_iter_init(&it, set);
    const char *substring;

    uint32_t substring_len = 0;
    uint32_t substring_count = 0;
    uint32_t total_real_substring_count = 0;
    while (mc_substring_set_iter_next(&it, &substring, &substring_len, &substring_count)) {
        TEST_PRINTF("Substring starting %lld, ending %lld, count %u: \"%.*s\"\n",
                    (long long)((uint8_t *)substring - sets->base_string->buf.data),
                    (long long)((uint8_t *)substring - sets->base_string->buf.data + substring_len),
                    substring_count,
                    substring_len,
                    substring);
        if (substring_len == sets->base_string->buf.len) {
            // This is padding, so there should be no more entries due to how we ordered them
            ASSERT(!mc_substring_set_iter_next(&it, NULL, NULL, NULL));
            break;
        }

        ASSERT_CMPPTR((uint8_t *)substring + substring_len,
                      <=,
                      sets->base_string->buf.data + sets->base_string->buf.len);
        ASSERT_CMPUINT32(substring_len, <=, sets->base_string->buf.len - 1);
        ASSERT_CMPUINT32(0, <, substring_len);
        ASSERT_CMPUINT32(1, ==, substring_count);
        total_real_substring_count++;
    }
    ASSERT_CMPUINT32(total_real_substring_count, ==, n_real_substrings);
    if (substring_len == sets->base_string->buf.len) {
        // Padding
        ASSERT_CMPPTR((uint8_t *)substring, ==, sets->base_string->buf.data);
        ASSERT_CMPUINT32(substring_count, ==, n_padding);
    } else {
        // No padding found
        ASSERT_CMPUINT32(n_padding, ==, 0);
    }
cleanup:
    mc_str_encode_sets_destroy(sets);
}

static void test_nofold_substring_case_multiple_mlen(_mongocrypt_tester_t *tester,
                                                     const char *str,
                                                     uint32_t lb,
                                                     uint32_t ub,
                                                     uint32_t unfolded_codepoint_len,
                                                     bool casef,
                                                     bool diacf,
                                                     int foldable_codepoints) {
    if (unfolded_codepoint_len > 1) {
        // mlen < unfolded_codepoint_len
        test_nofold_substring_case(tester, str, lb, ub, unfolded_codepoint_len - 1, casef, diacf, foldable_codepoints);
    }
    // mlen = unfolded_codepoint_len
    test_nofold_substring_case(tester, str, lb, ub, unfolded_codepoint_len, casef, diacf, foldable_codepoints);
    // mlen > unfolded_codepoint_len
    test_nofold_substring_case(tester, str, lb, ub, unfolded_codepoint_len + 1, casef, diacf, foldable_codepoints);
    // mlen >> unfolded_codepoint_len
    test_nofold_substring_case(tester, str, lb, ub, unfolded_codepoint_len + 64, casef, diacf, foldable_codepoints);

    uint32_t byte_len = (uint32_t)strlen(str);
    if (byte_len > 1) {
        // mlen < byte_len
        test_nofold_substring_case(tester, str, lb, ub, byte_len - 1, casef, diacf, foldable_codepoints);
    }
    if (byte_len > 0) {
        // mlen = byte_len
        test_nofold_substring_case(tester, str, lb, ub, byte_len, casef, diacf, foldable_codepoints);
    }
    // mlen > byte_len
    test_nofold_substring_case(tester, str, lb, ub, byte_len + 1, casef, diacf, foldable_codepoints);
    // mlen = padded_len
    test_nofold_substring_case(tester,
                               str,
                               lb,
                               ub,
                               16 * (uint32_t)((byte_len + 5 + 15) / 16) - 5,
                               casef,
                               diacf,
                               foldable_codepoints);
    // mlen >> byte_len
    test_nofold_substring_case(tester, str, lb, ub, byte_len + 64, casef, diacf, foldable_codepoints);
}

static const char *normal_ascii_strings[] = {
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
    "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F",
    "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
static const char *ascii_diacritics[] = {"^", "`"};
static const char *normal_unicode_strings[] = {"ã", "ã‚", "ãƒ", "ã„", "ã…", "ã†", "ã‡", "ãˆ", "ã‰", "ãŠ", "ã‹", "ãŒ",
                                               "ã", "ãŽ", "ã", "ã‘", "Ѐ",  "Ð",  "Ђ",  "Ѓ",  "Є",  "Ð…",  "І",  "Ї",
                                               "Ј",  "Љ",  "Њ",  "Ћ",  "ÐŒ",  "Ð",  "ÐŽ",  "Ð",  "ð“€€",  "ð“€",  "𓀂",  "𓀃",
                                               "𓀄",  "ð“€…",  "𓀆",  "𓀇",  "𓀈",  "𓀉",  "𓀊",  "𓀋",  "𓀌",  "ð“€",  "𓀎",  "ð“€"};
static const char *unicode_diacritics[] = {"Ì€", "Ì", "Ì‚", "̃", "Ì„", "Ì…",  "̆",  "̇",  "̈",  "̉",  "ÌŠ",  "Ì‹",  "ÌŒ",  "Ì", "ÌŽ",
                                           "Ì", "á·„", "á·…", "á·†", "á·‡", "á·ˆ",  "á·‰",  "á·Š",  "á·‹",  "á·Œ",  "á·",  "á·Ž",  "á·",  "︠", "︡",
                                           "︢", "︣", "︤", "︥", "︦", "︧", "︨", "︩", "︪", "︫", "︬", "︭", "︮", "︯"};

// Build a random string which has unfolded_len codepoints, but folds to folded_len codepoints after diacritic folding.
static char *build_random_string_to_fold(uint32_t folded_len, uint32_t unfolded_len) {
    // 1/3 to generate all unicode, 1/3 to be half and half, 1/3 to be all ascii.
    int ascii_ratio = rand() % 3;
    ASSERT_CMPUINT32(unfolded_len, >=, folded_len);
    // Max size in bytes is # unicode characters * 4 bytes for each character + 1 null terminator.
    char *str = malloc(unfolded_len * 4 + 1);
    char *ptr = str;
    uint32_t folded_size = 0;
    uint32_t diacritics = unfolded_len - folded_len;
    int dia_prob = (diacritics * 1000) / unfolded_len;
    for (uint32_t n_codepoints = 0; n_codepoints < unfolded_len; n_codepoints++) {
        const char *src_ptr;
        bool must_add_diacritic = folded_size == folded_len;
        bool must_add_normal = n_codepoints - folded_size == diacritics;
        if (must_add_diacritic || (!must_add_normal && (rand() % 1000 < dia_prob))) {
            // Add diacritic.
            if (rand() % 2 < ascii_ratio) {
                int i = rand() % (sizeof(ascii_diacritics) / sizeof(char *));
                src_ptr = ascii_diacritics[i];
            } else {
                int i = rand() % (sizeof(unicode_diacritics) / sizeof(char *));
                src_ptr = unicode_diacritics[i];
            }
        } else {
            // Add normal character.
            if (rand() % 2 < ascii_ratio) {
                int i = rand() % (sizeof(normal_ascii_strings) / sizeof(char *));
                src_ptr = normal_ascii_strings[i];
            } else {
                int i = rand() % (sizeof(normal_unicode_strings) / sizeof(char *));
                src_ptr = normal_unicode_strings[i];
            }
            folded_size++;
        }
        strcpy(ptr, src_ptr);
        ptr += strlen(src_ptr);
    }

    uint32_t len = (uint32_t)(ptr - str);
    // ptr points to the final null character, include that in the final string.
    str = realloc(str, len + 1);
    ASSERT(str);

    // Make sure we did everything right.
    ASSERT_CMPUINT32(unfolded_len, ==, get_utf8_codepoint_length(str, len));
    mongocrypt_status_t *status = mongocrypt_status_new();
    char *out_str;
    size_t out_len;
    ASSERT_OK_STATUS(unicode_fold(str, len, kUnicodeFoldRemoveDiacritics, &out_str, &out_len, status), status);
    ASSERT_CMPUINT32(folded_len, ==, get_utf8_codepoint_length(out_str, (uint32_t)out_len));
    bson_free(out_str);
    mongocrypt_status_destroy(status);
    return str;
}

static void suffix_prefix_run_folding_case(_mongocrypt_tester_t *tester,
                                           const char *short_s,
                                           const char *medium_s,
                                           const char *long_s,
                                           bool casef,
                                           bool diacf,
                                           int foldable_codepoints) {
    // LB > 16
    test_nofold_suffix_prefix_case(tester, short_s, 17, 19, casef, diacf, foldable_codepoints);
    // Simple cases
    test_nofold_suffix_prefix_case(tester, short_s, 2, 4, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 3, 6, casef, diacf, foldable_codepoints);
    // LB = UB
    test_nofold_suffix_prefix_case(tester, short_s, 2, 2, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 9, 9, casef, diacf, foldable_codepoints);
    // UB = len
    test_nofold_suffix_prefix_case(tester, short_s, 2, 9, casef, diacf, foldable_codepoints);
    // 16 > UB > len
    test_nofold_suffix_prefix_case(tester, short_s, 2, 14, casef, diacf, foldable_codepoints);
    // UB = 16
    test_nofold_suffix_prefix_case(tester, short_s, 2, 16, casef, diacf, foldable_codepoints);
    // UB > 16
    test_nofold_suffix_prefix_case(tester, short_s, 2, 19, casef, diacf, foldable_codepoints);
    // UB > 32
    test_nofold_suffix_prefix_case(tester, short_s, 2, 35, casef, diacf, foldable_codepoints);
    // 16 >= LB > len
    test_nofold_suffix_prefix_case(tester, short_s, 12, 19, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 12, 16, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 16, 19, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 12, 35, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, short_s, 16, 35, casef, diacf, foldable_codepoints);

    // len = 16 cases
    // LB > 16
    test_nofold_suffix_prefix_case(tester, medium_s, 17, 19, casef, diacf, foldable_codepoints);
    // Simple cases
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 4, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, medium_s, 3, 6, casef, diacf, foldable_codepoints);
    // LB = UB
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 2, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, medium_s, 16, 16, casef, diacf, foldable_codepoints);
    // UB = len
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 16, casef, diacf, foldable_codepoints);
    // UB > len
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 19, casef, diacf, foldable_codepoints);
    // UB = 32
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 32, casef, diacf, foldable_codepoints);
    // UB > 32
    test_nofold_suffix_prefix_case(tester, medium_s, 2, 35, casef, diacf, foldable_codepoints);
    // LB = len
    test_nofold_suffix_prefix_case(tester, medium_s, 16, 19, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, medium_s, 16, 35, casef, diacf, foldable_codepoints);

    // len > 16 cases
    // LB > 32
    test_nofold_suffix_prefix_case(tester, long_s, 33, 38, casef, diacf, foldable_codepoints);
    // Simple cases
    test_nofold_suffix_prefix_case(tester, long_s, 2, 4, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 3, 6, casef, diacf, foldable_codepoints);
    // LB < 16 <= UB <= len
    test_nofold_suffix_prefix_case(tester, long_s, 3, 18, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 3, 16, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 3, 27, casef, diacf, foldable_codepoints);
    // 16 <= LB < UB <= len
    test_nofold_suffix_prefix_case(tester, long_s, 18, 24, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 16, 24, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 18, 27, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 16, 27, casef, diacf, foldable_codepoints);
    // LB = UB
    test_nofold_suffix_prefix_case(tester, long_s, 3, 3, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 16, 16, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 27, 27, casef, diacf, foldable_codepoints);
    // 32 > UB > len
    test_nofold_suffix_prefix_case(tester, long_s, 3, 29, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 18, 29, casef, diacf, foldable_codepoints);
    // UB = 32
    test_nofold_suffix_prefix_case(tester, long_s, 3, 32, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 18, 32, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 27, 32, casef, diacf, foldable_codepoints);
    // UB > 32
    test_nofold_suffix_prefix_case(tester, long_s, 3, 35, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 18, 35, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 27, 32, casef, diacf, foldable_codepoints);
    // UB > 48
    test_nofold_suffix_prefix_case(tester, long_s, 3, 49, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 18, 49, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 27, 32, casef, diacf, foldable_codepoints);
    // 32 >= LB > len
    test_nofold_suffix_prefix_case(tester, long_s, 28, 30, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 28, 28, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 28, 32, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 28, 34, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 28, 49, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 32, 32, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 32, 34, casef, diacf, foldable_codepoints);
    test_nofold_suffix_prefix_case(tester, long_s, 32, 49, casef, diacf, foldable_codepoints);
}

static const uint32_t UNFOLDED_CASES[] = {0, 1, 3, 16};
// Predefined lengths to test a variety of cases
static const uint32_t SHORT_LEN = 9;
static const uint32_t MEDIUM_LEN = 16;
static const uint32_t LONG_LEN = 27;

static void _test_text_search_str_encode_suffix_prefix(_mongocrypt_tester_t *tester) {
    unsigned int seed = (unsigned int)time(0);
    TEST_PRINTF("Testing with seed: %u", seed);
    srand(seed);
    // Run diacritic folding and case+diacritic folding for a variety of folded/unfolded sizes.
    for (uint32_t i = 0; i < sizeof(UNFOLDED_CASES) / sizeof(UNFOLDED_CASES[0]); i++) {
        char *short_s = build_random_string_to_fold(SHORT_LEN, SHORT_LEN + UNFOLDED_CASES[i]);
        char *medium_s = build_random_string_to_fold(MEDIUM_LEN, MEDIUM_LEN + UNFOLDED_CASES[i]);
        char *long_s = build_random_string_to_fold(LONG_LEN, LONG_LEN + UNFOLDED_CASES[i]);
        for (int casef = 0; casef <= 1; casef++) {
            suffix_prefix_run_folding_case(tester,
                                           short_s,
                                           medium_s,
                                           long_s,
                                           casef,
                                           true /* diacf */,
                                           UNFOLDED_CASES[i]);
        }
        bson_free(short_s);
        bson_free(medium_s);
        bson_free(long_s);
    }
    // Run case folding and no folding for different sizes. Only unfolded size matters.
    char *short_s = build_random_string_to_fold(SHORT_LEN, SHORT_LEN);
    char *medium_s = build_random_string_to_fold(MEDIUM_LEN, MEDIUM_LEN);
    char *long_s = build_random_string_to_fold(LONG_LEN, LONG_LEN);
    for (int casef = 0; casef <= 1; casef++) {
        suffix_prefix_run_folding_case(tester, short_s, medium_s, long_s, casef, false /* diacf*/, 0);
    }
    bson_free(short_s);
    bson_free(medium_s);
    bson_free(long_s);

    // Test fixed strings where byte_len+5 is a multiple of 16. Regression test for MONGOCRYPT-917
    test_nofold_suffix_prefix_case(tester, "abcdefghijk" /* 11 chars */, 1, 30, false, false, 0);
}

static void substring_run_folding_case(_mongocrypt_tester_t *tester,
                                       const char *short_s,
                                       uint32_t short_unfolded_codepoint_len,
                                       const char *medium_s,
                                       uint32_t medium_unfolded_codepoint_len,
                                       const char *long_s,
                                       uint32_t long_unfolded_codepoint_len,
                                       bool casef,
                                       bool diacf,
                                       int foldable_codepoints) {
    // LB > 16
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             17,
                                             19,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // Simple cases
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             4,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             3,
                                             6,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // LB = UB
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             2,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             9,
                                             9,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB = len
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             9,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // 16 > UB > len
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             14,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB = 16
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             16,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > 16
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             19,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             2,
                                             35,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // 16 >= LB > len
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             12,
                                             19,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             12,
                                             16,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             16,
                                             19,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             12,
                                             35,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             short_s,
                                             16,
                                             35,
                                             short_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);

    // len = 16 cases
    // LB > 16
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             17,
                                             19,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // Simple cases
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             4,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             3,
                                             6,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // LB = UB
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             2,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             16,
                                             16,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB = len
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             16,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > len
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             19,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB = 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             32,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             2,
                                             35,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // LB = len
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             16,
                                             19,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             medium_s,
                                             16,
                                             35,
                                             medium_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);

    // len > 16 cases
    // LB > 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             33,
                                             38,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // Simple cases
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             2,
                                             4,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             6,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // LB < 16 <= UB <= len
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             18,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             16,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             27,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // 16 <= LB < UB <= len
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             24,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             16,
                                             24,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             27,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             16,
                                             27,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // LB = UB
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             3,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             16,
                                             16,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             27,
                                             27,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // 32 > UB > len
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             29,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             29,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB = 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             27,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > 32
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             35,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             35,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             27,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // UB > 48
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             3,
                                             49,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             18,
                                             49,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             27,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    // 32 >= LB > len
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             28,
                                             30,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             28,
                                             28,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             28,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             28,
                                             34,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             28,
                                             49,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             32,
                                             32,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             32,
                                             34,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
    test_nofold_substring_case_multiple_mlen(tester,
                                             long_s,
                                             32,
                                             49,
                                             long_unfolded_codepoint_len,
                                             casef,
                                             diacf,
                                             foldable_codepoints);
}

static void _test_text_search_str_encode_substring(_mongocrypt_tester_t *tester) {
    unsigned int seed = (unsigned int)time(0);
    TEST_PRINTF("Testing with seed: %u", seed);
    srand(seed);
    // Run diacritic folding and case+diacritic folding for a variety of folded/unfolded sizes.
    for (uint32_t i = 0; i < sizeof(UNFOLDED_CASES) / sizeof(UNFOLDED_CASES[0]); i++) {
        char *short_s = build_random_string_to_fold(SHORT_LEN, SHORT_LEN + UNFOLDED_CASES[i]);
        char *medium_s = build_random_string_to_fold(MEDIUM_LEN, MEDIUM_LEN + UNFOLDED_CASES[i]);
        char *long_s = build_random_string_to_fold(LONG_LEN, LONG_LEN + UNFOLDED_CASES[i]);
        for (int casef = 0; casef <= 1; casef++) {
            substring_run_folding_case(tester,
                                       short_s,
                                       SHORT_LEN + UNFOLDED_CASES[i],
                                       medium_s,
                                       MEDIUM_LEN + UNFOLDED_CASES[i],
                                       long_s,
                                       LONG_LEN + UNFOLDED_CASES[i],
                                       casef,
                                       true /* diacf */,
                                       UNFOLDED_CASES[i]);
        }
        bson_free(short_s);
        bson_free(medium_s);
        bson_free(long_s);
    }
    // Run case folding and no folding for different sizes. Only unfolded size matters.
    char *short_s = build_random_string_to_fold(SHORT_LEN, SHORT_LEN);
    char *medium_s = build_random_string_to_fold(MEDIUM_LEN, MEDIUM_LEN);
    char *long_s = build_random_string_to_fold(LONG_LEN, LONG_LEN);
    for (int casef = 0; casef <= 1; casef++) {
        substring_run_folding_case(tester,
                                   short_s,
                                   SHORT_LEN,
                                   medium_s,
                                   MEDIUM_LEN,
                                   long_s,
                                   LONG_LEN,
                                   casef,
                                   false /* diacf */,
                                   0);
    }
    bson_free(short_s);
    bson_free(medium_s);
    bson_free(long_s);

    // Test fixed strings where byte_len+5 is a multiple of 16. Regression test for MONGOCRYPT-917.
    test_nofold_substring_case(tester, "abcdefghijk" /* 11 chars */, 1, 30, 16, false, false, 0);
}

static void _test_text_search_str_encode_multiple(_mongocrypt_tester_t *tester) {
    mc_FLE2TextSearchInsertSpec_t spec = {.v = "123456789",
                                          .len = 9,
                                          .substr = {{20, 9, 9}, true},
                                          .suffix = {{1, 5}, true},
                                          .prefix = {{6, 8}, true}};
    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_str_encode_sets_t *sets = mc_text_search_str_encode(&spec, status);
    // Ensure that we ran tree generation for suffix, prefix, and substring successfully by checking the first entry of
    // each.
    const char *str;
    uint32_t len, count;

    ASSERT_OR_PRINT(sets, status);
    mongocrypt_status_destroy(status);
    ASSERT(sets->suffix_set != NULL);
    mc_affix_set_iter_t it;
    mc_affix_set_iter_init(&it, sets->suffix_set);
    ASSERT(mc_affix_set_iter_next(&it, &str, &len, &count));
    ASSERT_CMPUINT32(len, ==, 1);
    ASSERT_CMPUINT8((uint8_t)*str, ==, (uint8_t)'9');
    ASSERT_CMPUINT32(count, ==, 1);

    ASSERT(sets->prefix_set != NULL);
    mc_affix_set_iter_init(&it, sets->prefix_set);
    ASSERT(mc_affix_set_iter_next(&it, &str, &len, &count));
    ASSERT_CMPUINT32(len, ==, 6);
    ASSERT_CMPINT(0, ==, memcmp("123456", str, 6));
    ASSERT_CMPUINT32(count, ==, 1);

    ASSERT(sets->substring_set != NULL);
    mc_substring_set_iter_t ss_it;
    mc_substring_set_iter_init(&ss_it, sets->substring_set);
    ASSERT(mc_substring_set_iter_next(&ss_it, &str, &len, &count));
    ASSERT_CMPUINT32(len, ==, 9);
    ASSERT_CMPINT(0, ==, memcmp("123456789", str, 9));
    ASSERT_CMPUINT32(count, ==, 1);

    ASSERT_CMPUINT32(sets->exact.len, ==, 9);
    ASSERT_CMPINT(0, ==, memcmp(sets->exact.data, str, 9));

    ASSERT_CMPUINT32(sets->msize, ==, 1 + 3 + 5 + 3); /* exact + substring + suffix + prefix */

    mc_str_encode_sets_destroy(sets);
}

static void _test_text_search_str_encode_bad_string(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_FLE2TextSearchInsertSpec_t spec = {.v = "\xff\xff\xff\xff\xff\xff\xff\xff\xff",
                                          .len = 9,
                                          .substr = {{20, 4, 7}, true},
                                          .suffix = {{1, 5}, true},
                                          .prefix = {{6, 8}, true}};
    mc_str_encode_sets_t *sets = mc_text_search_str_encode(&spec, status);
    ASSERT_FAILS_STATUS(sets, status, "not valid UTF-8");
    mc_str_encode_sets_destroy(sets);
    mongocrypt_status_destroy(status);
}

static void _test_text_search_str_encode_empty_string(_mongocrypt_tester_t *tester) {
    for (int casef = 0; casef <= 1; casef++) {
        for (int diacf = 0; diacf <= 1; diacf++) {
            test_nofold_suffix_prefix_case(tester, "", 1, 1, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 1, 2, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 2, 3, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 1, 16, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 1, 17, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 2, 16, casef, diacf, 0);
            test_nofold_suffix_prefix_case(tester, "", 2, 17, casef, diacf, 0);

            test_nofold_substring_case_multiple_mlen(tester, "", 1, 1, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 1, 2, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 2, 3, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 1, 16, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 1, 17, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 2, 16, 1, casef, diacf, 0);
            test_nofold_substring_case_multiple_mlen(tester, "", 2, 17, 1, casef, diacf, 0);
        }
    }
}

// Tests mc_text_search_str_query() fails on invalid utf-8.
static void _test_text_search_str_query_bad_string(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_buffer_t out;
    mc_FLE2TextSearchInsertSpec_t spec = {.v = "\xff\xff\xff\xff\xff\xff\xff\xff\xff", .len = 9};

    bool res = mc_text_search_str_query(&spec, &out, status);
    ASSERT_FAILS_STATUS(res, status, "not valid UTF-8");
    mongocrypt_status_destroy(status);
}

// Tests mc_text_search_str_query() checks the input string codepoint length against lb and ub
// for substring, suffix, and prefix queries, and not for exact queres.
static void _test_text_search_str_query_bounds_checking(_mongocrypt_tester_t *tester) {
    mc_FLE2TextSearchInsertSpec_t substrSpec = {.substr = {{.mlen = 20, .lb = 4, .ub = 7}, .set = true}};
    mc_FLE2TextSearchInsertSpec_t suffixSpec = {.suffix = {{.lb = 4, .ub = 7}, .set = true}};
    mc_FLE2TextSearchInsertSpec_t prefixSpec = {.prefix = {{.lb = 4, .ub = 7}, .set = true}};
    mc_FLE2TextSearchInsertSpec_t *specs[3] = {&substrSpec, &suffixSpec, &prefixSpec};

    char *short_str = build_random_string_to_fold(3, 5);
    char *long_str = build_random_string_to_fold(8, 10);

    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_buffer_t out;

    for (int i = 0; i < 3; i++) {
        // long_str always fails regardless of folding
        for (int d = 0; d < 2; d++) {
            specs[i]->v = long_str;
            specs[i]->len = (uint32_t)strlen(specs[i]->v);
            specs[i]->diacf = d;
            ASSERT_FAILS_STATUS(mc_text_search_str_query(specs[i], &out, status),
                                status,
                                "longer than the maximum query length");
            _mongocrypt_buffer_cleanup(&out);
            _mongocrypt_status_reset(status);
        }
        // short_str only fails if diacritic folding is on
        specs[i]->v = short_str;
        specs[i]->len = (uint32_t)strlen(specs[i]->v);
        specs[i]->diacf = true;
        ASSERT_FAILS_STATUS(mc_text_search_str_query(specs[i], &out, status),
                            status,
                            "shorter than the minimum query length");
        _mongocrypt_buffer_cleanup(&out);
        _mongocrypt_status_reset(status);

        specs[i]->diacf = false;
        ASSERT_OK_STATUS(mc_text_search_str_query(specs[i], &out, status), status);
        _mongocrypt_buffer_cleanup(&out);
        _mongocrypt_status_reset(status);
    }

    // test no bounds checking performed if no substr/suffix/prefix specs
    mc_FLE2TextSearchInsertSpec_t exactSpecShort = {.diacf = true, .v = short_str, .len = (uint32_t)strlen(short_str)};
    mc_FLE2TextSearchInsertSpec_t exactSpecLong = {.diacf = true, .v = long_str, .len = (uint32_t)strlen(long_str)};
    ASSERT_OK_STATUS(mc_text_search_str_query(&exactSpecShort, &out, status), status);
    _mongocrypt_buffer_cleanup(&out);
    _mongocrypt_status_reset(status);
    ASSERT_OK_STATUS(mc_text_search_str_query(&exactSpecLong, &out, status), status);

    _mongocrypt_buffer_cleanup(&out);
    mongocrypt_status_destroy(status);
    bson_free(short_str);
    bson_free(long_str);
}

// Tests mc_text_search_str_query() rejects empty string input for substr/suffix/prefix queries,
// but not for exact match queries.
static void _test_text_search_str_query_empty_string(_mongocrypt_tester_t *tester) {
    mc_FLE2TextSearchInsertSpec_t substrSpec = {.substr = {{20, 4, 7}, true}, .v = "", .len = 0};
    mc_FLE2TextSearchInsertSpec_t suffixSpec = {.suffix = {{4, 7}, true}, .v = "", .len = 0};
    mc_FLE2TextSearchInsertSpec_t prefixSpec = {.prefix = {{4, 7}, true}, .v = "", .len = 0};
    mc_FLE2TextSearchInsertSpec_t exactSpec = {.v = "", .len = 0};
    mc_FLE2TextSearchInsertSpec_t *specs[3] = {&substrSpec, &suffixSpec, &prefixSpec};
    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_buffer_t out;
    for (int i = 0; i < 3; i++) {
        ASSERT_FAILS_STATUS(mc_text_search_str_query(specs[i], &out, status), status, "string value cannot be empty");
        _mongocrypt_buffer_cleanup(&out);
        _mongocrypt_status_reset(status);
    }

    ASSERT_OK_STATUS(mc_text_search_str_query(&exactSpec, &out, status), status);
    _mongocrypt_buffer_cleanup(&out);
    mongocrypt_status_destroy(status);
}

// Tests mc_text_search_str_query() performs folding per the diacf and casef parameters.
static void _test_text_search_str_query_folding(_mongocrypt_tester_t *tester) {
    const char *testStr = "Düsseldorf";
    const char *diacFoldStr = "Dusseldorf";
    const char *caseFoldStr = "düsseldorf";
    const char *bothFoldStr = "dusseldorf";
    mc_FLE2TextSearchInsertSpec_t spec = {.v = testStr, .len = (uint32_t)strlen(testStr)};
    mongocrypt_status_t *status = mongocrypt_status_new();
    _mongocrypt_buffer_t out;

    spec.diacf = true;
    ASSERT_OK_STATUS(mc_text_search_str_query(&spec, &out, status), status);
    ASSERT_CMPUINT32((uint32_t)strlen(diacFoldStr) + 5, ==, out.len); // +5 for BSON overhead
    ASSERT_STREQUAL(diacFoldStr, (const char *)(out.data + 4));       // +4 skips past 32-bit size field

    _mongocrypt_buffer_cleanup(&out);
    _mongocrypt_status_reset(status);

    spec.casef = true;
    spec.diacf = false;
    ASSERT_OK_STATUS(mc_text_search_str_query(&spec, &out, status), status);
    ASSERT_CMPUINT32((uint32_t)strlen(caseFoldStr) + 5, ==, out.len);
    ASSERT_STREQUAL(caseFoldStr, (const char *)(out.data + 4));

    _mongocrypt_buffer_cleanup(&out);
    _mongocrypt_status_reset(status);

    spec.diacf = true;
    ASSERT_OK_STATUS(mc_text_search_str_query(&spec, &out, status), status);
    ASSERT_CMPUINT32((uint32_t)strlen(bothFoldStr) + 5, ==, out.len);
    ASSERT_STREQUAL(bothFoldStr, (const char *)(out.data + 4));

    _mongocrypt_buffer_cleanup(&out);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_text_search_str_encode(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_text_search_str_encode_suffix_prefix);
    INSTALL_TEST(_test_text_search_str_encode_substring);
    INSTALL_TEST(_test_text_search_str_encode_multiple);
    INSTALL_TEST(_test_text_search_str_encode_bad_string);
    INSTALL_TEST(_test_text_search_str_encode_empty_string);
    INSTALL_TEST(_test_text_search_str_query_bad_string);
    INSTALL_TEST(_test_text_search_str_query_bounds_checking);
    INSTALL_TEST(_test_text_search_str_query_empty_string);
    INSTALL_TEST(_test_text_search_str_query_folding);
}
libmongocrypt-1.19.0/test/test-mc-textopts.c000066400000000000000000000345731521103432300210670ustar00rootroot00000000000000/*
 * Copyright 2025-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 "mc-textopts-private.h"
#include "test-mongocrypt.h"

#define RAW_STRING(...) #__VA_ARGS__

static void test_mc_TextOpts_parse(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *expectError;
        bool expectCaseSensitive;
        bool expectDiacriticSensitive;
        bool expectSubstringSet;
        int32_t expectSubstringStrMaxLength;
        int32_t expectSubstringStrMinQueryLength;
        int32_t expectSubstringStrMaxQueryLength;
        bool expectPrefixSet;
        int32_t expectPrefixStrMaxLength;
        int32_t expectPrefixStrMinQueryLength;
        int32_t expectPrefixStrMaxQueryLength;
        bool expectSuffixSet;
        int32_t expectSuffixStrMaxLength;
        int32_t expectSuffixStrMinQueryLength;
        int32_t expectSuffixStrMaxQueryLength;
    } testcase;

    testcase tests[] = {
        {.desc = "Works with minimal options",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 2}
         }),
         .expectCaseSensitive = true,
         .expectDiacriticSensitive = false,
         .expectPrefixSet = true,
         .expectPrefixStrMinQueryLength = 1,
         .expectPrefixStrMaxQueryLength = 2},
        {.desc = "Works with substring options",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "substring" : {"strMaxLength" : 10, "strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .expectCaseSensitive = true,
         .expectDiacriticSensitive = false,
         .expectSubstringSet = true,
         .expectSubstringStrMaxLength = 10,
         .expectSubstringStrMinQueryLength = 3,
         .expectSubstringStrMaxQueryLength = 8},
        {.desc = "Errors if none of prefix, suffix, or substring is provided",
         .in = RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : false}),
         .expectError = "One of 'prefix', 'suffix', or 'substring' is required"},
        {.desc = "Errors if substring, prefix, and suffix are all provided",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "substring" : {"strMaxLength" : 10, "strMinQueryLength" : 3, "strMaxQueryLength" : 8},
             "prefix" : {"strMinQueryLength" : 2, "strMaxQueryLength" : 10},
             "suffix" : {"strMinQueryLength" : 4, "strMaxQueryLength" : 12}
         }),
         .expectError = "Cannot specify 'substring' with 'prefix' or 'suffix'"},
        {.desc = "Errors if strMaxLength is present in prefix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "prefix" : {"strMaxLength" : 12, "strMinQueryLength" : 2, "strMaxQueryLength" : 10}
         }),
         .expectError = "'strMaxLength' is not allowed in 'prefix'"},
        {.desc = "Errors if strMaxLength is present in suffix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "suffix" : {"strMaxLength" : 15, "strMinQueryLength" : 4, "strMaxQueryLength" : 12}
         }),
         .expectError = "'strMaxLength' is not allowed in 'suffix'"},
        {.desc = "Errors on invalid caseSensitive type",
         .in = RAW_STRING({"caseSensitive" : 123, "diacriticSensitive" : false}),
         .expectError = "Expected bool for caseSensitive"},
        {.desc = "Errors on invalid diacriticSensitive type",
         .in = RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : "false"}),
         .expectError = "Expected bool for diacriticSensitive"},
        {.desc = "Errors on invalid substring type",
         .in = RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : false, "substring" : "invalid"}),
         .expectError = "Expected document for substring"},
        {.desc = "Errors on invalid strMaxLength type",
         .in =
             RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : false, "substring" : {"strMaxLength" : "10"}}),
         .expectError = "must be an int32"},
        {.desc = "Errors on negative strMaxLength",
         .in = RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : false, "substring" : {"strMaxLength" : -1}}),
         .expectError = "must be greater than zero"},
        {.desc = "Errors on unrecognized field",
         .in = RAW_STRING({"caseSensitive" : true, "diacriticSensitive" : false, "unknown" : true}),
         .expectError = "Unrecognized field"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_TextOpts_t txo;
        TEST_PRINTF("running test_mc_TextOpts_parse subtest: %s\n", test->desc);
        bool ret = mc_TextOpts_parse(&txo, TMP_BSON_STR(test->in), status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_CMPINT(test->expectCaseSensitive, ==, txo.caseSensitive);
            ASSERT_CMPINT(test->expectDiacriticSensitive, ==, txo.diacriticSensitive);
            ASSERT_CMPINT(test->expectSubstringSet, ==, txo.substring.set);
            if (test->expectSubstringSet) {
                ASSERT_CMPINT32(test->expectSubstringStrMaxLength, ==, txo.substring.strMaxLength.value);
                ASSERT_CMPINT32(test->expectSubstringStrMinQueryLength, ==, txo.substring.strMinQueryLength);
                ASSERT_CMPINT32(test->expectSubstringStrMaxQueryLength, ==, txo.substring.strMaxQueryLength);
            }
            ASSERT_CMPINT(test->expectPrefixSet, ==, txo.prefix.set);
            if (test->expectPrefixSet) {
                ASSERT_CMPINT32(test->expectPrefixStrMaxLength, ==, txo.prefix.strMaxLength.value);
                ASSERT_CMPINT32(test->expectPrefixStrMinQueryLength, ==, txo.prefix.strMinQueryLength);
                ASSERT_CMPINT32(test->expectPrefixStrMaxQueryLength, ==, txo.prefix.strMaxQueryLength);
            }
            ASSERT_CMPINT(test->expectSuffixSet, ==, txo.suffix.set);
            if (test->expectSuffixSet) {
                ASSERT_CMPINT32(test->expectSuffixStrMaxLength, ==, txo.suffix.strMaxLength.value);
                ASSERT_CMPINT32(test->expectSuffixStrMinQueryLength, ==, txo.suffix.strMinQueryLength);
                ASSERT_CMPINT32(test->expectSuffixStrMaxQueryLength, ==, txo.suffix.strMaxQueryLength);
            }
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_TextOpts_to_FLE2TextSearchInsertSpec(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *v;
        const char *expectError;
        const char *expect;
    } testcase;

    testcase tests[] = {
        {.desc = "Works with substring",
         .in = RAW_STRING({
             "caseSensitive" : false,
             "diacriticSensitive" : true,
             "substring" : {"strMaxLength" : 10, "strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .expect = RAW_STRING(
             {"v" : {"v" : "test", "casef" : true, "diacf" : false, "substr" : {"mlen" : 10, "ub" : 8, "lb" : 3}}})},
        {.desc = "Works with prefix",
         .in = RAW_STRING({
             "caseSensitive" : false,
             "diacriticSensitive" : true,
             "prefix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : true, "diacf" : false, "prefix" : {"ub" : 8, "lb" : 3}}})},
        {.desc = "Works with suffix",
         .in = RAW_STRING({
             "caseSensitive" : false,
             "diacriticSensitive" : true,
             "suffix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : true, "diacf" : false, "suffix" : {"ub" : 8, "lb" : 3}}})},
        {.desc = "Works with prefix + suffix",
         .in = RAW_STRING({
             "caseSensitive" : false,
             "diacriticSensitive" : true,
             "prefix" : {"strMinQueryLength" : 4, "strMaxQueryLength" : 9},
             "suffix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .expect = RAW_STRING({
             "v" : {
                 "v" : "test",
                 "casef" : true,
                 "diacf" : false,
                 "prefix" : {"ub" : 9, "lb" : 4},
                 "suffix" : {"ub" : 8, "lb" : 3}
             }
         })},
        {.desc = "Errors with missing v",
         .in = RAW_STRING({
             "caseSensitive" : false,
             "diacriticSensitive" : true,
             "prefix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"foo" : "bar"}),
         .expectError = "Unable to find 'v' in input"}};

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_TextOpts_t txo;
        TEST_PRINTF("running test_mc_TextOpts_to_FLE2TextSearchInsertSpec subtest: %s\n", test->desc);
        ASSERT_OK_STATUS(mc_TextOpts_parse(&txo, TMP_BSON_STR(test->in), status), status);
        bson_t out = BSON_INITIALIZER;
        bool ret = mc_TextOpts_to_FLE2TextSearchInsertSpec(&txo, TMP_BSON_STR(test->v), &out, status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_EQUAL_BSON(TMP_BSON_STR(test->expect), &out);
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        bson_destroy(&out);
        mongocrypt_status_destroy(status);
    }
}

static void test_mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *desc;
        const char *in;
        const char *v;
        mongocrypt_query_type_t qt;
        const char *expectError;
        const char *expect;
    } testcase;

    testcase tests[] = {
        {.desc = "Works with substring",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "substring" : {"strMaxLength" : 10, "strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .qt = MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW,
         .expect = RAW_STRING(
             {"v" : {"v" : "test", "casef" : false, "diacf" : true, "substr" : {"mlen" : 10, "ub" : 8, "lb" : 3}}})},
        {.desc = "Works with prefix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "prefix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .qt = MONGOCRYPT_QUERY_TYPE_PREFIX,
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : false, "diacf" : true, "prefix" : {"ub" : 8, "lb" : 3}}})},
        {.desc = "Works with suffix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "suffix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .qt = MONGOCRYPT_QUERY_TYPE_SUFFIX,
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : false, "diacf" : true, "suffix" : {"ub" : 8, "lb" : 3}}})},
        {.desc = "Works with prefix + suffix when querying prefix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "prefix" : {"strMinQueryLength" : 4, "strMaxQueryLength" : 9},
             "suffix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .qt = MONGOCRYPT_QUERY_TYPE_PREFIX,
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : false, "diacf" : true, "prefix" : {"ub" : 9, "lb" : 4}}})},
        {.desc = "Works with prefix + suffix when querying suffix",
         .in = RAW_STRING({
             "caseSensitive" : true,
             "diacriticSensitive" : false,
             "prefix" : {"strMinQueryLength" : 4, "strMaxQueryLength" : 9},
             "suffix" : {"strMinQueryLength" : 3, "strMaxQueryLength" : 8}
         }),
         .v = RAW_STRING({"v" : "test"}),
         .qt = MONGOCRYPT_QUERY_TYPE_SUFFIX,
         .expect =
             RAW_STRING({"v" : {"v" : "test", "casef" : false, "diacf" : true, "suffix" : {"ub" : 8, "lb" : 3}}})},
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        testcase *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_TextOpts_t txo;
        TEST_PRINTF("running test_mc_TextOpts_to_FLE2TextSearchInsertSpec subtest: %s\n", test->desc);
        ASSERT_OK_STATUS(mc_TextOpts_parse(&txo, TMP_BSON_STR(test->in), status), status);
        bson_t out = BSON_INITIALIZER;
        bool ret =
            mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query(&txo, TMP_BSON_STR(test->v), test->qt, &out, status);
        if (!test->expectError) {
            ASSERT_OK_STATUS(ret, status);
            ASSERT_EQUAL_BSON(TMP_BSON_STR(test->expect), &out);
        } else {
            ASSERT_FAILS_STATUS(ret, status, test->expectError);
        }
        bson_destroy(&out);
        mongocrypt_status_destroy(status);
    }
}

void _mongocrypt_tester_install_mc_TextOpts(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mc_TextOpts_parse);
    INSTALL_TEST(test_mc_TextOpts_to_FLE2TextSearchInsertSpec);
    INSTALL_TEST(test_mc_TextOpts_to_FLE2TextSearchInsertSpec_for_query);
}
libmongocrypt-1.19.0/test/test-mc-tokens.c000066400000000000000000000602661521103432300204760ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-tokens-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#define FOREACH_FIELD(F)                                                                                               \
    F(root)                                                                                                            \
    F(value)                                                                                                           \
    F(collectionsLevel1Token)                                                                                          \
    F(serverDataEncryptionLevel1Token)                                                                                 \
    F(ServerTokenDerivationLevel1Token)                                                                                \
    F(EDCToken)                                                                                                        \
    F(ESCToken)                                                                                                        \
    F(ECCToken)                                                                                                        \
    F(ECOCToken)                                                                                                       \
    F(EDCDerivedFromDataToken)                                                                                         \
    F(ESCDerivedFromDataToken)                                                                                         \
    F(ECCDerivedFromDataToken)                                                                                         \
    F(serverDerivedFromDataToken)                                                                                      \
    F(EDCDerivedFromDataTokenAndContentionFactor)                                                                      \
    F(ESCDerivedFromDataTokenAndContentionFactor)                                                                      \
    F(ECCDerivedFromDataTokenAndContentionFactor)                                                                      \
    F(EDCTwiceDerivedToken)                                                                                            \
    F(ESCTwiceDerivedTagToken)                                                                                         \
    F(ESCTwiceDerivedValueToken)                                                                                       \
    F(serverCountAndContentionFactorEncryptionToken)                                                                   \
    F(serverZerosEncryptionToken)                                                                                      \
    F(AnchorPaddingTokenRoot)                                                                                          \
    F(AnchorPaddingKeyToken)                                                                                           \
    F(AnchorPaddingValueToken)                                                                                         \
    F(EDCTextExactToken)                                                                                               \
    F(EDCTextSubstringToken)                                                                                           \
    F(EDCTextSuffixToken)                                                                                              \
    F(EDCTextPrefixToken)                                                                                              \
    F(ESCTextExactToken)                                                                                               \
    F(ESCTextSubstringToken)                                                                                           \
    F(ESCTextSuffixToken)                                                                                              \
    F(ESCTextPrefixToken)                                                                                              \
    F(ServerTextExactToken)                                                                                            \
    F(ServerTextSubstringToken)                                                                                        \
    F(ServerTextSuffixToken)                                                                                           \
    F(ServerTextPrefixToken)                                                                                           \
    F(EDCTextExactDerivedFromDataToken)                                                                                \
    F(EDCTextSubstringDerivedFromDataToken)                                                                            \
    F(EDCTextSuffixDerivedFromDataToken)                                                                               \
    F(EDCTextPrefixDerivedFromDataToken)                                                                               \
    F(ESCTextExactDerivedFromDataToken)                                                                                \
    F(ESCTextSubstringDerivedFromDataToken)                                                                            \
    F(ESCTextSuffixDerivedFromDataToken)                                                                               \
    F(ESCTextPrefixDerivedFromDataToken)                                                                               \
    F(EDCTextExactDerivedFromDataTokenAndContentionFactorToken)                                                        \
    F(EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken)                                                    \
    F(EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken)                                                       \
    F(EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken)                                                       \
    F(ESCTextExactDerivedFromDataTokenAndContentionFactorToken)                                                        \
    F(ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken)                                                    \
    F(ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken)                                                       \
    F(ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken)                                                       \
    F(ServerTextExactDerivedFromDataToken)                                                                             \
    F(ServerTextSubstringDerivedFromDataToken)                                                                         \
    F(ServerTextSuffixDerivedFromDataToken)                                                                            \
    F(ServerTextPrefixDerivedFromDataToken)

typedef struct {
#define DECLARE_FIELD(f) _mongocrypt_buffer_t f;
    FOREACH_FIELD(DECLARE_FIELD)
#undef DECLARE_FIELD
    uint64_t contentionFactor;
} _mc_token_test;

static void _mc_token_test_cleanup(_mc_token_test *test) {
#define CLEANUP_FIELD(f) _mongocrypt_buffer_cleanup(&test->f);
    FOREACH_FIELD(CLEANUP_FIELD)
#undef CLEANUP_FIELD
}

static void _mc_token_test_run(_mongocrypt_tester_t *tester, const char *path) {
    TEST_PRINTF("Loading test from %s...\n", path);

    mongocrypt_binary_t *test_bin = TEST_FILE(path);
    if (!test_bin) {
        TEST_ERROR("Failed loading test data file '%s'\n", path);
    }
    if (test_bin->len == 5) {
        TEST_ERROR("Invalid JSON in file '%s'\n", path);
    }

    bson_t test_bson;
    ASSERT(bson_init_static(&test_bson, test_bin->data, test_bin->len));
    ASSERT(bson_validate(&test_bson, BSON_VALIDATE_NONE, NULL));

    bool hasContentionFactor = false;
    _mc_token_test test = {{0}};
    bson_iter_t it;
    ASSERT(bson_iter_init(&it, &test_bson));
    while (bson_iter_next(&it)) {
        const char *field = bson_iter_key(&it);
        ASSERT(field);

#define PARSE_FIELD(f)                                                                                                 \
    if (!strcmp(field, #f)) {                                                                                          \
        ASSERT_OR_PRINT_MSG(!test.f.data, "Duplicate field '" #f "' in test");                                         \
        ASSERT(BSON_ITER_HOLDS_UTF8(&it));                                                                             \
        const char *value = bson_iter_utf8(&it, NULL);                                                                 \
        _mongocrypt_buffer_copy_from_hex(&test.f, value);                                                              \
        ASSERT(strlen(value) == (test.f.len * 2));                                                                     \
    } else
        FOREACH_FIELD(PARSE_FIELD)
#undef PARSE_FIELD
        /* else */
        if (!strcmp(field, "contentionFactor")) {
            ASSERT_OR_PRINT_MSG(!hasContentionFactor, "Duplicate field 'contentionFactor' in test");
            ASSERT(BSON_ITER_HOLDS_INT32(&it) || BSON_ITER_HOLDS_INT64(&it));
            test.contentionFactor = bson_iter_as_int64(&it);
            hasContentionFactor = true;
        } else {
            TEST_ERROR("Unknown field '%s'", field);
        }
    }

#define CHECK_FIELD(f) ASSERT_OR_PRINT_MSG(test.f.data, "Missing field '" #f "' in test");
    FOREACH_FIELD(CHECK_FIELD)
#undef CHECK_FIELD
    ASSERT_OR_PRINT_MSG(hasContentionFactor, "Missing field 'contentionFactor' in test");

    // Run the actual test.
    mongocrypt_status_t *status = mongocrypt_status_new();
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    // collectionsLevel1Token
    mc_CollectionsLevel1Token_t *collectionsLevel1Token =
        mc_CollectionsLevel1Token_new(crypt->crypto, &test.root, status);
    ASSERT_OR_PRINT(collectionsLevel1Token, status);
    ASSERT_CMPBUF(*mc_CollectionsLevel1Token_get(collectionsLevel1Token), test.collectionsLevel1Token);

    // ServerDataEncryptionLevel1Token
    mc_ServerDataEncryptionLevel1Token_t *serverDataEncryptionLevel1Token =
        mc_ServerDataEncryptionLevel1Token_new(crypt->crypto, &test.root, status);
    ASSERT_OR_PRINT(serverDataEncryptionLevel1Token, status);
    ASSERT_CMPBUF(*mc_ServerDataEncryptionLevel1Token_get(serverDataEncryptionLevel1Token),
                  test.serverDataEncryptionLevel1Token);

    // ServerTokenDerivationLevel1Token
    mc_ServerTokenDerivationLevel1Token_t *ServerTokenDerivationLevel1Token =
        mc_ServerTokenDerivationLevel1Token_new(crypt->crypto, &test.root, status);
    ASSERT_OR_PRINT(ServerTokenDerivationLevel1Token, status);
    ASSERT_CMPBUF(*mc_ServerTokenDerivationLevel1Token_get(ServerTokenDerivationLevel1Token),
                  test.ServerTokenDerivationLevel1Token);

// (EDC|ESC|ECC|ECOC)Token
#define TEST_COLL_TOKEN(Name)                                                                                          \
    mc_##Name##Token_t *Name##Token = mc_##Name##Token_new(crypt->crypto, collectionsLevel1Token, status);             \
    ASSERT_OR_PRINT(Name##Token, status);                                                                              \
    ASSERT_CMPBUF(*mc_##Name##Token_get(Name##Token), test.Name##Token);
    TEST_COLL_TOKEN(EDC)
    TEST_COLL_TOKEN(ESC)
    TEST_COLL_TOKEN(ECC)
    TEST_COLL_TOKEN(ECOC)
#undef TEST_COLL_TOKEN

#define TEST_HELPER(Base, BaseSuffix, TokenSuffix, ExtraArgsToNew)                                                     \
    mc_##Base##TokenSuffix##_t *Base##TokenSuffix =                                                                    \
        mc_##Base##TokenSuffix##_new(crypt->crypto, Base##BaseSuffix, ExtraArgsToNew status);                          \
    ASSERT_OR_PRINT(Base##TokenSuffix, status);                                                                        \
    ASSERT_CMPBUF(*mc_##Base##TokenSuffix##_get(Base##TokenSuffix), test.Base##TokenSuffix)

#define COMMA ,

// (EDC|ESC|ECC)DerivedFromDataToken(AndContentionFactor)?
#define TEST_DERIVED(Name)                                                                                             \
    TEST_HELPER(Name, Token, DerivedFromDataToken, &test.value COMMA);                                                 \
    TEST_HELPER(Name, DerivedFromDataToken, DerivedFromDataTokenAndContentionFactor, test.contentionFactor COMMA)
    TEST_DERIVED(EDC);
    TEST_DERIVED(ESC);
    TEST_DERIVED(ECC);
#undef TEST_DERIVED

// (EDC|ESC)TwiceDerivedToken(Tag|Value)?
#define TEST_TWICE(Name, Suffix) TEST_HELPER(Name, DerivedFromDataTokenAndContentionFactor, TwiceDerived##Suffix, )
    TEST_TWICE(EDC, Token);
    TEST_TWICE(ESC, TagToken);
    TEST_TWICE(ESC, ValueToken);
#undef TEST_TWICE

    // ServerDerivedFromDataToken
    mc_ServerDerivedFromDataToken_t *serverDerivedFromDataToken =
        mc_ServerDerivedFromDataToken_new(crypt->crypto, ServerTokenDerivationLevel1Token, &test.value, status);
    ASSERT_OR_PRINT(serverDerivedFromDataToken, status);
    ASSERT_CMPBUF(*mc_ServerDerivedFromDataToken_get(serverDerivedFromDataToken), test.serverDerivedFromDataToken);

    // ServerCountAndContentionFactorEncryptionToken
    mc_ServerCountAndContentionFactorEncryptionToken_t *serverCACFET =
        mc_ServerCountAndContentionFactorEncryptionToken_new(crypt->crypto, serverDerivedFromDataToken, status);
    ASSERT_OR_PRINT(serverCACFET, status);
    ASSERT_CMPBUF(*mc_ServerCountAndContentionFactorEncryptionToken_get(serverCACFET),
                  test.serverCountAndContentionFactorEncryptionToken);

    // ServerZerosEncryptionToken
    mc_ServerZerosEncryptionToken_t *serverZeros =
        mc_ServerZerosEncryptionToken_new(crypt->crypto, serverDerivedFromDataToken, status);
    ASSERT_OR_PRINT(serverZeros, status);
    ASSERT_CMPBUF(*mc_ServerZerosEncryptionToken_get(serverZeros), test.serverZerosEncryptionToken);

    // AnchorPaddingTokenRoot
    mc_AnchorPaddingTokenRoot_t *padding = mc_AnchorPaddingTokenRoot_new(crypt->crypto, ESCToken, status);
    ASSERT_OR_PRINT(padding, status);
    ASSERT_CMPBUF(*mc_AnchorPaddingTokenRoot_get(padding), test.AnchorPaddingTokenRoot);

    mc_AnchorPaddingKeyToken_t *paddingKey = mc_AnchorPaddingKeyToken_new(crypt->crypto, padding, status);
    ASSERT_OR_PRINT(paddingKey, status);
    ASSERT_CMPBUF(*mc_AnchorPaddingKeyToken_get(paddingKey), test.AnchorPaddingKeyToken);

    mc_AnchorPaddingValueToken_t *paddingValue = mc_AnchorPaddingValueToken_new(crypt->crypto, padding, status);
    ASSERT_OR_PRINT(paddingValue, status);
    ASSERT_CMPBUF(*mc_AnchorPaddingValueToken_get(paddingValue), test.AnchorPaddingValueToken);

#define TEST_TEXT(Name, Suffix) TEST_HELPER(Name, Token, Text##Suffix##Token, )
#define TEST_TEXT_EXTRA(Name, Suffix, BaseSuffix) TEST_HELPER(Name, BaseSuffix##Token, Text##Suffix##Token, )
#define TEST_TEXT_DERIVED_FROM_DATA(Name) TEST_HELPER(Name, Token, DerivedFromDataToken, &test.value COMMA)
#define TEST_TEXT_DERIVED_FROM_CONTENTION(Name)                                                                        \
    TEST_HELPER(Name, Token, TokenAndContentionFactorToken, test.contentionFactor COMMA)

    TEST_TEXT(EDC, Exact);
    TEST_TEXT(EDC, Substring);
    TEST_TEXT(EDC, Suffix);
    TEST_TEXT(EDC, Prefix);

    TEST_TEXT(ESC, Exact);
    TEST_TEXT(ESC, Substring);
    TEST_TEXT(ESC, Suffix);
    TEST_TEXT(ESC, Prefix);

    TEST_TEXT_EXTRA(Server, Exact, TokenDerivationLevel1);
    TEST_TEXT_EXTRA(Server, Substring, TokenDerivationLevel1);
    TEST_TEXT_EXTRA(Server, Suffix, TokenDerivationLevel1);
    TEST_TEXT_EXTRA(Server, Prefix, TokenDerivationLevel1);

    TEST_TEXT_DERIVED_FROM_DATA(EDCTextExact);
    TEST_TEXT_DERIVED_FROM_DATA(EDCTextSubstring);
    TEST_TEXT_DERIVED_FROM_DATA(EDCTextSuffix);
    TEST_TEXT_DERIVED_FROM_DATA(EDCTextPrefix);
    TEST_TEXT_DERIVED_FROM_CONTENTION(EDCTextExactDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(EDCTextSubstringDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(EDCTextSuffixDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(EDCTextPrefixDerivedFromData);

    TEST_TEXT_DERIVED_FROM_DATA(ESCTextExact);
    TEST_TEXT_DERIVED_FROM_DATA(ESCTextSubstring);
    TEST_TEXT_DERIVED_FROM_DATA(ESCTextSuffix);
    TEST_TEXT_DERIVED_FROM_DATA(ESCTextPrefix);
    TEST_TEXT_DERIVED_FROM_CONTENTION(ESCTextExactDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(ESCTextSubstringDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(ESCTextSuffixDerivedFromData);
    TEST_TEXT_DERIVED_FROM_CONTENTION(ESCTextPrefixDerivedFromData);

    TEST_TEXT_DERIVED_FROM_DATA(ServerTextExact);
    TEST_TEXT_DERIVED_FROM_DATA(ServerTextSubstring);
    TEST_TEXT_DERIVED_FROM_DATA(ServerTextPrefix);
    TEST_TEXT_DERIVED_FROM_DATA(ServerTextSuffix);

    // Done.
    mc_ServerTextPrefixDerivedFromDataToken_destroy(ServerTextPrefixDerivedFromDataToken);
    mc_ServerTextSuffixDerivedFromDataToken_destroy(ServerTextSuffixDerivedFromDataToken);
    mc_ServerTextSubstringDerivedFromDataToken_destroy(ServerTextSubstringDerivedFromDataToken);
    mc_ServerTextExactDerivedFromDataToken_destroy(ServerTextExactDerivedFromDataToken);
    mc_ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken_destroy(
        ESCTextPrefixDerivedFromDataTokenAndContentionFactorToken);
    mc_ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken_destroy(
        ESCTextSuffixDerivedFromDataTokenAndContentionFactorToken);
    mc_ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken_destroy(
        ESCTextSubstringDerivedFromDataTokenAndContentionFactorToken);
    mc_ESCTextExactDerivedFromDataTokenAndContentionFactorToken_destroy(
        ESCTextExactDerivedFromDataTokenAndContentionFactorToken);
    mc_ESCTextPrefixDerivedFromDataToken_destroy(ESCTextPrefixDerivedFromDataToken);
    mc_ESCTextSuffixDerivedFromDataToken_destroy(ESCTextSuffixDerivedFromDataToken);
    mc_ESCTextSubstringDerivedFromDataToken_destroy(ESCTextSubstringDerivedFromDataToken);
    mc_ESCTextExactDerivedFromDataToken_destroy(ESCTextExactDerivedFromDataToken);
    mc_EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken_destroy(
        EDCTextPrefixDerivedFromDataTokenAndContentionFactorToken);
    mc_EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken_destroy(
        EDCTextSuffixDerivedFromDataTokenAndContentionFactorToken);
    mc_EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken_destroy(
        EDCTextSubstringDerivedFromDataTokenAndContentionFactorToken);
    mc_EDCTextExactDerivedFromDataTokenAndContentionFactorToken_destroy(
        EDCTextExactDerivedFromDataTokenAndContentionFactorToken);
    mc_EDCTextPrefixDerivedFromDataToken_destroy(EDCTextPrefixDerivedFromDataToken);
    mc_EDCTextSuffixDerivedFromDataToken_destroy(EDCTextSuffixDerivedFromDataToken);
    mc_EDCTextSubstringDerivedFromDataToken_destroy(EDCTextSubstringDerivedFromDataToken);
    mc_EDCTextExactDerivedFromDataToken_destroy(EDCTextExactDerivedFromDataToken);
    mc_ServerTextPrefixToken_destroy(ServerTextPrefixToken);
    mc_ServerTextSuffixToken_destroy(ServerTextSuffixToken);
    mc_ServerTextSubstringToken_destroy(ServerTextSubstringToken);
    mc_ServerTextExactToken_destroy(ServerTextExactToken);
    mc_ESCTextPrefixToken_destroy(ESCTextPrefixToken);
    mc_ESCTextSuffixToken_destroy(ESCTextSuffixToken);
    mc_ESCTextSubstringToken_destroy(ESCTextSubstringToken);
    mc_ESCTextExactToken_destroy(ESCTextExactToken);
    mc_EDCTextPrefixToken_destroy(EDCTextPrefixToken);
    mc_EDCTextSuffixToken_destroy(EDCTextSuffixToken);
    mc_EDCTextSubstringToken_destroy(EDCTextSubstringToken);
    mc_EDCTextExactToken_destroy(EDCTextExactToken);
    mc_AnchorPaddingValueToken_destroy(paddingValue);
    mc_AnchorPaddingKeyToken_destroy(paddingKey);
    mc_AnchorPaddingTokenRoot_destroy(padding);
    mc_ServerZerosEncryptionToken_destroy(serverZeros);
    mc_ServerCountAndContentionFactorEncryptionToken_destroy(serverCACFET);
    mc_ServerDerivedFromDataToken_destroy(serverDerivedFromDataToken);
    mc_ESCTwiceDerivedValueToken_destroy(ESCTwiceDerivedValueToken);
    mc_ESCTwiceDerivedTagToken_destroy(ESCTwiceDerivedTagToken);
    mc_EDCTwiceDerivedToken_destroy(EDCTwiceDerivedToken);
    mc_ECCDerivedFromDataTokenAndContentionFactor_destroy(ECCDerivedFromDataTokenAndContentionFactor);
    mc_ESCDerivedFromDataTokenAndContentionFactor_destroy(ESCDerivedFromDataTokenAndContentionFactor);
    mc_EDCDerivedFromDataTokenAndContentionFactor_destroy(EDCDerivedFromDataTokenAndContentionFactor);
    mc_ECCDerivedFromDataToken_destroy(ECCDerivedFromDataToken);
    mc_ESCDerivedFromDataToken_destroy(ESCDerivedFromDataToken);
    mc_EDCDerivedFromDataToken_destroy(EDCDerivedFromDataToken);
    mc_ECOCToken_destroy(ECOCToken);
    mc_ECCToken_destroy(ECCToken);
    mc_ESCToken_destroy(ESCToken);
    mc_EDCToken_destroy(EDCToken);
    mc_ServerTokenDerivationLevel1Token_destroy(ServerTokenDerivationLevel1Token);
    mc_ServerDataEncryptionLevel1Token_destroy(serverDataEncryptionLevel1Token);
    mc_CollectionsLevel1Token_destroy(collectionsLevel1Token);
    _mc_token_test_cleanup(&test);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);

    TEST_PRINTF("Finished tests in %s\n", path);
}

static void _test_mc_tokens(_mongocrypt_tester_t *tester) {
    _mc_token_test_run(tester, "test/data/tokens/mc.json");
    _mc_token_test_run(tester, "test/data/tokens/server.json");
}

static void _test_mc_tokens_error(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    mongocrypt_t *crypt;
    _mongocrypt_buffer_t RootKey;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    /* RootKey is incorrect length. */
    _mongocrypt_buffer_copy_from_hex(&RootKey, "AAAA");

    mc_CollectionsLevel1Token_t *CollectionsLevel1Token =
        mc_CollectionsLevel1Token_new(crypt->crypto, &RootKey, status);
    ASSERT_FAILS_STATUS(CollectionsLevel1Token != NULL, status, "invalid hmac_sha_256 key length");

    mc_CollectionsLevel1Token_destroy(CollectionsLevel1Token);
    _mongocrypt_buffer_cleanup(&RootKey);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _test_mc_tokens_raw_buffer(_mongocrypt_tester_t *tester) {
    mc_ServerDataEncryptionLevel1Token_t *token1;
    mc_ServerDataEncryptionLevel1Token_t *token2;
    _mongocrypt_buffer_t test_input;
    _mongocrypt_buffer_t expected;

    _mongocrypt_buffer_copy_from_hex(&test_input, "6c6a349956c19f9c5e638e612011a71fbb71921edb540310c17cd0208b7f548b");

    /* Make a token from a raw buffer */
    token1 = mc_ServerDataEncryptionLevel1Token_new_from_buffer(&test_input);
    token2 = mc_ServerDataEncryptionLevel1Token_new_from_buffer_copy(&test_input);

    /* Assert new_from_buffer did not steal ownership. */
    ASSERT(test_input.owned);
    ASSERT(test_input.len == MONGOCRYPT_HMAC_SHA256_LEN);

    _mongocrypt_buffer_copy_from_hex(&expected, "6c6a349956c19f9c5e638e612011a71fbb71921edb540310c17cd0208b7f548b");

    ASSERT_CMPBUF(*mc_ServerDataEncryptionLevel1Token_get(token1), expected);
    ASSERT_CMPBUF(*mc_ServerDataEncryptionLevel1Token_get(token2), expected);

    /* Assert new_from_buffer references original buffer instead of a copy. */
    test_input.data[0] = '0';
    expected.data[0] = '0';
    ASSERT_CMPBUF(*mc_ServerDataEncryptionLevel1Token_get(token1), expected);

    // Assert new_from_buffer_copy references a new buffer.
    ASSERT_CMPUINT8(mc_ServerDataEncryptionLevel1Token_get(token2)->data[0], !=, expected.data[0]);

    _mongocrypt_buffer_cleanup(&test_input);
    _mongocrypt_buffer_cleanup(&expected);
    mc_ServerDataEncryptionLevel1Token_destroy(token1);
    mc_ServerDataEncryptionLevel1Token_destroy(token2);
}

void _mongocrypt_tester_install_mc_tokens(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mc_tokens);
    INSTALL_TEST(_test_mc_tokens_error);
    INSTALL_TEST(_test_mc_tokens_raw_buffer);
}
libmongocrypt-1.19.0/test/test-mc-writer.c000066400000000000000000000134121521103432300204760ustar00rootroot00000000000000/*
 * Copyright 2022-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 "mc-reader-private.h"
#include "mc-writer-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

static void _test_mc_writer_ints(_mongocrypt_tester_t *tester) {
    {
        mongocrypt_status_t *status;
        status = mongocrypt_status_new();

        _mongocrypt_buffer_t write_buffer;
        _mongocrypt_buffer_init_size(&write_buffer, sizeof(uint8_t));

        mc_writer_t writer;
        mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

        uint8_t num = 4;
        ASSERT_OK_STATUS(mc_writer_write_u8(&writer, num, status), status);

        mc_reader_t reader;
        mc_reader_init_from_buffer(&reader, &write_buffer, __func__);

        uint8_t out;
        ASSERT_OK_STATUS(mc_reader_read_u8(&reader, &out, status), status);

        ASSERT_CMPUINT8(out, ==, num);

        _mongocrypt_buffer_cleanup(&write_buffer);
        mongocrypt_status_destroy(status);
    }

    {
        mongocrypt_status_t *status;
        status = mongocrypt_status_new();

        _mongocrypt_buffer_t write_buffer;
        _mongocrypt_buffer_init_size(&write_buffer, sizeof(uint32_t));

        mc_writer_t writer;
        mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

        uint32_t num = 23832405;
        ASSERT_OK_STATUS(mc_writer_write_u32(&writer, num, status), status);

        mc_reader_t reader;
        mc_reader_init_from_buffer(&reader, &write_buffer, __func__);

        uint32_t out;
        ASSERT_OK_STATUS(mc_reader_read_u32(&reader, &out, status), status);

        ASSERT_CMPUINT32(out, ==, num);

        _mongocrypt_buffer_cleanup(&write_buffer);
        mongocrypt_status_destroy(status);
    }

    {
        mongocrypt_status_t *status;
        status = mongocrypt_status_new();

        _mongocrypt_buffer_t write_buffer;
        _mongocrypt_buffer_init_size(&write_buffer, sizeof(uint64_t));

        mc_writer_t writer;
        mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

        uint64_t num = 23832405;
        ASSERT_OK_STATUS(mc_writer_write_u64(&writer, num, status), status);

        mc_reader_t reader;
        mc_reader_init_from_buffer(&reader, &write_buffer, __func__);

        uint64_t out;
        ASSERT_OK_STATUS(mc_reader_read_u64(&reader, &out, status), status);

        ASSERT_CMPUINT64(out, ==, num);

        _mongocrypt_buffer_cleanup(&write_buffer);
        mongocrypt_status_destroy(status);
    }
}

static void _test_mc_writer_buffer(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    _mongocrypt_buffer_t input_buffer;

    _mongocrypt_buffer_copy_from_hex(&input_buffer,
                                     "07123456781234987612341234567890120243bba14ddf42da823c33569f4689f465a");

    _mongocrypt_buffer_t write_buffer;
    _mongocrypt_buffer_init_size(&write_buffer, input_buffer.len);

    mc_writer_t writer;
    mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

    ASSERT_OK_STATUS(mc_writer_write_buffer(&writer, &input_buffer, input_buffer.len, status), status);

    _mongocrypt_buffer_t read_buffer;
    mc_reader_t reader;
    mc_reader_init_from_buffer(&reader, &write_buffer, __func__);
    ASSERT_OK_STATUS(mc_reader_read_buffer(&reader, &read_buffer, write_buffer.len, status), status);

    ASSERT_CMPBUF(input_buffer, read_buffer);

    _mongocrypt_buffer_cleanup(&input_buffer);
    _mongocrypt_buffer_cleanup(&write_buffer);
    _mongocrypt_buffer_cleanup(&read_buffer);
    mongocrypt_status_destroy(status);
}

static void _test_mc_writer_prf(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    _mongocrypt_buffer_t input_buffer;
    _mongocrypt_buffer_copy_from_hex(&input_buffer, "c03cd1d0536aa07d4ffaa0301656222fc03cd1d0536aa07d4ffaa0301656222f");

    _mongocrypt_buffer_t write_buffer;
    _mongocrypt_buffer_init_size(&write_buffer, input_buffer.len);

    mc_writer_t writer;
    mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

    ASSERT_OK_STATUS(mc_writer_write_prfblock_buffer(&writer, &input_buffer, status), status);
    ASSERT_CMPBUF(input_buffer, write_buffer);

    _mongocrypt_buffer_cleanup(&input_buffer);
    _mongocrypt_buffer_cleanup(&write_buffer);
    mongocrypt_status_destroy(status);
}

static void _test_mc_writer_uuid(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    status = mongocrypt_status_new();

    _mongocrypt_buffer_t input_buffer;
    _mongocrypt_buffer_copy_from_hex(&input_buffer, "c03cd1d0536aa07d4ffaa0301656222f");

    _mongocrypt_buffer_t write_buffer;
    _mongocrypt_buffer_init_size(&write_buffer, input_buffer.len);

    mc_writer_t writer;
    mc_writer_init_from_buffer(&writer, &write_buffer, __func__);

    ASSERT_OK_STATUS(mc_writer_write_uuid_buffer(&writer, &input_buffer, status), status);
    ASSERT_CMPBUF(input_buffer, write_buffer);

    _mongocrypt_buffer_cleanup(&input_buffer);
    _mongocrypt_buffer_cleanup(&write_buffer);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_mc_writer(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mc_writer_ints);
    INSTALL_TEST(_test_mc_writer_buffer);
    INSTALL_TEST(_test_mc_writer_prf);
    INSTALL_TEST(_test_mc_writer_uuid);
}
libmongocrypt-1.19.0/test/test-mongocrypt-assert-match-bson.c000066400000000000000000000704411521103432300243230ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt-assert.h"

#include 

/* string comparison functions for Windows */
#ifdef _WIN32
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif

/* The following matching logic is copied from libmongoc. */
bool bson_init_from_value(bson_t *b, const bson_value_t *v);

char *single_quotes_to_double(const char *str);

/* match_action_t determines if default check for a field is overridden. */
typedef enum {
    MATCH_ACTION_SKIP,    /* do not use the default check. */
    MATCH_ACTION_ABORT,   /* an error occurred, stop checking. */
    MATCH_ACTION_CONTINUE /* use the default check. */
} match_action_t;

struct _match_ctx_t;
/* doc_iter may be null if the pattern field is not found. */
typedef match_action_t (*match_visitor_fn)(struct _match_ctx_t *ctx, bson_iter_t *pattern_iter, bson_iter_t *doc_iter);

typedef struct _match_ctx_t {
    char errmsg[1000];
    bool strict_numeric_types;
    /* if retain_dots_in_keys is true, then don't consider a path with dots to
     * indicate recursing into a sub document. */
    bool retain_dots_in_keys;
    /* if allow_placeholders is true, treats 42 and "42" as placeholders. I.e.
     * comparing 42 to anything is ok. */
    bool allow_placeholders;
    /* path is the dot separated breadcrumb trail of keys. */
    char path[1000];
    /* if visitor_fn is not NULL, this is called on for every key in the pattern.
     * The returned match_action_t can override the default match behavior. */
    match_visitor_fn visitor_fn;
    void *visitor_ctx;
    /* if is_command is true, then compare the first key case insensitively. */
    bool is_command;
} match_ctx_t;

void assert_match_bson(const bson_t *doc, const bson_t *pattern, bool is_command);

bool match_bson(const bson_t *doc, const bson_t *pattern, bool is_command);

int64_t bson_value_as_int64(const bson_value_t *value);

bool match_bson_value(const bson_value_t *doc, const bson_value_t *pattern, match_ctx_t *ctx);

bool match_bson_with_ctx(const bson_t *doc, const bson_t *pattern, match_ctx_t *ctx);

bool match_json(const bson_t *doc,
                bool is_command,
                const char *filename,
                int lineno,
                const char *funcname,
                const char *json_pattern,
                ...);

const char *_mongoc_bson_type_to_str(bson_type_t t);

static bool get_exists_operator(const bson_value_t *value, bool *exists);

static bool get_empty_operator(const bson_value_t *value, bool *exists);

static bool get_type_operator(const bson_value_t *value, bson_type_t *out);

static bool is_empty_doc_or_array(const bson_value_t *value);

static bool
find(bson_iter_t *iter, const bson_t *doc, const char *key, bool is_command, bool is_first, bool retain_dots_in_keys);

/*--------------------------------------------------------------------------
 *
 * single_quotes_to_double --
 *
 *       Copy str with single-quotes replaced by double.
 *
 * Returns:
 *       A string you must bson_free.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

char *single_quotes_to_double(const char *str) {
    char *result = bson_strdup(str);
    char *p;

    for (p = result; *p; p++) {
        if (*p == '\'') {
            *p = '"';
        }
    }

    return result;
}

/*--------------------------------------------------------------------------
 *
 * match_json --
 *
 *       Call match_bson on "doc" and "json_pattern".
 *       For convenience, single-quotes are synonymous with double-quotes.
 *
 *       A NULL doc or NULL json_pattern means "{}".
 *
 * Returns:
 *       True or false.
 *
 * Side effects:
 *       Logs if no match. Aborts if json is malformed.
 *
 *--------------------------------------------------------------------------
 */

MLIB_ANNOTATE_PRINTF(6, 7)

bool match_json(const bson_t *doc,
                bool is_command,
                const char *filename,
                int lineno,
                const char *funcname,
                const char *json_pattern,
                ...) {
    va_list args;
    char *json_pattern_formatted;
    char *double_quoted;
    bson_error_t error;
    bson_t *pattern;
    match_ctx_t ctx = {{0}};
    bool matches;

    va_start(args, json_pattern);
    json_pattern_formatted = bson_strdupv_printf(json_pattern ? json_pattern : "{}", args);
    va_end(args);

    double_quoted = single_quotes_to_double(json_pattern_formatted);
    pattern = bson_new_from_json((const uint8_t *)double_quoted, -1, &error);

    if (!pattern) {
        TEST_STDERR_PRINTF("couldn't parse JSON: %s\n", error.message);
        abort();
    }

    ctx.is_command = is_command;
    matches = match_bson_with_ctx(doc, pattern, &ctx);

    if (!matches) {
        char *as_string = doc ? bson_as_canonical_extended_json(doc, NULL) : NULL;
        TEST_STDERR_PRINTF("ASSERT_MATCH failed with document:\n\n"
                           "%s\n"
                           "pattern:\n%s\n"
                           "%s\n"
                           "%s:%d %s()\n",
                           as_string ? as_string : "{}",
                           double_quoted,
                           ctx.errmsg,
                           filename,
                           lineno,
                           funcname);
        bson_free(as_string);
    }

    bson_destroy(pattern);
    bson_free(json_pattern_formatted);
    bson_free(double_quoted);

    return matches;
}

/*--------------------------------------------------------------------------
 *
 * match_bson --
 *
 *       Does "doc" match "pattern"?
 *
 *       See match_bson_with_ctx for details.
 *
 * Returns:
 *       True or false.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

bool match_bson(const bson_t *doc, const bson_t *pattern, bool is_command) {
    match_ctx_t ctx = {{0}};

    ctx.strict_numeric_types = true;
    ctx.is_command = is_command;

    return match_bson_with_ctx(doc, pattern, &ctx);
}

MLIB_ANNOTATE_PRINTF(2, 3)

static void match_err(match_ctx_t *ctx, const char *fmt, ...) {
    va_list args;
    char *formatted;

    BSON_ASSERT(ctx);

    va_start(args, fmt);
    formatted = bson_strdupv_printf(fmt, args);
    va_end(args);

    BSON_ASSERT(0 < bson_snprintf(ctx->errmsg, sizeof ctx->errmsg, "%s: %s", ctx->path, formatted)); // Truncation OK.

    bson_free(formatted);
}

/* When matching two docs, and preparing to recurse to match two subdocs with
 * the given key, derive context for matching them from the current context. */
static void derive(match_ctx_t *ctx, match_ctx_t *derived, const char *key) {
    BSON_ASSERT(ctx);
    BSON_ASSERT(derived);
    BSON_ASSERT(key);

    derived->strict_numeric_types = ctx->strict_numeric_types;

    if (strlen(ctx->path) > 0) {
        BSON_ASSERT(0 < bson_snprintf(derived->path, sizeof derived->path, "%s.%s", ctx->path, key)); // Truncation OK.
    } else {
        BSON_ASSERT(0 < bson_snprintf(derived->path, sizeof derived->path, "%s", key)); // Truncation OK.
    }
    derived->retain_dots_in_keys = ctx->retain_dots_in_keys;
    derived->allow_placeholders = ctx->allow_placeholders;
    derived->visitor_ctx = ctx->visitor_ctx;
    derived->visitor_fn = ctx->visitor_fn;
    derived->is_command = false;
    derived->errmsg[0] = 0;
}

/*--------------------------------------------------------------------------
 *
 * match_bson_with_ctx --
 *
 *       Does "doc" match "pattern"?
 *
 *       mongoc_matcher_t prohibits $-prefixed keys, which is something
 *       we need to test in e.g. test_mongoc_client_read_prefs, so this
 *       does *not* use mongoc_matcher_t. Instead, "doc" matches "pattern"
 *       if its key-value pairs are a simple superset of pattern's. Order
 *       matters.
 *
 *       The only special pattern syntaxes are:
 *         "field": {"$exists": true/false}
 *         "field": {"$empty": true/false}
 *         "field": {"$$type": "type string"}
 *
 *       The first key matches case-insensitively if ctx->is_command.
 *
 *       An optional match visitor (match_visitor_fn and match_visitor_ctx)
 *       can be set in ctx to provide custom matching behavior.
 *
 *       A NULL doc or NULL pattern means "{}".
 *
 * Returns:
 *       True or false.
 *
 * Side effects:
 *       None.
 *
 *--------------------------------------------------------------------------
 */

bool match_bson_with_ctx(const bson_t *doc, const bson_t *pattern, match_ctx_t *ctx) {
    bson_iter_t pattern_iter;
    const char *key;
    const bson_value_t *value;
    bool is_first = true;
    bool is_exists_operator;
    bool is_empty_operator;
    bool is_type_operator;
    bool exists;
    bool empty = false;
    bson_type_t bson_type = (bson_type_t)0;
    bool found;
    bson_iter_t doc_iter;
    bson_value_t doc_value;
    match_ctx_t derived;

    if (bson_empty0(pattern)) {
        /* matches anything */
        return true;
    }

    BSON_ASSERT(bson_iter_init(&pattern_iter, pattern));

    while (bson_iter_next(&pattern_iter)) {
        key = bson_iter_key(&pattern_iter);
        value = bson_iter_value(&pattern_iter);

        found = find(&doc_iter, doc, key, ctx->is_command, is_first, ctx->retain_dots_in_keys);
        if (found) {
            bson_value_copy(bson_iter_value(&doc_iter), &doc_value);
        }

        /* is value {"$exists": true} or {"$exists": false} ? */
        is_exists_operator = get_exists_operator(value, &exists);

        /* is value {"$empty": true} or {"$empty": false} ? */
        is_empty_operator = get_empty_operator(value, &empty);

        /* is value {"$$type": "string" } ? */
        is_type_operator = get_type_operator(value, &bson_type);

        derive(ctx, &derived, key);

        if (ctx->visitor_fn) {
            match_action_t action = ctx->visitor_fn(ctx, &pattern_iter, found ? &doc_iter : NULL);
            if (action == MATCH_ACTION_ABORT) {
                goto fail;
            } else if (action == MATCH_ACTION_SKIP) {
                goto next;
            }
        }

        if (value->value_type == BSON_TYPE_NULL && found) {
            /* pattern has "key": null, and "key" is in doc */
            if (doc_value.value_type != BSON_TYPE_NULL) {
                match_err(&derived, "%s should be null or absent", key);
                goto fail;
            }
        } else if (is_exists_operator) {
            if (exists != found) {
                match_err(&derived, "%s found", found ? "" : "not");
                goto fail;
            }
        } else if (!found) {
            match_err(&derived, "key '%s' not found", key);
            goto fail;
        } else if (is_empty_operator) {
            if (empty != is_empty_doc_or_array(&doc_value)) {
                match_err(&derived, "%s found", empty ? "" : " not");
                goto fail;
            }
        } else if (is_type_operator) {
            if (doc_value.value_type != bson_type) {
                match_err(&derived, "incorrect type");
                goto fail;
            }
        } else if (!match_bson_value(&doc_value, value, &derived)) {
            goto fail;
        }

    next:
        is_first = false;
        if (found) {
            bson_value_destroy(&doc_value);
        }
    }

    return true;

fail:
    if (found) {
        bson_value_destroy(&doc_value);
    }

    if (strlen(derived.errmsg) > 0) {
        memcpy(ctx->errmsg, derived.errmsg, sizeof(derived.errmsg));
    }

    return false;
}

/*--------------------------------------------------------------------------
 *
 * find --
 *
 *       Find the value for a key.
 *
 * Returns:
 *       Whether the key was found.
 *
 * Side effects:
 *       Copies the found value into "iter_out".
 *
 *--------------------------------------------------------------------------
 */

static bool find(bson_iter_t *iter_out,
                 const bson_t *doc,
                 const char *key,
                 bool is_command,
                 bool is_first,
                 bool retain_dots_in_keys) {
    bson_iter_t iter;
    bson_iter_t descendent;

    bson_iter_init(&iter, doc);

    if (!retain_dots_in_keys && strchr(key, '.')) {
        if (!bson_iter_find_descendant(&iter, key, &descendent)) {
            return false;
        }

        memcpy(iter_out, &descendent, sizeof(bson_iter_t));
        return true;
    } else if (is_command && is_first) {
        if (!bson_iter_find_case(&iter, key)) {
            return false;
        }
    } else if (!bson_iter_find(&iter, key)) {
        return false;
    }

    memcpy(iter_out, &iter, sizeof(bson_iter_t));
    return true;
}

bool bson_init_from_value(bson_t *b, const bson_value_t *v) {
    BSON_ASSERT(v->value_type == BSON_TYPE_ARRAY || v->value_type == BSON_TYPE_DOCUMENT);

    return bson_init_static(b, v->value.v_doc.data, v->value.v_doc.data_len);
}

static bool _is_operator(const char *op_name, const bson_value_t *value, bool *op_val) {
    bson_t bson;
    bson_iter_t iter;

    if (value->value_type == BSON_TYPE_DOCUMENT && bson_init_from_value(&bson, value)
        && bson_iter_init_find(&iter, &bson, op_name)) {
        *op_val = bson_iter_as_bool(&iter);
        return true;
    }

    return false;
}

/*--------------------------------------------------------------------------
 *
 * get_exists_operator --
 *
 *       Is value a subdocument like {"$exists": bool}?
 *
 * Returns:
 *       True if the value is a subdocument with the first key "$exists",
 *       or if value is BSON null.
 *
 * Side effects:
 *       If the function returns true, *exists is set to true or false,
 *       the value of the bool.
 *
 *--------------------------------------------------------------------------
 */

static bool get_exists_operator(const bson_value_t *value, bool *exists) {
    if (_is_operator("$exists", value, exists)) {
        return true;
    }

    if (value->value_type == BSON_TYPE_NULL) {
        *exists = false;
        return true;
    }

    return false;
}

/*--------------------------------------------------------------------------
 *
 * get_empty_operator --
 *
 *       Is value a subdocument like {"$empty": bool}?
 *
 * Returns:
 *       True if the value is a subdocument with the first key "$empty".
 *
 * Side effects:
 *       If the function returns true, *empty is set to true or false,
 *       the value of the bool.
 *
 *--------------------------------------------------------------------------
 */

bool get_empty_operator(const bson_value_t *value, bool *empty) {
    return _is_operator("$empty", value, empty);
}

/*--------------------------------------------------------------------------
 *
 * get_type_operator --
 *
 *       Is value a subdocument like {"$$type": "BSON type string"}?
 *
 * Returns:
 *       True if the value is a subdocument with the first key "$$type",
 *       and sets the @bson_type.
 *
 * Side effects:
 *       If the function returns true, *@bson_type is set.
 *
 *--------------------------------------------------------------------------
 */

static bool get_type_operator(const bson_value_t *value, bson_type_t *out) {
    bson_t bson;
    bson_iter_t iter;
    const char *value_string;

    /* See list of aliases on this page:
     * https://docs.mongodb.com/manual/reference/bson-types/ */
    if (value->value_type == BSON_TYPE_DOCUMENT && bson_init_from_value(&bson, value)
        && bson_iter_init_find(&iter, &bson, "$$type")) {
        value_string = bson_iter_utf8(&iter, NULL);
        if (0 == strcasecmp("double", value_string)) {
            *out = BSON_TYPE_DOUBLE;
        } else if (0 == strcasecmp("string", value_string)) {
            *out = BSON_TYPE_UTF8;
        } else if (0 == strcasecmp("object", value_string)) {
            *out = BSON_TYPE_DOCUMENT;
        } else if (0 == strcasecmp("array", value_string)) {
            *out = BSON_TYPE_ARRAY;
        } else if (0 == strcasecmp("binData", value_string)) {
            *out = BSON_TYPE_BINARY;
        } else if (0 == strcasecmp("undefined", value_string)) {
            *out = BSON_TYPE_UNDEFINED;
        } else if (0 == strcasecmp("objectId", value_string)) {
            *out = BSON_TYPE_OID;
        } else if (0 == strcasecmp("bool", value_string)) {
            *out = BSON_TYPE_BOOL;
        } else if (0 == strcasecmp("date", value_string)) {
            *out = BSON_TYPE_DATE_TIME;
        } else if (0 == strcasecmp("null", value_string)) {
            *out = BSON_TYPE_NULL;
        } else if (0 == strcasecmp("regex", value_string)) {
            *out = BSON_TYPE_REGEX;
        } else if (0 == strcasecmp("dbPointer", value_string)) {
            *out = BSON_TYPE_DBPOINTER;
        } else if (0 == strcasecmp("javascript", value_string)) {
            *out = BSON_TYPE_CODE;
        } else if (0 == strcasecmp("symbol", value_string)) {
            *out = BSON_TYPE_SYMBOL;
        } else if (0 == strcasecmp("javascriptWithScope", value_string)) {
            *out = BSON_TYPE_CODEWSCOPE;
        } else if (0 == strcasecmp("int", value_string)) {
            *out = BSON_TYPE_INT32;
        } else if (0 == strcasecmp("timestamp", value_string)) {
            *out = BSON_TYPE_TIMESTAMP;
        } else if (0 == strcasecmp("long", value_string)) {
            *out = BSON_TYPE_INT64;
        } else if (0 == strcasecmp("decimal", value_string)) {
            *out = BSON_TYPE_DECIMAL128;
        } else if (0 == strcasecmp("minKey", value_string)) {
            *out = BSON_TYPE_MINKEY;
        } else if (0 == strcasecmp("maxKey", value_string)) {
            *out = BSON_TYPE_MAXKEY;
        } else {
            TEST_STDERR_PRINTF("unrecognized $$type value: %s\n", value_string);
            abort();
        }
        return true;
    }

    return false;
}

/*--------------------------------------------------------------------------
 *
 * is_empty_doc_or_array --
 *
 *       Is value the subdocument {} or the array []?
 *
 *--------------------------------------------------------------------------
 */

static bool is_empty_doc_or_array(const bson_value_t *value) {
    bson_t doc;

    if (!(value->value_type == BSON_TYPE_ARRAY || value->value_type == BSON_TYPE_DOCUMENT)) {
        return false;
    }
    BSON_ASSERT(bson_init_static(&doc, value->value.v_doc.data, value->value.v_doc.data_len));

    return bson_count_keys(&doc) == 0;
}

static bool match_bson_arrays(const bson_t *array, const bson_t *pattern, match_ctx_t *ctx) {
    uint32_t array_count;
    uint32_t pattern_count;
    bson_iter_t array_iter;
    bson_iter_t pattern_iter;
    const bson_value_t *array_value;
    const bson_value_t *pattern_value;
    match_ctx_t derived;

    array_count = bson_count_keys(array);
    pattern_count = bson_count_keys(pattern);

    if (array_count != pattern_count) {
        match_err(ctx, "expected %" PRIu32 " keys, not %" PRIu32, pattern_count, array_count);
        return false;
    }

    BSON_ASSERT(bson_iter_init(&array_iter, array));
    BSON_ASSERT(bson_iter_init(&pattern_iter, pattern));

    while (bson_iter_next(&array_iter)) {
        BSON_ASSERT(bson_iter_next(&pattern_iter));
        array_value = bson_iter_value(&array_iter);
        pattern_value = bson_iter_value(&pattern_iter);

        derive(ctx, &derived, bson_iter_key(&array_iter));

        if (!match_bson_value(array_value, pattern_value, &derived)) {
            // Propagate error message.
            if (strlen(derived.errmsg) > 0) {
                memcpy(ctx->errmsg, derived.errmsg, sizeof(derived.errmsg));
            }
            return false;
        }
    }

    return true;
}

static bool is_number_type(bson_type_t t) {
    if (t == BSON_TYPE_DOUBLE || t == BSON_TYPE_INT32 || t == BSON_TYPE_INT64) {
        return true;
    }

    return false;
}

int64_t bson_value_as_int64(const bson_value_t *value) {
    if (value->value_type == BSON_TYPE_DOUBLE) {
        return (int64_t)value->value.v_double;
    } else if (value->value_type == BSON_TYPE_INT32) {
        return (int64_t)value->value.v_int32;
    } else if (value->value_type == BSON_TYPE_INT64) {
        return value->value.v_int64;
    } else {
        return -123;
    }
}

bool match_bson_value(const bson_value_t *doc, const bson_value_t *pattern, match_ctx_t *ctx) {
    bson_t subdoc;
    bson_t pattern_subdoc;
    int64_t doc_int64;
    int64_t pattern_int64;
    bool ret = false;

    if (ctx && ctx->allow_placeholders) {
        /* The change streams spec tests use the value 42 as a placeholder. */
        bool is_placeholder = false;
        if (is_number_type(pattern->value_type) && bson_value_as_int64(pattern) == 42) {
            is_placeholder = true;
        }
        if (pattern->value_type == BSON_TYPE_UTF8 && !strcmp(pattern->value.v_utf8.str, "42")) {
            is_placeholder = true;
        }
        if (is_placeholder) {
            return true;
        }
    }

    if (is_number_type(doc->value_type) && is_number_type(pattern->value_type) && ctx && !ctx->strict_numeric_types) {
        doc_int64 = bson_value_as_int64(doc);
        pattern_int64 = bson_value_as_int64(pattern);

        if (doc_int64 != pattern_int64) {
            match_err(ctx, "expected %" PRId64 ", got %" PRId64, pattern_int64, doc_int64);
            return false;
        }

        return true;
    }

    if (doc->value_type != pattern->value_type) {
        match_err(ctx,
                  "expected type %s, got %s",
                  _mongoc_bson_type_to_str(pattern->value_type),
                  _mongoc_bson_type_to_str(doc->value_type));
        return false;
    }

    switch (doc->value_type) {
    case BSON_TYPE_ARRAY:
    case BSON_TYPE_DOCUMENT:

        if (!bson_init_from_value(&subdoc, doc)) {
            return false;
        }

        if (!bson_init_from_value(&pattern_subdoc, pattern)) {
            bson_destroy(&subdoc);
            return false;
        }

        if (doc->value_type == BSON_TYPE_ARRAY) {
            ret = match_bson_arrays(&subdoc, &pattern_subdoc, ctx);
        } else {
            ret = match_bson_with_ctx(&subdoc, &pattern_subdoc, ctx);
        }

        bson_destroy(&subdoc);
        bson_destroy(&pattern_subdoc);

        return ret;

    case BSON_TYPE_BINARY:
        ret = doc->value.v_binary.data_len == pattern->value.v_binary.data_len
           && !memcmp(doc->value.v_binary.data, pattern->value.v_binary.data, doc->value.v_binary.data_len);
        break;

    case BSON_TYPE_BOOL:
        ret = doc->value.v_bool == pattern->value.v_bool;

        if (!ret) {
            match_err(ctx, "expected %d, got %d", pattern->value.v_bool, doc->value.v_bool);
        }

        return ret;

    case BSON_TYPE_CODE:
        ret = doc->value.v_code.code_len == pattern->value.v_code.code_len
           && !memcmp(doc->value.v_code.code, pattern->value.v_code.code, doc->value.v_code.code_len);

        break;

    case BSON_TYPE_CODEWSCOPE:
        ret = doc->value.v_codewscope.code_len == pattern->value.v_codewscope.code_len
           && !memcmp(doc->value.v_codewscope.code, pattern->value.v_codewscope.code, doc->value.v_codewscope.code_len)
           && doc->value.v_codewscope.scope_len == pattern->value.v_codewscope.scope_len
           && !memcmp(doc->value.v_codewscope.scope_data,
                      pattern->value.v_codewscope.scope_data,
                      doc->value.v_codewscope.scope_len);

        break;

    case BSON_TYPE_DATE_TIME:
        ret = doc->value.v_datetime == pattern->value.v_datetime;

        if (!ret) {
            match_err(ctx, "expected %" PRId64 ", got %" PRId64, pattern->value.v_datetime, doc->value.v_datetime);
        }

        return ret;

    case BSON_TYPE_DOUBLE:
        ret = doc->value.v_double == pattern->value.v_double;

        if (!ret) {
            match_err(ctx, "expected %f, got %f", pattern->value.v_double, doc->value.v_double);
        }

        return ret;

    case BSON_TYPE_INT32:
        ret = doc->value.v_int32 == pattern->value.v_int32;

        if (!ret) {
            match_err(ctx, "expected %" PRId32 ", got %" PRId32, pattern->value.v_int32, doc->value.v_int32);
        }

        return ret;

    case BSON_TYPE_INT64:
        ret = doc->value.v_int64 == pattern->value.v_int64;

        if (!ret) {
            match_err(ctx, "expected %" PRId64 ", got %" PRId64, pattern->value.v_int64, doc->value.v_int64);
        }

        return ret;

    case BSON_TYPE_OID: ret = bson_oid_equal(&doc->value.v_oid, &pattern->value.v_oid); break;

    case BSON_TYPE_REGEX:
        ret = !strcmp(doc->value.v_regex.regex, pattern->value.v_regex.regex)
           && !strcmp(doc->value.v_regex.options, pattern->value.v_regex.options);

        break;

    case BSON_TYPE_SYMBOL:
        ret = doc->value.v_symbol.len == pattern->value.v_symbol.len
           && !strncmp(doc->value.v_symbol.symbol, pattern->value.v_symbol.symbol, doc->value.v_symbol.len);

        break;

    case BSON_TYPE_TIMESTAMP:
        ret = doc->value.v_timestamp.timestamp == pattern->value.v_timestamp.timestamp
           && doc->value.v_timestamp.increment == pattern->value.v_timestamp.increment;

        break;

    case BSON_TYPE_UTF8:
        ret = doc->value.v_utf8.len == pattern->value.v_utf8.len
           && !strncmp(doc->value.v_utf8.str, pattern->value.v_utf8.str, doc->value.v_utf8.len);

        if (!ret) {
            match_err(ctx, "expected \"%s\", got \"%s\"", pattern->value.v_utf8.str, doc->value.v_utf8.str);
        }

        return ret;

    /* these are empty types, if "a" and "b" are the same type they're equal */
    case BSON_TYPE_EOD:
    case BSON_TYPE_MAXKEY:
    case BSON_TYPE_MINKEY:
    case BSON_TYPE_NULL:
    case BSON_TYPE_UNDEFINED: return true;

    case BSON_TYPE_DBPOINTER:
        ret = (0 == strcmp(doc->value.v_dbpointer.collection, pattern->value.v_dbpointer.collection)
               && bson_oid_equal(&doc->value.v_dbpointer.oid, &pattern->value.v_dbpointer.oid));
        break;

    case BSON_TYPE_DECIMAL128:
        ret = (doc->value.v_decimal128.low == pattern->value.v_decimal128.low
               && doc->value.v_decimal128.high == pattern->value.v_decimal128.high);
        if (!ret) {
            match_err(ctx,
                      "Decimal128 is not an exact binary match (though "
                      "numeric values may be equal)");
        }
        break;
    default:
        match_err(ctx, "unexpected value type %d: %s", (int)doc->value_type, _mongoc_bson_type_to_str(doc->value_type));
    }

    if (!ret) {
        match_err(ctx, "%s values mismatch", _mongoc_bson_type_to_str(pattern->value_type));
    }

    return ret;
}

const char *_mongoc_bson_type_to_str(bson_type_t t) {
    switch (t) {
    case BSON_TYPE_EOD: return "EOD";
    case BSON_TYPE_DOUBLE: return "DOUBLE";
    case BSON_TYPE_UTF8: return "UTF8";
    case BSON_TYPE_DOCUMENT: return "DOCUMENT";
    case BSON_TYPE_ARRAY: return "ARRAY";
    case BSON_TYPE_BINARY: return "BINARY";
    case BSON_TYPE_UNDEFINED: return "UNDEFINED";
    case BSON_TYPE_OID: return "OID";
    case BSON_TYPE_BOOL: return "BOOL";
    case BSON_TYPE_DATE_TIME: return "DATE_TIME";
    case BSON_TYPE_NULL: return "NULL";
    case BSON_TYPE_REGEX: return "REGEX";
    case BSON_TYPE_DBPOINTER: return "DBPOINTER";
    case BSON_TYPE_CODE: return "CODE";
    case BSON_TYPE_SYMBOL: return "SYMBOL";
    case BSON_TYPE_CODEWSCOPE: return "CODEWSCOPE";
    case BSON_TYPE_INT32: return "INT32";
    case BSON_TYPE_TIMESTAMP: return "TIMESTAMP";
    case BSON_TYPE_INT64: return "INT64";
    case BSON_TYPE_MAXKEY: return "MAXKEY";
    case BSON_TYPE_MINKEY: return "MINKEY";
    case BSON_TYPE_DECIMAL128: return "DECIMAL128";
    default: return "Unknown";
    }
}

bool _check_match_bson(const bson_t *doc, const bson_t *pattern, char *errmsg, size_t errmsg_len) {
    // Set `retain_dots_in_keys` to interpret a pattern key "db.test" as a key, rather than a key path.
    match_ctx_t ctx = {.retain_dots_in_keys = true};
    bool matched = match_bson_with_ctx(doc, pattern, &ctx);
    if (matched) {
        bson_strncpy(errmsg, "", errmsg_len);
    } else {
        if (matched) {
            bson_strncpy(errmsg, ctx.errmsg, errmsg_len);
        }
    }
    return matched;
}
libmongocrypt-1.19.0/test/test-mongocrypt-assert-match-bson.h000066400000000000000000000062131521103432300243240ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef TEST_MONGOCRYPT_ASSERT_MATCH_BSON_H
#define TEST_MONGOCRYPT_ASSERT_MATCH_BSON_H

#include 

/* Copied from libmongoc. */
bool _check_match_bson(const bson_t *doc, const bson_t *pattern, char *errmsg, size_t errmsg_len);

#define _assert_match_bson(doc, pattern)                                                                               \
    if (1) {                                                                                                           \
        char errmsg[1024] = "";                                                                                        \
        if (!_check_match_bson(doc, pattern, errmsg, sizeof(errmsg))) {                                                \
            char *doc_str = bson_as_relaxed_extended_json(doc, NULL);                                                  \
            char *pattern_str = bson_as_relaxed_extended_json(pattern, NULL);                                          \
                                                                                                                       \
            TEST_ERROR("ASSERT_MATCH failed with document:\n\n"                                                        \
                       "%s\n"                                                                                          \
                       "pattern:\n%s\n"                                                                                \
                       "%s\n",                                                                                         \
                       doc_str ? doc_str : "{}",                                                                       \
                       pattern_str,                                                                                    \
                       errmsg);                                                                                        \
                                                                                                                       \
            bson_free(doc_str);                                                                                        \
            bson_free(pattern_str);                                                                                    \
        }                                                                                                              \
    } else                                                                                                             \
        (void)0

#endif /* TEST_MONGOCRYPT_ASSERT_MATCH_BSON_H */
libmongocrypt-1.19.0/test/test-mongocrypt-assert.h000066400000000000000000000463541521103432300223050ustar00rootroot00000000000000/*
 * 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.
 */

#ifndef TEST_MONGOCRYPT_ASSERT_H
#define TEST_MONGOCRYPT_ASSERT_H

#include "test-mongocrypt-util.h"

#include "mongocrypt.h"

#include 

#include 

// TEST_PRINTF ensures stdout and stderr are flushed.
#define TEST_PRINTF(...)                                                                                               \
    if (1) {                                                                                                           \
        fflush(stderr);                                                                                                \
        fprintf(stdout, __VA_ARGS__);                                                                                  \
        fflush(stdout);                                                                                                \
    } else                                                                                                             \
        ((void)0)

// TEST_STDERR_PRINTF ensures stdout and stderr are flushed.
#define TEST_STDERR_PRINTF(...)                                                                                        \
    if (1) {                                                                                                           \
        fflush(stdout);                                                                                                \
        fprintf(stderr, __VA_ARGS__);                                                                                  \
        fflush(stderr);                                                                                                \
    } else                                                                                                             \
        ((void)0)

#define TEST_ERROR(...)                                                                                                \
    if (1) {                                                                                                           \
        TEST_STDERR_PRINTF("test error %s:%d %s(): ", __FILE__, __LINE__, __func__);                                   \
        TEST_STDERR_PRINTF(__VA_ARGS__);                                                                               \
        TEST_STDERR_PRINTF("\n");                                                                                      \
        abort();                                                                                                       \
    } else                                                                                                             \
        ((void)0)

#define ASSERT(stmt)                                                                                                   \
    if (!(stmt)) {                                                                                                     \
        TEST_ERROR("statement failed %s", #stmt);                                                                      \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_OR_PRINT_MSG(_statement, msg)                                                                           \
    if (1) {                                                                                                           \
        if (!(_statement)) {                                                                                           \
            TEST_ERROR("%s failed with msg: %s", #_statement, (msg));                                                  \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_OR_PRINT(_statement, _err) ASSERT_OR_PRINT_MSG(_statement, mongocrypt_status_message(_err, NULL))

#define ASSERT_OK_STATUS(_stmt, _status)                                                                               \
    if (1) {                                                                                                           \
        bool _retval = (_stmt);                                                                                        \
        bool _status_ok = mongocrypt_status_ok(_status);                                                               \
        const char *_msg = mongocrypt_status_message(_status, NULL);                                                   \
        if (!_retval) {                                                                                                \
            TEST_ERROR("%s failed with msg: %s\n", #_stmt, _msg);                                                      \
        } else if (!_status_ok) {                                                                                      \
            TEST_ERROR("%s resulted in unexpected error status: %s\n", #_stmt, _msg);                                  \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_FAILS_STATUS(_stmt, _status, _msg_pattern)                                                              \
    if (1) {                                                                                                           \
        bool _retval = (_stmt);                                                                                        \
        bool _status_ok = mongocrypt_status_ok(_status);                                                               \
        const char *_msg = mongocrypt_status_message(_status, NULL);                                                   \
        bool _found_msg = _msg && strstr(_msg, _msg_pattern) != NULL;                                                  \
        if (_retval) {                                                                                                 \
            TEST_ERROR("%s succeeded (but should have failed) with msg: '%s'\n", #_stmt, _msg_pattern);                \
        } else if (_status_ok) {                                                                                       \
            TEST_ERROR("%s resulted in unexpected ok status: %s\n", #_stmt, _msg);                                     \
        } else if (!_found_msg) {                                                                                      \
            TEST_ERROR("'%s' does not contain '%s'\n", _msg, _msg_pattern);                                            \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_OK(_stmt, _obj) ASSERT_OK_STATUS(_stmt, (_obj)->status)

#define ASSERT_FAILS(_stmt, _obj, _msg_pattern) ASSERT_FAILS_STATUS(_stmt, (_obj)->status, _msg_pattern)

#define ASSERT_OR_PRINT_BSON(_statement, _err) ASSERT_OR_PRINT_MSG(_statement, _err.message)

#define ASSERT_STATUS_CONTAINS(status, _msg_pattern) ASSERT_FAILS_STATUS(false, status, _msg_pattern)

#define ASSERT_STREQUAL(_expr_a, _expr_b)                                                                              \
    if (1) {                                                                                                           \
        const char *_str_a = (_expr_a);                                                                                \
        const char *_str_b = (_expr_b);                                                                                \
        ASSERT(_str_a);                                                                                                \
        ASSERT(_str_b);                                                                                                \
        int _ret = strcmp(_str_a, _str_b);                                                                             \
        if (_ret != 0) {                                                                                               \
            TEST_ERROR("strings not equal:\n%s\nvs.\n%s\n", _str_a, _str_b);                                           \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_STRCONTAINS(_expr_a, _expr_b)                                                                           \
    if (1) {                                                                                                           \
        const char *_str_a = (_expr_a);                                                                                \
        const char *_str_b = (_expr_b);                                                                                \
        char *_ret = strstr(_str_a, _str_b);                                                                           \
        if (_ret == NULL) {                                                                                            \
            TEST_ERROR("string %s does not contain %s\n", _str_a, _str_b);                                             \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_STATE_EQUAL(actual, expected)                                                                           \
    if (1) {                                                                                                           \
        if (actual != expected) {                                                                                      \
            TEST_ERROR("actual state: %s, but expected state: %s\n",                                                   \
                       mongocrypt_ctx_state_to_string(actual),                                                         \
                       mongocrypt_ctx_state_to_string(expected));                                                      \
            abort();                                                                                                   \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_CMPBYTES(expected_bytes, expected_len, actual_bytes, actual_len)                                        \
    if (1) {                                                                                                           \
        char *_actual_hex = data_to_hex(actual_bytes, actual_len);                                                     \
        char *_expected_hex = data_to_hex(expected_bytes, expected_len);                                               \
        ASSERT_STREQUAL(_actual_hex, _expected_hex);                                                                   \
        free(_actual_hex);                                                                                             \
        free(_expected_hex);                                                                                           \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_CMPBUF(expected, actual) ASSERT_CMPBYTES((expected).data, (expected).len, (actual).data, (actual).len)

#define ASSERT_CMP_HELPER(_a, _operator, _b, fmt, type)                                                                \
    if (1) {                                                                                                           \
        type _a_value = (_a);                                                                                          \
        type _b_value = (_b);                                                                                          \
        if (!(_a_value _operator _b_value)) {                                                                          \
            TEST_ERROR("comparison failed: %" fmt " %s %" fmt, _a_value, #_operator, _b_value);                        \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_CMPINT(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "d", int)
#define ASSERT_CMPUINT(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "u", unsigned int)
#define ASSERT_CMPLONG(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "ld", long)
#define ASSERT_CMPULONG(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "lu", unsigned long)
#define ASSERT_CMPINT8(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRId8, int8_t)
#define ASSERT_CMPINT32(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRId32, int32_t)
#define ASSERT_CMPINT64(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRId64, int64_t)
#define ASSERT_CMPUINT8(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRIu8, uint8_t)
#define ASSERT_CMPUINT16(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRIu16, uint16_t)
#define ASSERT_CMPUINT32(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRIu32, uint32_t)
#define ASSERT_CMPUINT64(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, PRIu64, uint64_t)
#define ASSERT_CMPSIZE_T(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "zu", size_t)
#define ASSERT_CMPSSIZE_T(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "zd", ssize_t)
#define ASSERT_CMPDOUBLE(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "f", double)
#define ASSERT_CMPPTR(a, eq, b) ASSERT_CMP_HELPER(a, eq, b, "p", const void *)

#define ASSERT_CMPINT128_EQ(A, B)                                                                                      \
    if (1) {                                                                                                           \
        mlib_int128 left = (A);                                                                                        \
        mlib_int128 right = (B);                                                                                       \
        const bool result = mlib_int128_eq(left, right);                                                               \
        if (!result) {                                                                                                 \
            TEST_ERROR("Comparison failed: %s == %s (%s != %s)",                                                       \
                       #A,                                                                                             \
                       #B,                                                                                             \
                       mlib_int128_format(left).str,                                                                   \
                       mlib_int128_format(right).str);                                                                 \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_EQUAL_BSON(expected, actual)                                                                            \
    if (1) {                                                                                                           \
        bson_t *_expected_bson = expected, *_actual_bson = actual;                                                     \
        char *_expected_str, *_actual_str;                                                                             \
        _expected_str = bson_as_canonical_extended_json(_expected_bson, NULL);                                         \
        _actual_str = bson_as_canonical_extended_json(_actual_bson, NULL);                                             \
        if (!bson_equal(_expected_bson, _actual_bson)) {                                                               \
            TEST_ERROR("BSON unequal.\nExpected: %s\n     Got: %s", _expected_str, _actual_str);                       \
        }                                                                                                              \
        bson_free(_actual_str);                                                                                        \
        bson_free(_expected_str);                                                                                      \
    } else                                                                                                             \
        ((void)0)

#define ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expected, actual)                                                          \
    if (1) {                                                                                                           \
        bson_t _expected_bson, _actual_bson;                                                                           \
        char *_expected_str, *_actual_str;                                                                             \
        ASSERT(_mongocrypt_binary_to_bson(expected, &_expected_bson));                                                 \
        ASSERT(_mongocrypt_binary_to_bson(actual, &_actual_bson));                                                     \
        _expected_str = bson_as_canonical_extended_json(&_expected_bson, NULL);                                        \
        _actual_str = bson_as_canonical_extended_json(&_actual_bson, NULL);                                            \
        if (!bson_equal(&_expected_bson, &_actual_bson)) {                                                             \
            TEST_ERROR("BSON unequal.\nExpected: %s\n     Got: %s", _expected_str, _actual_str);                       \
        }                                                                                                              \
        bson_free(_actual_str);                                                                                        \
        bson_free(_expected_str);                                                                                      \
    } else                                                                                                             \
        ((void)0)

#endif /* TEST_MONGOCRYPT_ASSERT_H */
libmongocrypt-1.19.0/test/test-mongocrypt-buffer.c000066400000000000000000000250671521103432300222460ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 

#include "mongocrypt-buffer-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

#include "mongocrypt-endian-private.h"

#define TEST_STRING "?????" /* 3F 3F 3F 3F 3F */
#define TEST_INT 5555555    /* 54 C5 63 */

static void _get_bytes(const void *in, char *out, int len) {
    const unsigned char *src = in;
    char *dest = out;

    for (int i = 0; i < len; i++, dest += 3) {
        ASSERT(3 == bson_snprintf(dest, 4, "%02X ", src[i]));
    }
    dest[-1] = '\0';
}

static bool assert_excess_bytes_removed(char *key, char *wrapped, char *unwrapped, uint32_t type, bson_value_t *out) {
    _mongocrypt_buffer_t plaintext = {0};
    _mongocrypt_marking_t marking = {0};
    bson_iter_t iter;
    bson_t wrapper = BSON_INITIALIZER;
    char actual[100] = {0};
    bool ret = false;
    bson_t bson = BSON_INITIALIZER;

    BSON_APPEND_UTF8(&bson, "str_key", TEST_STRING);
    BSON_APPEND_INT32(&bson, "int_key", TEST_INT);

    bson_iter_init_find(&iter, &bson, key);
    memcpy(&marking.u.fle1.v_iter, &iter, sizeof(bson_iter_t));

    bson_append_iter(&wrapper, "", 0, &marking.u.fle1.v_iter);
    _get_bytes(bson_get_data(&wrapper), actual, wrapper.len);
    BSON_ASSERT(0 == strcmp(wrapped, actual));

    _mongocrypt_buffer_from_iter(&plaintext, &(&marking)->u.fle1.v_iter);
    _get_bytes(plaintext.data, actual, plaintext.len);
    BSON_ASSERT(0 == strcmp(unwrapped, actual));

    _mongocrypt_marking_cleanup(&marking);
    bson_destroy(&wrapper);

    ret = _mongocrypt_buffer_to_bson_value(&plaintext, type, out);

    _mongocrypt_buffer_cleanup(&plaintext);
    bson_destroy(&bson);
    return ret;
}

static void _test_mongocrypt_buffer_from_iter(_mongocrypt_tester_t *tester) {
    /*
     * This section explains the purpose of each byte in a BSON document. This is
     * used to extract only the value of a BSON document for later storage. Below
     * is an example of the leftmost derivation of one of the BSON documents
     * used for this test.
     *
     * NOTES:
     * - When used as a unary operator, * means that the repetition can occur 0
     *   or more times.
     *
     * - int32     4 bytes (32-bit signed integer, two's complement)
     * - (byte*)   Zero or more modified UTF-8 encoded characters followed by
     *             '\x00'. The (byte*) MUST NOT contain '\x00', hence it is
     *             not full UTF-8.
     *
     * RULES:
     * 1. document ::=  int32 e_list "\x00"     int32 is the total number of
     *                                          bytes comprising the doc.
     * 2. e_list   ::=  element e_list
     *              |   ""
     * 3. element  ::=  "\x02" e_name string    UTF-8 string
     *              |   "\x10" e_name int32 	  32-bit integer
     * 4. e_name   ::=  cstring                 Key name
     * 5. string   ::=  int32 (byte*) "\x00"
     * 6. cstring  ::=  (byte*) "\x00"
     *
     * BELOW IS A LEFTMOST DERIVATION:
     * Let doc = { "" : "?????" }
     *
     * -  doc  ::= int32 e_list "\x00"
     *
     * -- rule2 -> int32 element e_list "\x00"
     * -- rule3 -> int32 "\x02" e_name string e_list "\x00"
     * -- rule4 -> int32 "\x02" cstring string e_list "\x00"
     * -- rule6 -> int32 "\x02" (byte*) "\x00" string e_list "\x00"
     * -- key   -> int32 "\x02" "" "\x00" string e_list "\x00"
     ** The key is an empty string, i.e. 0 bytes **
     * -- rule5 -> int32 "\x02" "" "\x00" int32 (byte*) "\x00" e_list "\x00"
     * -- value -> int32 "\x02" "" "\x00" int32=6 "?????" "\x00" e_list "\x00"
     ** Above, the value is set. The int32 before the value is the size of the **
     ** value in bytes, plus one for the the null char. **
     * -- rule2 -> int32=17 "\x02" "" "\x00" int32=6 "?????" "\x00" "" "\x00"
     *
     * Finally, we have the byte sequence:
     *    "11000000 02 "" 00 06000000 "?????" 00 00"
     *
     * Note, the hexcode for '?' is '3F'. Grouping the sequence by byte for
     * readability, more precisely we have:
     *    "11 00 00 00 02 00 06 00 00 00 3F 3F 3F 3F 3F 00 00"
     *
     * with the value, including its length and null terminator being:
     *    "06 00 00 00 3F 3F 3F 3F 3F 00".
     * This is what we will store.
     */

    bson_t wrapper = BSON_INITIALIZER;
    bson_value_t out;

    BSON_ASSERT(assert_excess_bytes_removed("str_key",
                                            "11 00 00 00 02 00 06 00 00 00 3F 3F 3F 3F 3F 00 00",
                                            /** no prefix **/ "06 00 00 00 3F 3F 3F 3F 3F 00",
                                            0x02, /* string type */
                                            &out));

    BSON_ASSERT(out.value_type == BSON_TYPE_UTF8);
    BSON_ASSERT(0 == strcmp(TEST_STRING, out.value.v_utf8.str));
    BSON_ASSERT(5 == out.value.v_utf8.len);
    bson_value_destroy(&out);

    BSON_ASSERT(assert_excess_bytes_removed("int_key",
                                            "0B 00 00 00 10 00 63 C5 54 00 00",
                                            /** no prefix **/ "63 C5 54 00",
                                            0x10, /* int type */
                                            &out));

    BSON_ASSERT(out.value_type == BSON_TYPE_INT32);
    BSON_ASSERT(TEST_INT == out.value.v_int32);

    BSON_ASSERT(!assert_excess_bytes_removed("int_key",
                                             "0B 00 00 00 10 00 63 C5 54 00 00",
                                             /** no prefix **/ "63 C5 54 00",
                                             0x99, /* invalid type */
                                             &out));
    bson_destroy(&wrapper);
}

static void _test_mongocrypt_buffer_copy_from_data_and_size(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t buf;
    const uint8_t data[] = {0, 1, 2};

    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&buf, data, 3));
    ASSERT_CMPBYTES(data, sizeof(data), buf.data, buf.len);
    _mongocrypt_buffer_cleanup(&buf);
}

static void _test_mongocrypt_buffer_steal_from_data_and_size(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t buf;
    uint8_t *data = bson_malloc0(3);

    data[0] = 0;
    data[1] = 1;
    data[2] = 2;
    ASSERT(_mongocrypt_buffer_steal_from_data_and_size(&buf, data, 3));
    ASSERT_CMPBYTES(data, 3, buf.data, buf.len);
    _mongocrypt_buffer_cleanup(&buf);
}

static void _test_mongocrypt_buffer_steal_from_string(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t buf;
    char *str = bson_strdup("foo");

    ASSERT(_mongocrypt_buffer_steal_from_string(&buf, str));
    ASSERT_STREQUAL((const char *)buf.data, str);
    _mongocrypt_buffer_cleanup(&buf);
}

static void _test_mongocrypt_buffer_copy_from_uint64_le(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t expect;
    _mongocrypt_buffer_t got;

    _mongocrypt_buffer_copy_from_hex(&expect, "0100000000000000");
    _mongocrypt_buffer_copy_from_uint64_le(&got, 0x0000000000000001ULL);
    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&got);

    _mongocrypt_buffer_copy_from_hex(&expect, "1122334455667788");
    _mongocrypt_buffer_copy_from_uint64_le(&got, 0x8877665544332211ULL);
    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&got);
}

static void _test_mongocrypt_buffer_from_subrange(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t input;
    _mongocrypt_buffer_t expect;
    _mongocrypt_buffer_t got;

    _mongocrypt_buffer_copy_from_hex(&input, "010203");
    _mongocrypt_buffer_copy_from_hex(&expect, "01");
    ASSERT(_mongocrypt_buffer_from_subrange(&got, &input, 0, 1));
    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&input);

    _mongocrypt_buffer_copy_from_hex(&input, "010203");
    _mongocrypt_buffer_copy_from_hex(&expect, "010203");
    ASSERT(_mongocrypt_buffer_from_subrange(&got, &input, 0, 3));
    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&input);

    _mongocrypt_buffer_copy_from_hex(&input, "010203");
    _mongocrypt_buffer_copy_from_hex(&expect, "0203");
    ASSERT(_mongocrypt_buffer_from_subrange(&got, &input, 1, 2));
    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&input);

    _mongocrypt_buffer_copy_from_hex(&input, "010203");
    ASSERT(!_mongocrypt_buffer_from_subrange(&got, &input, 0, 4));
    _mongocrypt_buffer_cleanup(&input);
}

static void _test_mongocrypt_buffer_copy_from_string_as_bson_value(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t buf;
    _mongocrypt_buffer_t expectedLenBuf;
    const char *data = "foobar";

    // expect output to contain 4-byte length + data + null string terminator
    size_t expectedLen = sizeof(int32_t) + strlen(data) + sizeof(uint8_t);
    _mongocrypt_buffer_copy_from_hex(&expectedLenBuf, "07000000");

    _mongocrypt_buffer_copy_from_string_as_bson_value(&buf, data, (int)strlen(data));
    ASSERT(buf.len == expectedLen);

    // check 4-byte length
    ASSERT_CMPBYTES(expectedLenBuf.data, expectedLenBuf.len, buf.data, expectedLenBuf.len);
    // check data + null byte
    ASSERT_CMPBYTES((const uint8_t *)data,
                    strlen(data) + 1,
                    buf.data + expectedLenBuf.len,
                    buf.len - expectedLenBuf.len);
    _mongocrypt_buffer_cleanup(&buf);
    _mongocrypt_buffer_cleanup(&expectedLenBuf);
}

void _mongocrypt_tester_install_buffer(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mongocrypt_buffer_from_iter);
    INSTALL_TEST(_test_mongocrypt_buffer_copy_from_data_and_size);
    INSTALL_TEST(_test_mongocrypt_buffer_steal_from_data_and_size);
    INSTALL_TEST(_test_mongocrypt_buffer_steal_from_string);
    INSTALL_TEST(_test_mongocrypt_buffer_copy_from_uint64_le);
    INSTALL_TEST(_test_mongocrypt_buffer_from_subrange);
    INSTALL_TEST(_test_mongocrypt_buffer_copy_from_string_as_bson_value);
}
libmongocrypt-1.19.0/test/test-mongocrypt-cache-oauth.c000066400000000000000000000121651521103432300231510ustar00rootroot00000000000000/*
 * 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-cache-oauth-private.h"
#include "test-mongocrypt.h"

static void _test_cache_oauth_expiration(_mongocrypt_tester_t *tester) {
    mc_mapof_kmsid_to_token_t *cache;
    char *token;
    bool ret;
    mongocrypt_status_t *status;

    cache = mc_mapof_kmsid_to_token_new();
    token = mc_mapof_kmsid_to_token_get_token(cache, "aws");
    BSON_ASSERT(!token);

    status = mongocrypt_status_new();
    ret = mc_mapof_kmsid_to_token_add_response(cache,
                                               "aws",
                                               TMP_BSON("{'expires_in': 0, 'access_token': 'foo'}"),
                                               status);
    ASSERT_OR_PRINT(ret, status);
    /* Does not return expired token. */
    token = mc_mapof_kmsid_to_token_get_token(cache, "aws");
    BSON_ASSERT(!token);

    /* Attempt to get again, to ensure MONGOCRYPT-321 is fixed. */
    token = mc_mapof_kmsid_to_token_get_token(cache, "aws");
    BSON_ASSERT(!token);

    /* Add an unexpired token. */
    ret = mc_mapof_kmsid_to_token_add_response(cache,
                                               "aws",
                                               TMP_BSON("{'expires_in': 1000, 'access_token': 'bar'}"),
                                               status);
    ASSERT_OR_PRINT(ret, status);

    token = mc_mapof_kmsid_to_token_get_token(cache, "aws");
    ASSERT_STREQUAL(token, "bar");
    bson_free(token);

    mc_mapof_kmsid_to_token_destroy(cache);
    mongocrypt_status_destroy(status);
}

#define BSON_STR(...) #__VA_ARGS__

static void test_mc_mapof_kmsid_to_token(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    bson_t *response1 = TMP_BSON(BSON_STR({"access_token" : "foo", "expires_in" : 1234}));
    bson_t *response2 = TMP_BSON(BSON_STR({"access_token" : "bar", "expires_in" : 4567}));

    // Test inserting one entry.
    {
        mc_mapof_kmsid_to_token_t *k2t = mc_mapof_kmsid_to_token_new();
        ASSERT(NULL == mc_mapof_kmsid_to_token_get_token(k2t, "local:1"));
        ASSERT_OK_STATUS(mc_mapof_kmsid_to_token_add_response(k2t, "local:1", response1, status), status);
        char *got = mc_mapof_kmsid_to_token_get_token(k2t, "local:1");
        ASSERT_STREQUAL(got, "foo");
        bson_free(got);
        mc_mapof_kmsid_to_token_destroy(k2t);
    }

    // Test inserting two entries.
    {
        mc_mapof_kmsid_to_token_t *k2t = mc_mapof_kmsid_to_token_new();

        // Insert first.
        {
            ASSERT(NULL == mc_mapof_kmsid_to_token_get_token(k2t, "local:1"));
            ASSERT_OK_STATUS(mc_mapof_kmsid_to_token_add_response(k2t, "local:1", response1, status), status);
            char *got = mc_mapof_kmsid_to_token_get_token(k2t, "local:1");
            ASSERT_STREQUAL(got, "foo");
            bson_free(got);
        }

        // Insert second.
        {
            ASSERT(NULL == mc_mapof_kmsid_to_token_get_token(k2t, "local:2"));
            ASSERT_OK_STATUS(mc_mapof_kmsid_to_token_add_response(k2t, "local:2", response2, status), status);
            char *got = mc_mapof_kmsid_to_token_get_token(k2t, "local:2");
            ASSERT_STREQUAL(got, "bar");
            bson_free(got);
        }

        mc_mapof_kmsid_to_token_destroy(k2t);
    }

    // Test overwriting an entry.
    {
        mc_mapof_kmsid_to_token_t *k2t = mc_mapof_kmsid_to_token_new();

        // Insert first.
        {
            ASSERT(NULL == mc_mapof_kmsid_to_token_get_token(k2t, "local:1"));
            ASSERT_OK_STATUS(mc_mapof_kmsid_to_token_add_response(k2t, "local:1", response1, status), status);
            char *got = mc_mapof_kmsid_to_token_get_token(k2t, "local:1");
            ASSERT_STREQUAL(got, "foo");
            bson_free(got);
        }

        // Overwrite 'local:1' with a different token.
        {
            ASSERT_OK_STATUS(mc_mapof_kmsid_to_token_add_response(k2t, "local:1", response2, status), status);
            char *got = mc_mapof_kmsid_to_token_get_token(k2t, "local:1");
            ASSERT_STREQUAL(got, "bar");
            bson_free(got);
        }

        mc_mapof_kmsid_to_token_destroy(k2t);
    }
    // Test getting a missing entry.
    {
        mc_mapof_kmsid_to_token_t *k2t = mc_mapof_kmsid_to_token_new();
        ASSERT(NULL == mc_mapof_kmsid_to_token_get_token(k2t, "local:1"));
        mc_mapof_kmsid_to_token_destroy(k2t);
    }

    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_cache_oauth(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_cache_oauth_expiration);
    INSTALL_TEST(test_mc_mapof_kmsid_to_token);
}
libmongocrypt-1.19.0/test/test-mongocrypt-cache.c000066400000000000000000000206451521103432300220350ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-cache-collinfo-private.h"
#include "mongocrypt-crypto-private.h"
#include "test-mongocrypt.h"

static void _test_cache(_mongocrypt_tester_t *tester) {
    _mongocrypt_cache_t cache;
    mongocrypt_status_t *status;
    bson_t *entry = BCON_NEW("a", "b"), *entry2 = BCON_NEW("c", "d");
    bson_t *tmp = NULL;

    status = mongocrypt_status_new();

    _mongocrypt_cache_collinfo_init(&cache);

    /* Test get on an empty cache. */
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "1", (void **)&tmp));
    BSON_ASSERT(!tmp);

    /* Test set + get */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "1", entry, status), status);
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "1", (void **)&tmp));
    /* Assert we get a copy back. */
    BSON_ASSERT(entry != tmp);
    BSON_ASSERT(bson_equal(entry, tmp));
    bson_destroy(tmp);

    /* Test missing find. */
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "2", (void **)&tmp));
    BSON_ASSERT(!tmp);

    /* Test attempting to overwrite an entry. */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "1", entry2, status), status);
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "1", (void **)&tmp));
    /* Overwrite is ignored. */
    BSON_ASSERT(bson_equal(entry2, tmp));
    bson_destroy(tmp);

    /* Test with two entries in the cache. */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "2", entry2, status), status);
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "2", (void **)&tmp));
    BSON_ASSERT(bson_equal(entry2, tmp));
    bson_destroy(tmp);

    /* Test stealing an entry. */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_stolen(&cache, "3", entry, status), status);
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "3", (void **)&tmp));
    BSON_ASSERT(bson_equal(entry, tmp));
    bson_destroy(tmp);

    _mongocrypt_cache_cleanup(&cache);
    mongocrypt_status_destroy(status);
    bson_destroy(entry2);
}

static void _test_cache_expiration(_mongocrypt_tester_t *tester) {
    _mongocrypt_cache_t cache;
    mongocrypt_status_t *status;
    bson_t *entry = BCON_NEW("a", "b");
    bson_t *tmp = NULL;

    status = mongocrypt_status_new();

    _mongocrypt_cache_collinfo_init(&cache);
    _mongocrypt_cache_set_expiration(&cache, 1);
    /* Test set + get */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, "1", entry, status), status);
    BSON_ASSERT(_mongocrypt_cache_get(&cache, "1", (void **)&tmp));
    /* Assert we get a copy back. */
    BSON_ASSERT(entry != tmp);
    BSON_ASSERT(bson_equal(entry, tmp));
    bson_destroy(tmp);

    // Sleep to trigger cache expiration.
    // Cache entries expire after 1ms, but use 20ms to avoid timing errors observed on Windows distros: CDRIVER-4526
    _usleep(20 * 1000);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, "1", (void **)&tmp));
    BSON_ASSERT(!tmp);

    _mongocrypt_cache_cleanup(&cache);
    mongocrypt_status_destroy(status);
    bson_destroy(entry);
}

static void _test_cache_duplicates(_mongocrypt_tester_t *tester) {
    _mongocrypt_cache_t cache;
    mongocrypt_status_t *status;
    _mongocrypt_key_doc_t *placeholder_keydoc;
    _mongocrypt_cache_key_value_t *value1, *value2, *value3, *value4, *tmp;
    _mongocrypt_cache_key_attr_t *attr1, *attr2, *attr3, *attr4;
    _mongocrypt_buffer_t buf1, buf2, buf3, buf4; /* distinguished by first byte. */
    _mongocrypt_key_alt_name_t *alt_names1, *alt_names2, *alt_names3, *alt_names4;

    status = mongocrypt_status_new();

    _mongocrypt_buffer_init(&buf1);
    _mongocrypt_buffer_init(&buf2);
    _mongocrypt_buffer_init(&buf3);
    _mongocrypt_buffer_init(&buf4);

    _mongocrypt_buffer_resize(&buf1, MONGOCRYPT_KEY_LEN);
    _mongocrypt_buffer_resize(&buf2, MONGOCRYPT_KEY_LEN);
    _mongocrypt_buffer_resize(&buf3, MONGOCRYPT_KEY_LEN);
    _mongocrypt_buffer_resize(&buf4, MONGOCRYPT_KEY_LEN);

    buf1.data[0] = 1;
    buf2.data[0] = 2;
    buf3.data[0] = 3;
    buf4.data[0] = 4;

    placeholder_keydoc = _mongocrypt_key_new();

    value1 = _mongocrypt_cache_key_value_new(placeholder_keydoc, &buf1);
    value2 = _mongocrypt_cache_key_value_new(placeholder_keydoc, &buf2);
    value3 = _mongocrypt_cache_key_value_new(placeholder_keydoc, &buf3);
    value4 = _mongocrypt_cache_key_value_new(placeholder_keydoc, &buf4);

    alt_names1 = _MONGOCRYPT_KEY_ALT_NAME_CREATE("a", "b");
    alt_names2 = _MONGOCRYPT_KEY_ALT_NAME_CREATE("c", "d");
    alt_names3 = _MONGOCRYPT_KEY_ALT_NAME_CREATE("e");
    alt_names4 = _MONGOCRYPT_KEY_ALT_NAME_CREATE("a", "d");

    attr1 = _mongocrypt_cache_key_attr_new(NULL /* id */, alt_names1);
    attr2 = _mongocrypt_cache_key_attr_new(NULL /* id */, alt_names2);
    attr3 = _mongocrypt_cache_key_attr_new(NULL /* id */, alt_names3);
    attr4 = _mongocrypt_cache_key_attr_new(NULL /* id */, alt_names4);

    _mongocrypt_cache_key_init(&cache);

    /* add three non-intersecting entries */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, attr1, value1, status), status);
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, attr2, value2, status), status);
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, attr3, value3, status), status);
    BSON_ASSERT(_mongocrypt_cache_num_entries(&cache) == 3);

    /* all three should be in the cache */
    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr1, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 1);
    _mongocrypt_cache_key_value_destroy(tmp);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr2, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 2);
    _mongocrypt_cache_key_value_destroy(tmp);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr3, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 3);
    _mongocrypt_cache_key_value_destroy(tmp);

    /* add intersecting entry */
    ASSERT_OR_PRINT(_mongocrypt_cache_add_copy(&cache, attr4, value4, status), status);
    /* cache still only has 2 entries since two were deduplicated */
    BSON_ASSERT(_mongocrypt_cache_num_entries(&cache) == 2);

    /* attr1 and attr2 match the overwriting entry */
    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr1, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 4);
    _mongocrypt_cache_key_value_destroy(tmp);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr2, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 4);
    _mongocrypt_cache_key_value_destroy(tmp);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr3, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 3);
    _mongocrypt_cache_key_value_destroy(tmp);

    BSON_ASSERT(_mongocrypt_cache_get(&cache, attr4, (void **)&tmp));
    BSON_ASSERT(tmp);
    BSON_ASSERT(tmp->decrypted_key_material.data[0] == 4);
    _mongocrypt_cache_key_value_destroy(tmp);

    _mongocrypt_cache_key_attr_destroy(attr1);
    _mongocrypt_cache_key_attr_destroy(attr2);
    _mongocrypt_cache_key_attr_destroy(attr3);
    _mongocrypt_cache_key_attr_destroy(attr4);

    _mongocrypt_key_alt_name_destroy_all(alt_names1);
    _mongocrypt_key_alt_name_destroy_all(alt_names2);
    _mongocrypt_key_alt_name_destroy_all(alt_names3);
    _mongocrypt_key_alt_name_destroy_all(alt_names4);

    _mongocrypt_cache_key_value_destroy(value1);
    _mongocrypt_cache_key_value_destroy(value2);
    _mongocrypt_cache_key_value_destroy(value3);
    _mongocrypt_cache_key_value_destroy(value4);

    _mongocrypt_key_destroy(placeholder_keydoc);

    _mongocrypt_buffer_cleanup(&buf1);
    _mongocrypt_buffer_cleanup(&buf2);
    _mongocrypt_buffer_cleanup(&buf3);
    _mongocrypt_buffer_cleanup(&buf4);

    mongocrypt_status_destroy(status);
    _mongocrypt_cache_cleanup(&cache);
}

void _mongocrypt_tester_install_cache(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_cache);
    INSTALL_TEST(_test_cache_expiration);
    INSTALL_TEST(_test_cache_duplicates);
}
libmongocrypt-1.19.0/test/test-mongocrypt-ciphertext.c000066400000000000000000000216721521103432300231520ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-marking-private.h"
#include "mongocrypt-private.h"

#include "test-mongocrypt.h"

/* From BSON Binary subtype 6 specification:
struct fle_blob {
 uint8  fle_blob_subtype = (1 or 2);
 uint8  key_uuid[16];
 uint8  original_bson_type;
 uint8  ciphertext[ciphertext_length];
}
*/
static void _test_ciphertext_serialization(_mongocrypt_tester_t *tester) {
    _mongocrypt_ciphertext_t original, returned;
    _mongocrypt_buffer_t serialized;
    char *expected = "\x01\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C"
                     "\x0D\x0E\x0F\x02\x00\x01";
    mongocrypt_status_t *status;

    _mongocrypt_ciphertext_init(&original);
    _mongocrypt_ciphertext_init(&returned);
    _mongocrypt_buffer_init(&serialized);
    status = mongocrypt_status_new();

    original.blob_subtype = 1;
    original.original_bson_type = 2;
    _mongocrypt_tester_fill_buffer(&original.data, 2);
    _mongocrypt_tester_fill_buffer(&original.key_id, 16);

    BSON_ASSERT(_mongocrypt_serialize_ciphertext(&original, &serialized));
    BSON_ASSERT(0 == memcmp(expected, serialized.data, serialized.len));

    /* Now parse it back, should get the same ciphertext. */
    BSON_ASSERT(_mongocrypt_ciphertext_parse_unowned(&serialized, &returned, status));
    BSON_ASSERT(original.blob_subtype == returned.blob_subtype);
    BSON_ASSERT(original.original_bson_type == returned.original_bson_type);
    BSON_ASSERT(0 == _mongocrypt_buffer_cmp(&original.key_id, &returned.key_id));
    BSON_ASSERT(0 == _mongocrypt_buffer_cmp(&original.data, &returned.data));

    _mongocrypt_ciphertext_cleanup(&original);
    _mongocrypt_ciphertext_cleanup(&returned);
    _mongocrypt_buffer_cleanup(&serialized);
    mongocrypt_status_destroy(status);
}

static void _test_malformed_ciphertext(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t serialized;
    _mongocrypt_ciphertext_t returned;
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    /* the minimum size for a ciphertext is 19 bytes. */
    _mongocrypt_tester_fill_buffer(&serialized, 18);

    BSON_ASSERT(!_mongocrypt_ciphertext_parse_unowned(&serialized, &returned, status));
    BSON_ASSERT(0 == strcmp(mongocrypt_status_message(status, NULL), "malformed ciphertext, too small"));
    _mongocrypt_buffer_cleanup(&serialized);

    _mongocrypt_tester_fill_buffer(&serialized, 19);
    /* give a valid blob_subtype. */
    serialized.data[0] = 1;
    BSON_ASSERT(_mongocrypt_ciphertext_parse_unowned(&serialized, &returned, status));

    /* now an invalid blob_subtype. */
    serialized.data[0] = 16;
    BSON_ASSERT(!_mongocrypt_ciphertext_parse_unowned(&serialized, &returned, status));
    BSON_ASSERT(
        0 == strcmp(mongocrypt_status_message(status, NULL), "malformed ciphertext, expected blob subtype of 1 or 2"));

    _mongocrypt_ciphertext_cleanup(&returned);
    _mongocrypt_buffer_cleanup(&serialized);
    mongocrypt_status_destroy(status);
}

static void _test_ciphertext_algorithm(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_status_t *status;
    _mongocrypt_key_broker_t *kb;
    _mongocrypt_buffer_t zeros;
    _mongocrypt_ciphertext_t type1_valueA, type1_valueA_again, type2_valueA, type1_valueB;
    _mongocrypt_marking_t marking = {0};
    bson_iter_t a_iter, b_iter;
    bson_t *bson;
    bool res;

    status = mongocrypt_status_new();
    _mongocrypt_ciphertext_init(&type1_valueA);
    _mongocrypt_ciphertext_init(&type1_valueA_again);
    _mongocrypt_ciphertext_init(&type2_valueA);
    _mongocrypt_ciphertext_init(&type1_valueB);

    _mongocrypt_buffer_init(&zeros);
    _mongocrypt_buffer_resize(&zeros, MONGOCRYPT_IV_LEN);
    memset(zeros.data, 0, MONGOCRYPT_IV_LEN);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    /* use a mongocrypt_ctx_t to get a key broker */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);

    _mongocrypt_buffer_from_binary(&marking.u.fle1.key_id, TEST_BIN(16));
    marking.u.fle1.key_id.subtype = BSON_SUBTYPE_UUID;
    kb = &ctx->kb;
    _mongocrypt_key_broker_add_test_key(kb, &marking.u.fle1.key_id);

    bson = BCON_NEW("v", "a", "v", "b");
    bson_iter_init(&a_iter, bson);
    bson_iter_next(&a_iter);
    bson_iter_init(&b_iter, bson);
    bson_iter_next(&b_iter);
    bson_iter_next(&b_iter);

    /* Marking type = 1, plaintext = a */
    marking.u.fle1.algorithm = 1;
    memcpy(&marking.u.fle1.v_iter, &a_iter, sizeof(bson_iter_t));
    res = _mongocrypt_marking_to_ciphertext((void *)kb, &marking, &type1_valueA, status);
    ASSERT_OR_PRINT(res, status);

    /* Marking type = 1, plaintext = a */
    marking.u.fle1.algorithm = 1;
    memcpy(&marking.u.fle1.v_iter, &a_iter, sizeof(bson_iter_t));
    res = _mongocrypt_marking_to_ciphertext((void *)kb, &marking, &type1_valueA_again, status);
    ASSERT_OR_PRINT(res, status);

    /* Marking type = 2, plaintext = a */
    marking.u.fle1.algorithm = 2;
    memcpy(&marking.u.fle1.v_iter, &a_iter, sizeof(bson_iter_t));
    res = _mongocrypt_marking_to_ciphertext((void *)kb, &marking, &type2_valueA, status);
    ASSERT_OR_PRINT(res, status);

    /* Marking type = 1, plaintext = b */
    marking.u.fle1.algorithm = 1;
    memcpy(&marking.u.fle1.v_iter, &b_iter, sizeof(bson_iter_t));
    res = _mongocrypt_marking_to_ciphertext((void *)kb, &marking, &type1_valueB, status);
    ASSERT_OR_PRINT(res, status);

    /* Shorten all buffers to their IV length's */
    type1_valueA.data.len = MONGOCRYPT_IV_LEN;
    type1_valueA_again.data.len = MONGOCRYPT_IV_LEN;
    type2_valueA.data.len = MONGOCRYPT_IV_LEN;
    type1_valueB.data.len = MONGOCRYPT_IV_LEN;

    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type1_valueA.data, &zeros));
    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type1_valueA_again.data, &zeros));
    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type2_valueA.data, &zeros));
    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type1_valueB.data, &zeros));

    /* Type 1 IV should be repeatable for same plaintext. */
    BSON_ASSERT(0 == _mongocrypt_buffer_cmp(&type1_valueA.data, &type1_valueA_again.data));
    /* Type 1 IV should differ from type 2 random IV. */
    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type1_valueA.data, &type2_valueA.data));
    /* Type 1 IV should differ if plaintext differs. */
    BSON_ASSERT(0 != _mongocrypt_buffer_cmp(&type1_valueA.data, &type1_valueB.data));

    _mongocrypt_ciphertext_cleanup(&type1_valueA);
    _mongocrypt_ciphertext_cleanup(&type1_valueA_again);
    _mongocrypt_ciphertext_cleanup(&type2_valueA);
    _mongocrypt_ciphertext_cleanup(&type1_valueB);
    _mongocrypt_buffer_cleanup(&zeros);
    _mongocrypt_marking_cleanup(&marking);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_destroy(bson);
    mongocrypt_status_destroy(status);
}

static void _test_ciphertext_serialize_associated_data(_mongocrypt_tester_t *tester) {
    _mongocrypt_ciphertext_t ciphertext;
    _mongocrypt_buffer_t serialized;
    /* Expected associated data is:
     * One byte for blob subtype for deterministic encryption:
     * \x01
     * Followed by 16 byte UUID, a repeating 123 pattern:
     * \x01\x02\x03\x01\x02\x03\x01\x02\x03\x01\x02\x03\x01\x02\x03\x01
     * Followed by the BSON type for UTF8
     * \x02
     */
    char *expected = "\x01\x01\x02\x03\x01\x02\x03\x01\x02\x03\x01\x02\x03\x01"
                     "\x02\x03\x01\x02";

    _mongocrypt_ciphertext_init(&ciphertext);
    _mongocrypt_buffer_init(&serialized);

    /* Create a UUID */
    _mongocrypt_buffer_from_binary(&ciphertext.key_id, TEST_BIN(16));
    ciphertext.key_id.subtype = BSON_SUBTYPE_UUID;

    ciphertext.original_bson_type = BSON_TYPE_UTF8;
    ciphertext.blob_subtype = MC_SUBTYPE_FLE1DeterministicEncryptedValue;

    BSON_ASSERT(_mongocrypt_ciphertext_serialize_associated_data(&ciphertext, &serialized));
    BSON_ASSERT(serialized.len == 18);
    BSON_ASSERT(0 == memcmp(serialized.data, expected, strlen(expected)));

    _mongocrypt_ciphertext_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&serialized);
}

void _mongocrypt_tester_install_ciphertext(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_malformed_ciphertext);
    INSTALL_TEST(_test_ciphertext_serialization);
    INSTALL_TEST(_test_ciphertext_algorithm);
    INSTALL_TEST(_test_ciphertext_serialize_associated_data);
}
libmongocrypt-1.19.0/test/test-mongocrypt-cleanup.c000066400000000000000000000410601521103432300224130ustar00rootroot00000000000000/*
 * Copyright 2023-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 "test-mongocrypt.h"

static void _test_cleanup_success(_mongocrypt_tester_t *tester) {
    const char basepath[] = "./test/data/cleanup/";
    char datapath[1000];
    char cmdfile[1000];
    char collfile[1000];
    char payloadfile[1000];
    strcpy(datapath, basepath);
    size_t nullb = strlen(basepath);
    const char *simple_success_tests[] = {
        "success/",
        "text-search/",
    };
    for (size_t i = 0; i < sizeof(simple_success_tests) / sizeof(simple_success_tests[0]); i++) {
        datapath[nullb] = 0;
        strcat(datapath, simple_success_tests[i]);
        strcpy(cmdfile, datapath);
        strcat(cmdfile, "cmd.json");
        strcpy(collfile, datapath);
        strcat(collfile, "collinfo.json");
        strcpy(payloadfile, datapath);
        strcat(payloadfile, "encrypted-payload.json");
        mongocrypt_t *crypt;
        mongocrypt_ctx_t *ctx;

        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(cmdfile)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(collfile)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "ABCDEFAB123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789013-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(payloadfile), out);
            mongocrypt_binary_destroy(out);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_cleanup_nonlocal_kms(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    {
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789013-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "ABCDEFAB123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/cleanup/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_cleanup_missing_key_id(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/missing-key-id/collinfo.json")),
                     ctx,
                     "unable to find 'keyId' or 'keyAltName' in 'field' document");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_cleanup_key_not_provided(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "not all keys requested were satisfied");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_cleanup_need_kms_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}")), crypt);
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    {
        ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                       TEST_BSON("{'aws': {"
                                                                 "   'accessKeyId': 'example',"
                                                                 "   'secretAccessKey': 'example'}}")),
                  ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    {
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789013-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "ABCDEFAB123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/cleanup/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_cleanup_no_fields(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/cleanup/no-fields/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/cleanup/no-fields/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_cleanup_from_encrypted_field_config_map(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Initialize crypt with encrypted_field_config_map */
    {
        char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
        mongocrypt_binary_t *localkey;

        crypt = mongocrypt_new();
        mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
        localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
        mongocrypt_setopt_kms_provider_local(crypt, localkey);
        mongocrypt_binary_destroy(localkey);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/cleanup/success/encrypted-field-config-map.json")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    }
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/cleanup/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/cleanup/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_cleanup(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_cleanup_success);
    INSTALL_TEST(_test_cleanup_nonlocal_kms);
    INSTALL_TEST(_test_cleanup_missing_key_id);
    INSTALL_TEST(_test_cleanup_key_not_provided);
    INSTALL_TEST(_test_cleanup_need_kms_credentials);
    INSTALL_TEST(_test_cleanup_no_fields);
    INSTALL_TEST(_test_cleanup_from_encrypted_field_config_map);
}
libmongocrypt-1.19.0/test/test-mongocrypt-compact.c000066400000000000000000000455001521103432300224150ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt.h"

static void _test_compact_success(_mongocrypt_tester_t *tester) {
    const char basepath[] = "./test/data/compact/";
    char datapath[1000];
    char cmdfile[1000];
    char collfile[1000];
    char payloadfile[1000];
    strcpy(datapath, basepath);
    size_t nullb = strlen(basepath);
    const char *simple_success_tests[] = {
        "success/",
        "anchor-pad/",
        "text-search/",
    };
    for (size_t i = 0; i < sizeof(simple_success_tests) / sizeof(simple_success_tests[0]); i++) {
        datapath[nullb] = 0;
        strcat(datapath, simple_success_tests[i]);
        strcpy(cmdfile, datapath);
        strcat(cmdfile, "cmd.json");
        strcpy(collfile, datapath);
        strcat(collfile, "collinfo.json");
        strcpy(payloadfile, datapath);
        strcat(payloadfile, "encrypted-payload.json");

        mongocrypt_t *crypt;
        mongocrypt_ctx_t *ctx;

        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(cmdfile)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(collfile)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "ABCDEFAB123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789013-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(payloadfile), out);
            mongocrypt_binary_destroy(out);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test `compactStructuredEncryptionData` without range fields omits encryptionInformation.
    // This is a regression test for MONGOCRYPT-699.
    datapath[nullb] = 0;
    strcat(datapath, "no-range/");
    strcpy(cmdfile, datapath);
    strcat(cmdfile, "cmd.json");
    strcpy(collfile, datapath);
    strcat(collfile, "collinfo.json");
    strcpy(payloadfile, datapath);
    strcat(payloadfile, "encrypted-payload.json");

    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(cmdfile)), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(collfile)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(payloadfile), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_nonlocal_kms(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    {
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789013-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "ABCDEFAB123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/compact/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_missing_key_id(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/missing-key-id/collinfo.json")),
                     ctx,
                     "unable to find 'keyId' or 'keyAltName' in 'field' document");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_key_not_provided(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "not all keys requested were satisfied");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_need_kms_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}")), crypt);
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/success/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    {
        ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                       TEST_BSON("{'aws': {"
                                                                 "   'accessKeyId': 'example',"
                                                                 "   'secretAccessKey': 'example'}}")),
                  ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-aws-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    {
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789013-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "ABCDEFAB123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kms_ctx);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                          TEST_FILE("./test/data/keys/"
                                                    "12345678123498761234123456789012-"
                                                    "aws-decrypt-reply.txt")),
                  kms_ctx);
        ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/compact/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_no_fields(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/compact/no-fields/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/compact/no-fields/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_compact_from_encrypted_field_config_map(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Initialize crypt with encrypted_field_config_map */
    {
        char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
        mongocrypt_binary_t *localkey;

        crypt = mongocrypt_new();
        mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
        localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
        mongocrypt_setopt_kms_provider_local(crypt, localkey);
        mongocrypt_binary_destroy(localkey);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/compact/success/encrypted-field-config-map.json")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    }
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/compact/success/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789013-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/compact/success/encrypted-payload.json"), out);
        mongocrypt_binary_destroy(out);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_compact(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_compact_success);
    INSTALL_TEST(_test_compact_nonlocal_kms);
    INSTALL_TEST(_test_compact_missing_key_id);
    INSTALL_TEST(_test_compact_key_not_provided);
    INSTALL_TEST(_test_compact_need_kms_credentials);
    INSTALL_TEST(_test_compact_no_fields);
    INSTALL_TEST(_test_compact_from_encrypted_field_config_map);
}
libmongocrypt-1.19.0/test/test-mongocrypt-crypto-hooks.c000066400000000000000000001165511521103432300234350ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-config.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-private.h"
#include "test-mongocrypt-crypto-std-hooks.h"

#include "test-mongocrypt.h"

#define IV_HEX "1F572A1B84EC8F99B7915AA2A2AEA2F4"
#define HMAC_HEX                                                                                                       \
    "60676DE9FD305FD2C0815763C422687270DA2416D94A917B276E9DCBB13F412F"                                                 \
    "92FA403AA8AE172BD2E4729ED352793795EE588A2977C9C1F218D2AAD779C997"
/* only the first 32 bytes are appended. */
#define HMAC_HEX_TAG "60676DE9FD305FD2C0815763C422687270DA2416D94A917B276E9DCBB13F412F"

#define HMAC_KEY_HEX "CCD3836C8F24AC5FAAFAAA630C5C6C5D210FD03934EA1440CD67E0DCDE3F8EA6"
#define ENCRYPTION_KEY_HEX "E1D1727BAF970E01181C0868CB9D3E574B47AC09771FF30FE2D093B0950C7DAF"
#define IV_KEY_HEX "0A9328FCB6405ABDF5B4BFEC243FE9CF503CD4F24360872B75F08A2A3961802B"
/* full 96 byte key consists of three "sub" keys */
#define KEY_HEX HMAC_KEY_HEX ENCRYPTION_KEY_HEX IV_KEY_HEX
#define HASH_HEX "489EC3238378DC624C74B8CC4598ACED2B7EA5DE5C5F7602D8761BAE92FD8ABE"
#define RANDOM_HEX                                                                                                     \
    "670ACBB44D4E04A279CC0B95D217493205A038C50F537F452C59EFF6541D0026670ACBB44"                                        \
    "D4E04A279CC0B95D217493205A038C50F537F452C59EFF6541D0026670ACBB44D4E04A279"                                        \
    "CC0B95D217493205A038C50F537F452C59EFF6541D0026"

/* a document containing the history of calls */
static char *call_history;

// APPEND_CALLHISTORY appends a formatted string to `call_history`.
#define APPEND_CALLHISTORY(...)                                                                                        \
    if (1) {                                                                                                           \
        char *previous = call_history;                                                                                 \
        char *addition = bson_strdup_printf(__VA_ARGS__);                                                              \
        call_history = bson_strdup_printf("%s%s", previous ? previous : "", addition);                                 \
        bson_free(addition);                                                                                           \
        bson_free(previous);                                                                                           \
    } else                                                                                                             \
        (void)0

static char *to_hex(_mongocrypt_buffer_t *buf) {
    BSON_ASSERT_PARAM(buf);

    BSON_ASSERT(buf->len < (UINT32_MAX - 1) / 2);

    char *hex = bson_malloc0(buf->len * 2 + 1);
    BSON_ASSERT(hex);

    char *out = hex;

    for (uint32_t i = 0; i < buf->len; i++, out += 2) {
        BSON_ASSERT(2 == bson_snprintf(out, 3, "%02X", buf->data[i]));
    }
    return hex;
}

static void _append_bin(const char *name, mongocrypt_binary_t *bin) {
    _mongocrypt_buffer_t tmp;
    char *hex;

    _mongocrypt_buffer_from_binary(&tmp, bin);
    hex = to_hex(&tmp);
    APPEND_CALLHISTORY("%s:%s\n", name, hex);
    bson_free(hex);
    _mongocrypt_buffer_cleanup(&tmp);
}

static bool _mock_aes_256_xxx_encrypt(void *ctx,
                                      mongocrypt_binary_t *key,
                                      mongocrypt_binary_t *iv,
                                      mongocrypt_binary_t *in,
                                      mongocrypt_binary_t *out,
                                      uint32_t *bytes_written,
                                      mongocrypt_status_t *status) {
    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("key", key);
    if (NULL != iv) {
        _append_bin("iv", iv);
    }
    _append_bin("in", in);
    /* append it directly, don't encrypt. */
    uint8_t *out_u8 = out->data;
    memcpy(out_u8 + *bytes_written, in->data, in->len);
    *bytes_written += in->len;
    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);
    if (0 == strcmp((char *)ctx, "error_on:aes_256_cbc_encrypt")
        || 0 == strcmp((char *)ctx, "error_on:aes_256_ctr_encrypt")
        || 0 == strcmp((char *)ctx, "error_on:aes_256_ecb_encrypt")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _mock_aes_256_xxx_decrypt(void *ctx,
                                      mongocrypt_binary_t *key,
                                      mongocrypt_binary_t *iv,
                                      mongocrypt_binary_t *in,
                                      mongocrypt_binary_t *out,
                                      uint32_t *bytes_written,
                                      mongocrypt_status_t *status) {
    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("key", key);
    _append_bin("iv", iv);
    _append_bin("in", in);
    /* append it directly, don't decrypt. */
    uint8_t *out_u8 = out->data;
    memcpy(out_u8 + *bytes_written, in->data, in->len);
    *bytes_written += in->len;
    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);
    if (0 == strcmp((char *)ctx, "error_on:aes_256_cbc_decrypt")
        || 0 == strcmp((char *)ctx, "error_on:aes_256_ctr_decrypt")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _hmac_sha_512(void *ctx,
                          mongocrypt_binary_t *key,
                          mongocrypt_binary_t *in,
                          mongocrypt_binary_t *out,
                          mongocrypt_status_t *status) {
    _mongocrypt_buffer_t tmp;

    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("key", key);
    _append_bin("in", in);

    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);

    _mongocrypt_buffer_copy_from_hex(&tmp, HMAC_HEX);
    memcpy(out->data, tmp.data, tmp.len);
    _mongocrypt_buffer_cleanup(&tmp);
    if (0 == strcmp((char *)ctx, "error_on:hmac_sha512")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _hmac_sha_256(void *ctx,
                          mongocrypt_binary_t *key,
                          mongocrypt_binary_t *in,
                          mongocrypt_binary_t *out,
                          mongocrypt_status_t *status) {
    _mongocrypt_buffer_t tmp;

    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("key", key);
    _append_bin("in", in);

    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);

    _mongocrypt_buffer_copy_from_hex(&tmp, HASH_HEX);
    memcpy(out->data, tmp.data, tmp.len);
    _mongocrypt_buffer_cleanup(&tmp);
    if (0 == strcmp((char *)ctx, "error_on:hmac_sha256")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _sha_256(void *ctx, mongocrypt_binary_t *in, mongocrypt_binary_t *out, mongocrypt_status_t *status) {
    _mongocrypt_buffer_t tmp;

    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("in", in);

    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);

    _mongocrypt_buffer_copy_from_hex(&tmp, HASH_HEX);
    memcpy(out->data, tmp.data, tmp.len);
    _mongocrypt_buffer_cleanup(&tmp);
    if (0 == strcmp((char *)ctx, "error_on:sha256")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _random(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status) {
    /* only have 32 bytes of random test data. */
    BSON_ASSERT(count <= 96);

    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    APPEND_CALLHISTORY("count:%d\n", (int)count);
    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);

    _mongocrypt_buffer_t tmp;
    _mongocrypt_buffer_copy_from_hex(&tmp, RANDOM_HEX);
    memcpy(out->data, tmp.data, count);
    _mongocrypt_buffer_cleanup(&tmp);
    if (0 == strcmp((char *)ctx, "error_on:random")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static bool _sign_rsaes_pkcs1_v1_5(void *ctx,
                                   mongocrypt_binary_t *key,
                                   mongocrypt_binary_t *in,
                                   mongocrypt_binary_t *out,
                                   mongocrypt_status_t *status) {
    _mongocrypt_buffer_t tmp;

    BSON_ASSERT(0 == strncmp("error_on:", (char *)ctx, strlen("error_on:")));
    APPEND_CALLHISTORY("call:%s\n", BSON_FUNC);
    _append_bin("key", key);
    _append_bin("in", in);

    APPEND_CALLHISTORY("ret:%s\n", BSON_FUNC);
    memset(out->data, 0, out->len);

    _mongocrypt_buffer_copy_from_hex(&tmp, HASH_HEX);
    memcpy(out->data, tmp.data, tmp.len);
    _mongocrypt_buffer_cleanup(&tmp);
    if (0 == strcmp((char *)ctx, "error_on:sign_rsaes_pkcs1_v1_5")) {
        mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, 1, (char *)ctx, -1);
        return false;
    }
    return true;
}

static mongocrypt_t *
_create_mongocrypt_and_hooks(_mongocrypt_tester_t *tester, const char *error_on, bool ctr_hook, bool ecb_hook) {
    bool ret;

    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'gcp': { 'email': 'test', 'privateKey': 'AAAA'}}")),
              crypt);
    ret = mongocrypt_setopt_crypto_hooks(crypt,
                                         _mock_aes_256_xxx_encrypt,
                                         _mock_aes_256_xxx_decrypt,
                                         _random,
                                         _hmac_sha_512,
                                         _hmac_sha_256,
                                         _sha_256,
                                         (void *)error_on);
    ASSERT_OK(ret, crypt);
    ret = mongocrypt_setopt_crypto_hook_sign_rsaes_pkcs1_v1_5(crypt, _sign_rsaes_pkcs1_v1_5, (void *)error_on);
    ASSERT_OK(ret, crypt);
    if (ctr_hook) {
        ret = mongocrypt_setopt_aes_256_ctr(crypt,
                                            _mock_aes_256_xxx_encrypt,
                                            _mock_aes_256_xxx_decrypt,
                                            (void *)error_on);
        ASSERT_OK(ret, crypt);
    }
    if (ecb_hook) {
        ret = mongocrypt_setopt_aes_256_ecb(crypt, _mock_aes_256_xxx_encrypt, (void *)error_on);
        ASSERT_OK(ret, crypt);
    }
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    return crypt;
}

static mongocrypt_t *_create_mongocrypt(_mongocrypt_tester_t *tester, const char *error_on) {
    return _create_mongocrypt_and_hooks(tester, error_on, false, false);
}

static void
_test_crypto_hooks_encryption_helper(_mongocrypt_tester_t *tester, const char *error_on, bool ctr_hook, bool ecb_hook) {
    mongocrypt_t *crypt;
    bool ret;
    uint32_t bytes_written;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t iv, associated_data, key, plaintext, ciphertext;
    const char *expected_call_history = "call:_mock_aes_256_xxx_encrypt\n"
                                        "key:" ENCRYPTION_KEY_HEX "\n"
                                        "iv:" IV_HEX "\n"
                                        "in:BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E\n"
                                        "ret:_mock_aes_256_xxx_encrypt\n"
                                        "call:_hmac_sha_512\n"
                                        "key:CCD3836C8F24AC5FAAFAAA630C5C6C5D210FD03934EA1440CD67E0DCDE3F8EA6\n"
                                        "in:AAAA" IV_HEX "BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E0000000000000010\n"
                                        "ret:_hmac_sha_512\n";

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt_and_hooks(tester, error_on, ctr_hook, ecb_hook);

    _mongocrypt_buffer_copy_from_hex(&iv, IV_HEX);
    _mongocrypt_buffer_copy_from_hex(&associated_data, "AAAA");
    _mongocrypt_buffer_copy_from_hex(&plaintext, "BBBB");

    call_history = NULL;

    if (ctr_hook || ecb_hook) {
        const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
        _mongocrypt_buffer_copy_from_hex(&key, ENCRYPTION_KEY_HEX);
        _mongocrypt_buffer_init(&ciphertext);
        _mongocrypt_buffer_resize(&ciphertext, fle2alg->get_ciphertext_len(plaintext.len, status));
        ret =
            fle2alg
                ->do_encrypt(crypt->crypto, &iv, NULL /* aad */, &key, &plaintext, &ciphertext, &bytes_written, status);
    } else {
        const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
        _mongocrypt_buffer_copy_from_hex(&key, KEY_HEX);
        _mongocrypt_buffer_init(&ciphertext);
        _mongocrypt_buffer_resize(&ciphertext, fle1alg->get_ciphertext_len(plaintext.len, status));
        ret = fle1alg->do_encrypt(crypt->crypto,
                                  &iv,
                                  &associated_data,
                                  &key,
                                  &plaintext,
                                  &ciphertext,
                                  &bytes_written,
                                  status);
    }

    if (0 == strcmp(error_on, "error_on:none")) {
        ASSERT_OK_STATUS(ret, status);
        ciphertext.len = bytes_written;

        /* Check the full trace. */
        ASSERT_STREQUAL(call_history, expected_call_history);

        /* Check the structure of the ciphertext */
        BSON_ASSERT(0
                    == _mongocrypt_buffer_cmp_hex(&ciphertext,
                                                  IV_HEX "BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E" /* the "encrypted"
                                                                                             block which is
                                                                                             really plaintext.
                                                                                             BBBB + padding. */
                                                  HMAC_HEX_TAG));
    } else {
        ASSERT_FAILS_STATUS(ret, status, error_on);
    }

    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&iv);
    _mongocrypt_buffer_cleanup(&associated_data);
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&ciphertext);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_crypto_hooks_encryption(_mongocrypt_tester_t *tester) {
    _test_crypto_hooks_encryption_helper(tester, "error_on:none", false, false);
    _test_crypto_hooks_encryption_helper(tester, "error_on:aes_256_cbc_encrypt", false, false);
    _test_crypto_hooks_encryption_helper(tester, "error_on:aes_256_ctr_encrypt", true, false);
    _test_crypto_hooks_encryption_helper(tester, "error_on:aes_256_ecb_encrypt", false, true);
    _test_crypto_hooks_encryption_helper(tester, "error_on:hmac_sha512", false, false);
}

static void
_test_crypto_hooks_decryption_helper(_mongocrypt_tester_t *tester, const char *error_on, bool ctr_hook, bool ecb_hook) {
    mongocrypt_t *crypt;
    bool ret;
    uint32_t bytes_written;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t associated_data, key, plaintext, ciphertext;
    const char *expected_call_history = "call:_hmac_sha_512\n"
                                        "key:" HMAC_KEY_HEX "\n"
                                        "in:AAAA" IV_HEX "BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E0000000000000010\n"
                                        "ret:_hmac_sha_512\n"
                                        "call:_mock_aes_256_xxx_decrypt\n"
                                        "key:" ENCRYPTION_KEY_HEX "\n"
                                        "iv:" IV_HEX "\n"
                                        "in:BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E\n"
                                        "ret:_mock_aes_256_xxx_decrypt\n";

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt_and_hooks(tester, error_on, ctr_hook, ecb_hook);

    _mongocrypt_buffer_copy_from_hex(&associated_data, "AAAA");
    _mongocrypt_buffer_copy_from_hex(&ciphertext, IV_HEX "BBBB0E0E0E0E0E0E0E0E0E0E0E0E0E0E" HMAC_HEX_TAG);

    call_history = NULL;

    if (ctr_hook || ecb_hook) {
        const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
        _mongocrypt_buffer_copy_from_hex(&key, ENCRYPTION_KEY_HEX);
        _mongocrypt_buffer_init(&plaintext);
        _mongocrypt_buffer_resize(&plaintext, fle2alg->get_plaintext_len(ciphertext.len, status));

        ret = fle2alg->do_decrypt(crypt->crypto, NULL /* aad */, &key, &ciphertext, &plaintext, &bytes_written, status);
    } else {
        const _mongocrypt_value_encryption_algorithm_t *fle1alg = _mcFLE1Algorithm();
        _mongocrypt_buffer_copy_from_hex(&key, KEY_HEX);
        _mongocrypt_buffer_init(&plaintext);
        _mongocrypt_buffer_resize(&plaintext, fle1alg->get_plaintext_len(ciphertext.len, status));

        ret =
            fle1alg->do_decrypt(crypt->crypto, &associated_data, &key, &ciphertext, &plaintext, &bytes_written, status);
    }

    if (0 == strcmp(error_on, "error_on:none")) {
        ASSERT_OK_STATUS(ret, status);
        plaintext.len = bytes_written;

        /* Check the full trace. */
        ASSERT_STREQUAL(call_history, expected_call_history);

        /* Check the resulting plaintext */
        BSON_ASSERT(0 == _mongocrypt_buffer_cmp_hex(&plaintext, "BBBB"));
    } else {
        ASSERT_FAILS_STATUS(ret, status, error_on);
    }

    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&associated_data);
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&ciphertext);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_crypto_hooks_decryption(_mongocrypt_tester_t *tester) {
    _test_crypto_hooks_decryption_helper(tester, "error_on:none", false, false);
    _test_crypto_hooks_decryption_helper(tester, "error_on:aes_256_cbc_decrypt", false, false);
    _test_crypto_hooks_decryption_helper(tester, "error_on:aes_256_ctr_decrypt", true, false);
    _test_crypto_hooks_decryption_helper(tester, "error_on:aes_256_ecb_encrypt", false, true);
    _test_crypto_hooks_decryption_helper(tester, "error_on:hmac_sha512", false, false);
}

static void _test_crypto_hooks_iv_gen_helper(_mongocrypt_tester_t *tester, char *error_on) {
    mongocrypt_t *crypt;
    bool ret;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t associated_data, key, plaintext, iv;
    char *expected_iv = bson_strndup(HMAC_HEX_TAG, 16 * 2); /* only the first 16 bytes are used for IV. */
    const char *expected_call_history = "call:_hmac_sha_512\n"
                                        "key:" IV_KEY_HEX "\n"
                                        "in:AAAA0000000000000010BBBB\n"
                                        "ret:_hmac_sha_512\n";

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt(tester, error_on);

    _mongocrypt_buffer_copy_from_hex(&associated_data, "AAAA");
    _mongocrypt_buffer_copy_from_hex(&key, KEY_HEX);
    _mongocrypt_buffer_copy_from_hex(&plaintext, "BBBB");

    _mongocrypt_buffer_init(&iv);
    _mongocrypt_buffer_resize(&iv, MONGOCRYPT_IV_LEN);

    call_history = NULL;

    ret = _mongocrypt_calculate_deterministic_iv(crypt->crypto, &key, &plaintext, &associated_data, &iv, status);

    if (0 == strcmp(error_on, "error_on:none")) {
        ASSERT_OK_STATUS(ret, status);

        /* Check the full trace. */
        ASSERT_STREQUAL(call_history, expected_call_history);

        /* Check the resulting iv */
        BSON_ASSERT(0 == _mongocrypt_buffer_cmp_hex(&iv, expected_iv));
    } else {
        ASSERT_FAILS_STATUS(ret, status, error_on);
    }

    bson_free(expected_iv);
    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&associated_data);
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&iv);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_crypto_hooks_iv_gen(_mongocrypt_tester_t *tester) {
    _test_crypto_hooks_iv_gen_helper(tester, "error_on:none");
    _test_crypto_hooks_iv_gen_helper(tester, "error_on:hmac_sha512");
}

static void _test_crypto_hooks_random_helper(_mongocrypt_tester_t *tester, const char *error_on) {
    mongocrypt_t *crypt;
    bool ret;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t random;
    const char *expected_call_history = "call:_random\n"
                                        "count:96\n"
                                        "ret:_random\n";

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt(tester, error_on);

    _mongocrypt_buffer_init(&random);
    _mongocrypt_buffer_resize(&random, 96);

    call_history = NULL;

    ret = _mongocrypt_random(crypt->crypto, &random, random.len, status);

    if (0 == strcmp(error_on, "error_on:none")) {
        ASSERT_OK_STATUS(ret, status);

        /* Check the full trace. */
        ASSERT_STREQUAL(call_history, expected_call_history);

        /* Check the resulting iv */
        BSON_ASSERT(0 == _mongocrypt_buffer_cmp_hex(&random, RANDOM_HEX));
    } else {
        ASSERT_FAILS_STATUS(ret, status, error_on);
    }

    _mongocrypt_buffer_cleanup(&random);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_crypto_hooks_random(_mongocrypt_tester_t *tester) {
    _test_crypto_hooks_random_helper(tester, "error_on:none");
    _test_crypto_hooks_random_helper(tester, "error_on:random");
}

static void _test_kms_request_helper(_mongocrypt_tester_t *tester, const char *error_on) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    mongocrypt_ctx_t *ctx;
    bool ret;

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt(tester, error_on);
    ctx = mongocrypt_ctx_new(crypt);

    call_history = NULL;

    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "us-east-1", -1, "cmk", -1), ctx);

    mongocrypt_ctx_datakey_init(ctx);
    ret = mongocrypt_ctx_status(ctx, status);

    if (0 == strcmp(error_on, "error_on:none")) {
        ASSERT_OK_STATUS(ret, status);

        /* The call history includes some random data, just assert we've called
         * our hooks. */
        BSON_ASSERT(strstr(call_history, "call:_hmac_sha_256"));
        BSON_ASSERT(strstr(call_history, "call:_sha_256"));
    } else {
        ASSERT_FAILS_STATUS(ret, status, error_on);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_kms_request(_mongocrypt_tester_t *tester) {
    _test_kms_request_helper(tester, "error_on:none");
    _test_kms_request_helper(tester, "error_on:hmac_sha256");
    _test_kms_request_helper(tester, "error_on:sha256");
}

static void _test_crypto_hooks_unset(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;

    crypt = mongocrypt_new();
    mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    mongocrypt_destroy(crypt);
}

/* test a bug fix, that an error on explicit encryption in the crypto hooks sets
 * the context state */
static void _test_crypto_hooks_explicit_err(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin, *key_id;
    const char *deterministic = MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR;

    call_history = NULL;

    /* error on something during encryption. */
    crypt = _create_mongocrypt(tester, "error_on:hmac_sha512");

    ctx = mongocrypt_ctx_new(crypt);
    key_id = mongocrypt_binary_new_from_data(MONGOCRYPT_DATA_AND_LEN("aaaaaaaaaaaaaaaa"));

    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 123}")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    ASSERT_FAILS(mongocrypt_ctx_finalize(ctx, bin), ctx, "error_on:hmac_sha512");
    BSON_ASSERT(MONGOCRYPT_CTX_ERROR == mongocrypt_ctx_state(ctx));
    mongocrypt_binary_destroy(bin);
    mongocrypt_binary_destroy(key_id);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

/* validate that sha256 errors are handled correctly */
static void _test_crypto_hooks_explicit_sha256_err(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    mongocrypt_ctx_t *ctx;

    status = mongocrypt_status_new();
    crypt = _create_mongocrypt(tester, "error_on:sha256");
    ctx = mongocrypt_ctx_new(crypt);

    call_history = NULL;

    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "us-east-1", -1, "cmk", -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx), ctx, "failed to create KMS message");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

static void _test_crypto_hook_sign_rsaes_pkcs1_v1_5(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _create_mongocrypt(tester, "error_on:none");
    call_history = NULL;

    ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                             TEST_BSON("{'provider': 'gcp', 'projectId': 'test', 'location': "
                                                       "'global', 'keyRing': 'ring', 'keyName': 'key'}"));
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

    BSON_ASSERT(strstr(call_history, "call:_sign_rsaes_pkcs1_v1_5"));
    BSON_ASSERT(strstr(call_history, "key:000000"));

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(call_history);

    /* Test error when creating a data key. */
    crypt = _create_mongocrypt(tester, "error_on:sign_rsaes_pkcs1_v1_5");
    ctx = mongocrypt_ctx_new(crypt);
    call_history = NULL;

    mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                             TEST_BSON("{'provider': 'gcp', 'projectId': 'test', 'location': "
                                                       "'global', 'keyRing': 'ring', 'keyName': 'key'}"));
    ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx), ctx, "error_on:sign_rsaes_pkcs1_v1_5");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(call_history);

    /* Test error when encrypting. */
    crypt = _create_mongocrypt(tester, "error_on:sign_rsaes_pkcs1_v1_5");
    ctx = mongocrypt_ctx_new(crypt);
    call_history = NULL;

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-gcp.json")),
                 ctx,
                 "error_on:sign_rsaes_pkcs1_v1_5");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(call_history);
}

#ifdef MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO
bool _native_crypto_aes_256_ecb_encrypt(aes_256_args_t args);

static bool _aes_256_ecb_encrypt(void *ctx,
                                 mongocrypt_binary_t *key,
                                 mongocrypt_binary_t *iv,
                                 mongocrypt_binary_t *in,
                                 mongocrypt_binary_t *out,
                                 uint32_t *bytes_written,
                                 mongocrypt_status_t *status) {
    _mongocrypt_buffer_t key_buf;
    _mongocrypt_buffer_from_binary(&key_buf, key);
    if (iv) {
        CLIENT_ERR("IV expected to be NULL in this mode");
        return false;
    }
    _mongocrypt_buffer_t in_buf;
    _mongocrypt_buffer_from_binary(&in_buf, in);
    _mongocrypt_buffer_t out_buf;
    _mongocrypt_buffer_from_binary(&out_buf, out);

    aes_256_args_t args = {&key_buf, NULL, &in_buf, &out_buf, bytes_written, status};

    return _native_crypto_aes_256_ecb_encrypt(args);
}

static void _test_fle2_crypto_via_ecb_hook(_mongocrypt_tester_t *tester) {
    const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();
    bool ret;
    _mongocrypt_buffer_t key;
    _mongocrypt_buffer_t iv;
    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t ciphertext_reg;
    _mongocrypt_buffer_t ciphertext_ecb;
    _mongocrypt_buffer_t plaintext_ecb;
    uint32_t bytes_written;
    mongocrypt_status_t *status = mongocrypt_status_new();

    _mongocrypt_buffer_copy_from_hex(&iv, IV_HEX);
    _mongocrypt_buffer_copy_from_hex(&plaintext, "4f6c64204d63446f6e616c64206861642061206661726d2e20456965696f0a");
    _mongocrypt_buffer_copy_from_hex(&key, ENCRYPTION_KEY_HEX);

    /* Encrypt data using native CTR and ECB-hook and compare */

    mongocrypt_t *crypt_reg = mongocrypt_new();
    _mongocrypt_buffer_init(&ciphertext_reg);
    _mongocrypt_buffer_resize(&ciphertext_reg, fle2alg->get_ciphertext_len(plaintext.len, status));
    ret = fle2alg->do_encrypt(crypt_reg->crypto,
                              &iv,
                              NULL /* aad */,
                              &key,
                              &plaintext,
                              &ciphertext_reg,
                              &bytes_written,
                              status);
    ASSERT_OK(ret, crypt_reg);

    mongocrypt_t *crypt_ecb = mongocrypt_new();
    ret = mongocrypt_setopt_aes_256_ecb(crypt_ecb, _aes_256_ecb_encrypt, NULL);
    ASSERT_OK(ret, crypt_ecb);
    _mongocrypt_buffer_init(&ciphertext_ecb);
    _mongocrypt_buffer_resize(&ciphertext_ecb, fle2alg->get_ciphertext_len(plaintext.len, status));
    ret = fle2alg->do_encrypt(crypt_ecb->crypto,
                              &iv,
                              NULL /* aad */,
                              &key,
                              &plaintext,
                              &ciphertext_ecb,
                              &bytes_written,
                              status);
    ASSERT_OK(ret, crypt_ecb);

    ASSERT(0 == _mongocrypt_buffer_cmp(&ciphertext_reg, &ciphertext_ecb));

    /* Decrypt data using ECB-hook and compare to original */

    _mongocrypt_buffer_init(&plaintext_ecb);
    _mongocrypt_buffer_resize(&plaintext_ecb, fle2alg->get_plaintext_len(ciphertext_ecb.len, status));
    ret = fle2alg->do_decrypt(crypt_ecb->crypto,
                              NULL /* aad */,
                              &key,
                              &ciphertext_ecb,
                              &plaintext_ecb,
                              &bytes_written,
                              status);
    ASSERT_OK(ret, crypt_ecb);

    ASSERT(0 == _mongocrypt_buffer_cmp(&plaintext, &plaintext_ecb));

    _mongocrypt_buffer_cleanup(&key);
    _mongocrypt_buffer_cleanup(&iv);
    _mongocrypt_buffer_cleanup(&plaintext);
    _mongocrypt_buffer_cleanup(&ciphertext_reg);
    _mongocrypt_buffer_cleanup(&ciphertext_ecb);
    _mongocrypt_buffer_cleanup(&plaintext_ecb);
    mongocrypt_destroy(crypt_reg);
    mongocrypt_destroy(crypt_ecb);
    mongocrypt_status_destroy(status);
}
#endif

static void test_is_crypto_available_with_crypto_required(_mongocrypt_tester_t *tester) {
    // libmongocrypt is built with native crypto.
    ASSERT(mongocrypt_is_crypto_available());
}

static void test_is_crypto_available_with_crypto_prohibited(_mongocrypt_tester_t *tester) {
    // libmongocrypt is not built with native crypto.
    ASSERT(!mongocrypt_is_crypto_available());
}

static int ctr_encrypt_count;

static bool _aes_256_ctr_encrypt_and_count(void *ctx,
                                           mongocrypt_binary_t *key,
                                           mongocrypt_binary_t *iv,
                                           mongocrypt_binary_t *in,
                                           mongocrypt_binary_t *out,
                                           uint32_t *bytes_written,
                                           mongocrypt_status_t *status) {
    ctr_encrypt_count++;
    return _std_hook_native_crypto_aes_256_ctr_encrypt(ctx, key, iv, in, out, bytes_written, status);
}

static int ctr_decrypt_count;

static bool _aes_256_ctr_decrypt_and_count(void *ctx,
                                           mongocrypt_binary_t *key,
                                           mongocrypt_binary_t *iv,
                                           mongocrypt_binary_t *in,
                                           mongocrypt_binary_t *out,
                                           uint32_t *bytes_written,
                                           mongocrypt_status_t *status) {
    ctr_decrypt_count++;
    return _std_hook_native_crypto_aes_256_ctr_decrypt(ctx, key, iv, in, out, bytes_written, status);
}

static void test_setting_only_ctr_hook(_mongocrypt_tester_t *tester) {
    // Test that the CTR hook can be set without setting other crypto hooks.
    // This enables supporting macOS <= 10.14 in bindings using libmongocrypt with native crypto.
    // macOS <= 10.14 does not support native CTR encryption.

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    // Configure KMS providers with local KEK used to encrypt key documents.
    mongocrypt_binary_t *kms_providers =
        TEST_BSON("{'local' : { 'key': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
                  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'}}");
    _mongocrypt_buffer_t key_id1, key_id2;
    _mongocrypt_buffer_copy_from_hex(&key_id1, "12345678123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&key_id2, "ABCDEFAB123498761234123456789012");

    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_aes_256_ctr(crypt, _aes_256_ctr_encrypt_and_count, _aes_256_ctr_decrypt_and_count, NULL),
        crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    // Encrypt with an algorithm using the CTR hook.
    ctr_encrypt_count = 0;
    {
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, _mongocrypt_buffer_as_binary(&key_id1)), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v' : 123}")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *ciphertext = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, ciphertext), ctx);
        mongocrypt_binary_destroy(ciphertext);
        mongocrypt_ctx_destroy(ctx);
    }
    // CTR encrypt hook is called.
    ASSERT_CMPINT(ctr_encrypt_count, ==, 1);

    // Decrypt with an algorithm using the CTR hook.
    ctr_decrypt_count = 0;
    {
        // `ieev_payload_base64` is an IEEV payload, which uses the CTR hook to decrypt.
        const char *ieev_payload_base64 = "BxI0VngSNJh2EjQSNFZ4kBICQ7uhTd9C2oI8M1afRon0ZaYG0s6oTmt0aBZ9kO4S4mm5vId01"
                                          "BsW7tBHytA8pDJ2IiWBCmah3OGH2M4ET7PSqekQD4gkUCo4JeEttx4yj05Ou4D6yZUmYfVKmE"
                                          "ljge16NCxKm7Ir9gvmQsp8x1wqGBzpndA6gkqFxsxfvQ/"
                                          "cIqOwMW9dGTTWsfKge+jYkCUIFMfms+XyC/8evQhjjA+qR6eEmV+N/"
                                          "kwpR7Q7TJe0lwU5kw2kSe3/KiPKRZZTbn8znadvycfJ0cCWGad9SQ==";

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(
                      ctx,
                      TEST_BSON("{'v':{'$binary':{'base64': '%s','subType':'6'}}}", ieev_payload_base64)),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *decrypted = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, decrypted), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(decrypted, TEST_BSON("{'v': 'value123'}"));
        mongocrypt_binary_destroy(decrypted);
        mongocrypt_ctx_destroy(ctx);
    }
    // CTR decrypt hook is called repeatedly.
    ASSERT_CMPINT(ctr_decrypt_count, ==, 4);

    _mongocrypt_buffer_cleanup(&key_id2);
    _mongocrypt_buffer_cleanup(&key_id1);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_crypto_hooks(_mongocrypt_tester_t *tester) {
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_encryption, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_decryption, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_iv_gen, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_random, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_kms_request, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_unset, CRYPTO_PROHIBITED);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_explicit_err, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hooks_explicit_sha256_err, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(_test_crypto_hook_sign_rsaes_pkcs1_v1_5, CRYPTO_OPTIONAL);
    INSTALL_TEST_CRYPTO(test_is_crypto_available_with_crypto_required, CRYPTO_REQUIRED);
    INSTALL_TEST_CRYPTO(test_is_crypto_available_with_crypto_prohibited, CRYPTO_PROHIBITED);
    INSTALL_TEST_CRYPTO(test_setting_only_ctr_hook, CRYPTO_REQUIRED);
#ifdef MONGOCRYPT_ENABLE_CRYPTO_LIBCRYPTO
    INSTALL_TEST(_test_fle2_crypto_via_ecb_hook);
#endif
}
libmongocrypt-1.19.0/test/test-mongocrypt-crypto-std-hooks.c000066400000000000000000000207211521103432300242160ustar00rootroot00000000000000/*
 * Copyright 2022-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 "test-mongocrypt-crypto-std-hooks.h"

//

#include 

#include "test-mongocrypt.h"

bool _std_hook_native_crypto_aes_256_cbc_encrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status) {
    _mongocrypt_buffer_t keybuf, ivbuf, inbuf, outbuf;
    _mongocrypt_buffer_from_binary(&keybuf, key);
    _mongocrypt_buffer_from_binary(&ivbuf, iv);
    _mongocrypt_buffer_from_binary(&inbuf, in);
    _mongocrypt_buffer_from_binary(&outbuf, out);

    aes_256_args_t args =
        {.key = &keybuf, .iv = &ivbuf, .in = &inbuf, .out = &outbuf, .bytes_written = bytes_written, .status = status};
    return _native_crypto_aes_256_cbc_encrypt(args);
}

bool _std_hook_native_crypto_aes_256_cbc_decrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status) {
    _mongocrypt_buffer_t keybuf, ivbuf, inbuf, outbuf;
    _mongocrypt_buffer_from_binary(&keybuf, key);
    _mongocrypt_buffer_from_binary(&ivbuf, iv);
    _mongocrypt_buffer_from_binary(&inbuf, in);
    _mongocrypt_buffer_from_binary(&outbuf, out);

    aes_256_args_t args =
        {.key = &keybuf, .iv = &ivbuf, .in = &inbuf, .out = &outbuf, .bytes_written = bytes_written, .status = status};
    return _native_crypto_aes_256_cbc_decrypt(args);
}

bool _std_hook_native_crypto_aes_256_ctr_encrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status) {
    _mongocrypt_buffer_t key_buf;
    _mongocrypt_buffer_from_binary(&key_buf, key);
    _mongocrypt_buffer_t iv_buf;
    _mongocrypt_buffer_from_binary(&iv_buf, iv);
    _mongocrypt_buffer_t in_buf;
    _mongocrypt_buffer_from_binary(&in_buf, in);
    _mongocrypt_buffer_t out_buf;
    _mongocrypt_buffer_from_binary(&out_buf, out);

    aes_256_args_t args = {&key_buf, &iv_buf, &in_buf, &out_buf, bytes_written, status};
    return _native_crypto_aes_256_ctr_encrypt(args);
}

bool _std_hook_native_crypto_aes_256_ctr_decrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status) {
    _mongocrypt_buffer_t key_buf;
    _mongocrypt_buffer_from_binary(&key_buf, key);
    _mongocrypt_buffer_t iv_buf;
    _mongocrypt_buffer_from_binary(&iv_buf, iv);
    _mongocrypt_buffer_t in_buf;
    _mongocrypt_buffer_from_binary(&in_buf, in);
    _mongocrypt_buffer_t out_buf;
    _mongocrypt_buffer_from_binary(&out_buf, out);

    aes_256_args_t args = {&key_buf, &iv_buf, &in_buf, &out_buf, bytes_written, status};
    return _native_crypto_aes_256_ctr_decrypt(args);
}

bool _std_hook_native_crypto_random(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status) {
    _mongocrypt_buffer_t outbuf;
    _mongocrypt_buffer_from_binary(&outbuf, out);

    return _native_crypto_random(&outbuf, count, status);
}

bool _std_hook_native_hmac_sha512(void *ctx,
                                  mongocrypt_binary_t *key,
                                  mongocrypt_binary_t *in,
                                  mongocrypt_binary_t *out,
                                  mongocrypt_status_t *status) {
    _mongocrypt_buffer_t keybuf, inbuf, outbuf;
    _mongocrypt_buffer_from_binary(&keybuf, key);
    _mongocrypt_buffer_from_binary(&inbuf, in);
    _mongocrypt_buffer_from_binary(&outbuf, out);

    return _native_crypto_hmac_sha_512(&keybuf, &inbuf, &outbuf, status);
}

bool _std_hook_native_hmac_sha256(void *ctx,
                                  mongocrypt_binary_t *key,
                                  mongocrypt_binary_t *in,
                                  mongocrypt_binary_t *out,
                                  mongocrypt_status_t *status) {
    _mongocrypt_buffer_t keybuf, inbuf, outbuf;
    _mongocrypt_buffer_from_binary(&keybuf, key);
    _mongocrypt_buffer_from_binary(&inbuf, in);
    _mongocrypt_buffer_from_binary(&outbuf, out);

    return _native_crypto_hmac_sha_256(&keybuf, &inbuf, &outbuf, status);
}

bool _error_hook_native_crypto_aes_256_cbc_encrypt(void *ctx,
                                                   mongocrypt_binary_t *key,
                                                   mongocrypt_binary_t *iv,
                                                   mongocrypt_binary_t *in,
                                                   mongocrypt_binary_t *out,
                                                   uint32_t *bytes_written,
                                                   mongocrypt_status_t *status) {
    CLIENT_ERR("aes_256_cbc_encrypt not expected to have been called");
    return false;
}

bool _error_hook_native_crypto_aes_256_cbc_decrypt(void *ctx,
                                                   mongocrypt_binary_t *key,
                                                   mongocrypt_binary_t *iv,
                                                   mongocrypt_binary_t *in,
                                                   mongocrypt_binary_t *out,
                                                   uint32_t *bytes_written,
                                                   mongocrypt_status_t *status) {
    CLIENT_ERR("aes_256_cbc_decrypt not expected to have been called");
    return false;
}

bool _error_hook_native_crypto_random(void *ctx,
                                      mongocrypt_binary_t *out,
                                      uint32_t count,
                                      mongocrypt_status_t *status) {
    CLIENT_ERR("crypto_random not expected to have been called");
    return false;
}

bool _error_hook_native_hmac_sha512(void *ctx,
                                    mongocrypt_binary_t *key,
                                    mongocrypt_binary_t *in,
                                    mongocrypt_binary_t *out,
                                    mongocrypt_status_t *status) {
    CLIENT_ERR("hmac_sha512 not expected to have been called");
    return false;
}

bool _error_hook_native_hmac_sha256(void *ctx,
                                    mongocrypt_binary_t *key,
                                    mongocrypt_binary_t *in,
                                    mongocrypt_binary_t *out,
                                    mongocrypt_status_t *status) {
    CLIENT_ERR("hmac_sha256 not expected to have been called");
    return false;
}

bool _error_hook_native_sha256(void *ctx,
                               mongocrypt_binary_t *in,
                               mongocrypt_binary_t *out,
                               mongocrypt_status_t *status) {
    CLIENT_ERR("sha256 not expected to have been called");
    return false;
}
libmongocrypt-1.19.0/test/test-mongocrypt-crypto-std-hooks.h000066400000000000000000000131041521103432300242200ustar00rootroot00000000000000/*
 * Copyright 2022-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 

#include "test-mongocrypt.h"

/* Forwarding proxies for base crypto functions.
 * Each std hook wraps binary_t inputs into buffer_t
 * and calls the native crypto implementation.
 *
 * ctx is ignored.
 */
bool _std_hook_native_crypto_aes_256_cbc_encrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status);

bool _std_hook_native_crypto_aes_256_cbc_decrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status);

bool _std_hook_native_crypto_aes_256_ctr_encrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status);

bool _std_hook_native_crypto_aes_256_ctr_decrypt(void *ctx,
                                                 mongocrypt_binary_t *key,
                                                 mongocrypt_binary_t *iv,
                                                 mongocrypt_binary_t *in,
                                                 mongocrypt_binary_t *out,
                                                 uint32_t *bytes_written,
                                                 mongocrypt_status_t *status);

bool _std_hook_native_crypto_random(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status);

bool _std_hook_native_hmac_sha512(void *ctx,
                                  mongocrypt_binary_t *key,
                                  mongocrypt_binary_t *in,
                                  mongocrypt_binary_t *out,
                                  mongocrypt_status_t *status);

bool _std_hook_native_hmac_sha256(void *ctx,
                                  mongocrypt_binary_t *key,
                                  mongocrypt_binary_t *in,
                                  mongocrypt_binary_t *out,
                                  mongocrypt_status_t *status);

/* Hooks which fail and set an error indicating that the named
 * function was not expected to have been called.
 */
bool _error_hook_native_crypto_aes_256_cbc_encrypt(void *ctx,
                                                   mongocrypt_binary_t *key,
                                                   mongocrypt_binary_t *iv,
                                                   mongocrypt_binary_t *in,
                                                   mongocrypt_binary_t *out,
                                                   uint32_t *bytes_written,
                                                   mongocrypt_status_t *status);

bool _error_hook_native_crypto_aes_256_cbc_decrypt(void *ctx,
                                                   mongocrypt_binary_t *key,
                                                   mongocrypt_binary_t *iv,
                                                   mongocrypt_binary_t *in,
                                                   mongocrypt_binary_t *out,
                                                   uint32_t *bytes_written,
                                                   mongocrypt_status_t *status);

bool _error_hook_native_crypto_random(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status);

bool _error_hook_native_hmac_sha512(void *ctx,
                                    mongocrypt_binary_t *key,
                                    mongocrypt_binary_t *in,
                                    mongocrypt_binary_t *out,
                                    mongocrypt_status_t *status);

bool _error_hook_native_hmac_sha256(void *ctx,
                                    mongocrypt_binary_t *key,
                                    mongocrypt_binary_t *in,
                                    mongocrypt_binary_t *out,
                                    mongocrypt_status_t *status);

bool _error_hook_native_sha256(void *ctx,
                               mongocrypt_binary_t *in,
                               mongocrypt_binary_t *out,
                               mongocrypt_status_t *status);
libmongocrypt-1.19.0/test/test-mongocrypt-crypto.c000066400000000000000000000506471521103432300223170ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 
#include 

#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

typedef struct {
    const char *name;
    const _mongocrypt_value_encryption_algorithm_t *algo;
    _mongocrypt_buffer_t key;
    _mongocrypt_buffer_t aad;
    _mongocrypt_buffer_t iv;
    _mongocrypt_buffer_t plaintext;
    _mongocrypt_buffer_t ciphertext;
    const char *encrypt_error;
    const char *decrypt_error;
    // Special case test for payload which decrypts to empty string.
    bool ignore_ciphertext_mismatch_on_encrypt;
} _test_mc_crypto_roundtrip_t;

static bool _test_uses_ctr(const _test_mc_crypto_roundtrip_t *test) {
    return (test->algo == _mcFLE2AEADAlgorithm()) || (test->algo == _mcFLE2Algorithm());
}

#define ASSERT_BAD_DECRYPT(ret, out, test, status)                                                                     \
    if (test->algo == _mcFLE2Algorithm()) {                                                                            \
        /* A bad decrypt with CTR and no MAC isn't directly visible, */                                                \
        /* we just get garbage data. */                                                                                \
        ASSERT(out.len == test->plaintext.len);                                                                        \
        ASSERT(memcmp(out.data, test->plaintext.data, out.len) != 0);                                                  \
    } else if (1) {                                                                                                    \
        ASSERT_FAILS_STATUS(ret, status, "HMAC validation failure");                                                   \
    } else                                                                                                             \
        ((void)0)

static void _test_roundtrip_single(const _test_mc_crypto_roundtrip_t *test) {
    if (!_aes_ctr_is_supported_by_os && _test_uses_ctr(test)) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping %s", test->name);
        return;
    }

    TEST_PRINTF("Begin %s...\n", test->name);

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_status_t *const status = mongocrypt_status_new();
    _mongocrypt_buffer_t out;

    // Test encrypt
    _mongocrypt_buffer_init_size(&out, test->algo->get_ciphertext_len(test->plaintext.len, status));
    ASSERT_OK_STATUS(true, status);
    uint32_t outlen;
    bool ret =
        test->algo
            ->do_encrypt(crypt->crypto, &test->iv, &test->aad, &test->key, &test->plaintext, &out, &outlen, status);
    if (test->encrypt_error) {
        ASSERT_FAILS_STATUS(ret, status, test->encrypt_error);
        goto done;
    } else if (test->ignore_ciphertext_mismatch_on_encrypt) {
        _mongocrypt_status_reset(status);
    } else {
        ASSERT_OK_STATUS(ret, status);
        out.len = outlen;
        ASSERT_CMPBUF(out, test->ciphertext);
    }

    // Test decrypt
    const uint32_t plaintext_len = test->algo->get_plaintext_len(test->ciphertext.len, status);
    if (test->decrypt_error && !mongocrypt_status_ok(status)) {
        ASSERT_FAILS_STATUS(false, status, test->decrypt_error);
        goto done;
    }
    ASSERT_OK_STATUS(true, status);
    _mongocrypt_buffer_resize(&out, plaintext_len);
    ret = test->algo->do_decrypt(crypt->crypto, &test->aad, &test->key, &test->ciphertext, &out, &outlen, status);
    if (test->decrypt_error) {
        ASSERT_FAILS_STATUS(ret, status, test->decrypt_error);
        goto done;
    }
    ASSERT_OK_STATUS(ret, status);
    out.len = outlen;
    ASSERT_CMPBUF(out, test->plaintext);

    // Negative: Mutated IV
    _mongocrypt_buffer_t modified_ciphertext = {0};
    _mongocrypt_buffer_copy_to(&test->ciphertext, &modified_ciphertext);
    _mongocrypt_buffer_resize(&out, plaintext_len);
    modified_ciphertext.data[0] ^= 1;
    ret = test->algo->do_decrypt(crypt->crypto, &test->aad, &test->key, &modified_ciphertext, &out, &outlen, status);
    out.len = outlen;
    ASSERT_BAD_DECRYPT(ret, out, test, status);

    // Negative: Mutated ciphertext
    _mongocrypt_buffer_copy_to(&test->ciphertext, &modified_ciphertext);
    _mongocrypt_buffer_resize(&out, plaintext_len);
    modified_ciphertext.data[MONGOCRYPT_IV_LEN] ^= 1;
    ret = test->algo->do_decrypt(crypt->crypto, &test->aad, &test->key, &modified_ciphertext, &out, &outlen, status);
    ASSERT_BAD_DECRYPT(ret, out, test, status);

    // Negative: Mutated tag
    // Note: On algorithms without HMAC, this just repeats the mutated ciphertext
    // test in a different part of S.
    _mongocrypt_buffer_copy_to(&test->ciphertext, &modified_ciphertext);
    _mongocrypt_buffer_resize(&out, plaintext_len);
    modified_ciphertext.data[modified_ciphertext.len - 1] ^= 1;
    ret = test->algo->do_decrypt(crypt->crypto, &test->aad, &test->key, &modified_ciphertext, &out, &outlen, status);
    ASSERT_BAD_DECRYPT(ret, out, test, status);

    _mongocrypt_buffer_cleanup(&modified_ciphertext);
done:
    _mongocrypt_buffer_cleanup(&out);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);

    TEST_PRINTF("End %s...\n", test->name);
}

static const _mongocrypt_value_encryption_algorithm_t *get_algo_by_name(const char *name) {
    if (!strcmp(name, "AES-256-CBC/SHA-512-256") || !strcmp(name, "FLE1")) {
        return _mcFLE1Algorithm();
    }
    if (!strcmp(name, "AES-256-CTR/SHA-256") || !strcmp(name, "FLE2AEAD")) {
        return _mcFLE2AEADAlgorithm();
    }
    if (!strcmp(name, "AES-256-CTR/NONE") || !strcmp(name, "FLE2")) {
        return _mcFLE2Algorithm();
    }
    if (!strcmp(name, "AES-256-CBC/SHA-256") || !strcmp(name, "FLE2v2AEAD")) {
        return _mcFLE2v2AEADAlgorithm();
    }
    TEST_ERROR("Unknown algorithm: %s", name);
}

static void _parse_roundtrip_test(bson_iter_t *iter, _test_mc_crypto_roundtrip_t *test) {
    while (bson_iter_next(iter)) {
        const char *field = bson_iter_key(iter);
        ASSERT(field);

        if (!strcmp(field, "algo")) {
            ASSERT_OR_PRINT_MSG(!test->algo, "Duplicate field 'algo' in test");
            ASSERT(BSON_ITER_HOLDS_UTF8(iter));
            test->algo = get_algo_by_name(bson_iter_utf8(iter, NULL));
        } else if (!strcmp(field, "ignore_ciphertext_mismatch_on_encrypt")) {
            ASSERT_OR_PRINT_MSG(!test->ignore_ciphertext_mismatch_on_encrypt,
                                "Duplicate field 'ignore_ciphertext_mismatch_on_encrypt' in test");
            ASSERT(BSON_ITER_HOLDS_BOOL(iter));
            ASSERT_OR_PRINT_MSG(bson_iter_bool(iter), "value of 'ignore_ciphertext_mismatch_on_encrypt' must be true");
            test->ignore_ciphertext_mismatch_on_encrypt = true;
        }

#define STR_FIELD(Name)                                                                                                \
    else if (!strcmp(field, #Name)) {                                                                                  \
        ASSERT_OR_PRINT_MSG(!test->Name, "Duplicate field '" #Name "' in test");                                       \
        ASSERT(BSON_ITER_HOLDS_UTF8(iter));                                                                            \
        test->Name = bson_strdup(bson_iter_utf8(iter, NULL));                                                          \
    }

        STR_FIELD(name)
        STR_FIELD(encrypt_error)
        STR_FIELD(decrypt_error)

#undef STR_FIELD

// If we encounter a zero-length hexit string,
// then mcb_copy_from_hex will leave the buffer unallocated.
// This complicates field detection when we want an empty plaintext.
// Similarly, mcb_init_size will not allocate a lenght of zero.
// Simplify the flow elsewhere by allocating 1 byte, then truncating.
#define HEXBUF_FIELD(Name)                                                                                             \
    else if (!strcmp(field, #Name)) {                                                                                  \
        ASSERT_OR_PRINT_MSG(!test->Name.data, "Duplicate field '" #Name "' in test");                                  \
        ASSERT(BSON_ITER_HOLDS_UTF8(iter));                                                                            \
        const char *value = bson_iter_utf8(iter, NULL);                                                                \
        const size_t value_len = strlen(value);                                                                        \
        if (value_len > 0) {                                                                                           \
            _mongocrypt_buffer_copy_from_hex(&test->Name, value);                                                      \
            ASSERT(strlen(value) == (test->Name.len * 2));                                                             \
        } else {                                                                                                       \
            _mongocrypt_buffer_init_size(&test->Name, 1);                                                              \
            test->Name.len = 0;                                                                                        \
        }                                                                                                              \
    }

        HEXBUF_FIELD(key)
        HEXBUF_FIELD(aad)
        HEXBUF_FIELD(iv)
        HEXBUF_FIELD(plaintext)
        HEXBUF_FIELD(ciphertext)
#undef HEXBUF_FIELD
    }

    ASSERT_OR_PRINT_MSG(test->name, "Missing field 'name'");
    ASSERT_OR_PRINT_MSG(test->algo, "Missing field 'algo'");
    ASSERT_OR_PRINT_MSG(test->key.data, "Missing field 'key'");
    if (test->algo == _mcFLE2Algorithm()) {
        ASSERT_OR_PRINT_MSG(test->aad.len == 0, "Unexpected value in field 'aad' for cipher without MAC");
    } else {
        ASSERT_OR_PRINT_MSG(test->aad.data, "Missing field 'aad'");
    }
    ASSERT_OR_PRINT_MSG(test->iv.data, "Missing field 'iv'");
    ASSERT_OR_PRINT_MSG(test->plaintext.data, "Missing field 'plaintext'");
    ASSERT_OR_PRINT_MSG(test->ciphertext.data || test->encrypt_error, "Missing field 'ciphertext'");
}

static void _test_mc_crypto_roundtrip_destroy(_test_mc_crypto_roundtrip_t *test) {
    if (test->name) {
        bson_free((void *)test->name);
    }
    _mongocrypt_buffer_cleanup(&test->key);
    _mongocrypt_buffer_cleanup(&test->aad);
    _mongocrypt_buffer_cleanup(&test->iv);
    _mongocrypt_buffer_cleanup(&test->plaintext);
    _mongocrypt_buffer_cleanup(&test->ciphertext);
    if (test->encrypt_error) {
        bson_free((void *)test->encrypt_error);
    }
    if (test->decrypt_error) {
        bson_free((void *)test->decrypt_error);
    }
}

static void _test_roundtrip_set(_mongocrypt_tester_t *tester, const char *path) {
    TEST_PRINTF("Loading tests from %s...\n", path);

    mongocrypt_binary_t *test_bin = TEST_FILE(path);
    if (!test_bin) {
        TEST_ERROR("Failed loading test data file '%s'\n", path);
    }
    if (test_bin->len == 5) {
        TEST_ERROR("Invalid JSON in file '%s'\n", path);
    }

    bson_t test_bson;
    ASSERT(bson_init_static(&test_bson, test_bin->data, test_bin->len));
    ASSERT(bson_validate(&test_bson, BSON_VALIDATE_NONE, NULL));

    bson_iter_t it;
    ASSERT(bson_iter_init(&it, &test_bson));
    while (bson_iter_next(&it)) {
        bson_iter_t docit;
        ASSERT(BSON_ITER_HOLDS_DOCUMENT(&it));
        ASSERT(bson_iter_recurse(&it, &docit));
        _test_mc_crypto_roundtrip_t test = {0};
        _parse_roundtrip_test(&docit, &test);
        _test_roundtrip_single(&test);
        _test_mc_crypto_roundtrip_destroy(&test);
    }

    TEST_PRINTF("Finished tests in %s\n", path);
}

static void _test_roundtrip(_mongocrypt_tester_t *tester) {
    _test_roundtrip_set(tester, "./test/data/roundtrip/mcgrew.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/nist.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/aes-ctr.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2v2-aead-fixed.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2v2-aead-generated.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2aead-fixed.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2aead-generated.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2aead-decrypt.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2-fixed.json");
    _test_roundtrip_set(tester, "./test/data/roundtrip/fle2-generated.json");
}

typedef struct {
    const char *testname;
    const char *key;
    const char *input;
    const char *expect;
} hmac_sha_256_test_t;

static void _test_native_crypto_hmac_sha_256(_mongocrypt_tester_t *tester) {
    /* Test data generated with OpenSSL CLI:
    $ echo -n "test" | openssl dgst -mac hmac -macopt \
    hexkey:6bb2664e8d444377d3cd9566c005593b7ed8a35ab8eac9eb5ffa6e426854e5cc \
    -sha256
      d80a4d2271fdaa45ad4a1bf85d606fe465cb40176d1d83e69628a154c2c528ff

    Hex representation of "test" is: 74657374
    */
    hmac_sha_256_test_t tests[] = {{.testname = "String 'test'",
                                    .key = "6bb2664e8d444377d3cd9566c005593b"
                                           "7ed8a35ab8eac9eb5ffa6e426854e5cc",
                                    .input = "74657374",
                                    .expect = "d80a4d2271fdaa45ad4a1bf85d606fe4"
                                              "65cb40176d1d83e69628a154c2c528ff"},
                                   {.testname = "Data larger than one block",
                                    .key = "6bb2664e8d444377d3cd9566c005593b"
                                           "7ed8a35ab8eac9eb5ffa6e426854e5cc",
                                    .input = "fd2368de92202a33fcaf48f9b5807fc8"
                                             "6b9837aa376beb6044d6db6b07347f7e"
                                             "2af3eedfc968218f76b588fff9ae1c91"
                                             "74cca2368389bf211270f0449771c260"
                                             "689bb59a32f0c5ae40372ecb371ec2a7"
                                             "2179bbe8d46260eef7d0e7c1ae679b71",
                                    .expect = "1985743613238e3c8c05a0274be76fa6"
                                              "7821228f7b880e72dbd0f314fb63e63f"},
#include "./data/NIST-CAVP.cstructs"
                                   {0}};
    hmac_sha_256_test_t *test;
    mongocrypt_t *crypt;

    /* Create a mongocrypt_t to call _native_crypto_init(). */
    crypt = mongocrypt_new();

    for (test = tests; test->testname != NULL; test++) {
        bool ret;
        _mongocrypt_buffer_t key;
        _mongocrypt_buffer_t input;
        _mongocrypt_buffer_t expect;
        _mongocrypt_buffer_t got;
        mongocrypt_status_t *status;

        TEST_PRINTF("Begin test '%s'.\n", test->testname);

        _mongocrypt_buffer_copy_from_hex(&key, test->key);
        _mongocrypt_buffer_copy_from_hex(&input, test->input);
        _mongocrypt_buffer_copy_from_hex(&expect, test->expect);
        _mongocrypt_buffer_init(&got);
        _mongocrypt_buffer_resize(&got, MONGOCRYPT_HMAC_SHA256_LEN);
        status = mongocrypt_status_new();

        ret = _native_crypto_hmac_sha_256(&key, &input, &got, status);
        ASSERT_OR_PRINT(ret, status);
        if (expect.len < got.len) {
            /* Some NIST CAVP tests expect the output tag to be truncated. */
            got.len = expect.len;
        }
        ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);

        mongocrypt_status_destroy(status);
        _mongocrypt_buffer_cleanup(&got);
        _mongocrypt_buffer_cleanup(&expect);
        _mongocrypt_buffer_cleanup(&input);
        _mongocrypt_buffer_cleanup(&key);

        TEST_PRINTF("End test '%s'.\n", test->testname);
    }

    mongocrypt_destroy(crypt);
}

static bool _hook_hmac_sha_256(void *ctx,
                               mongocrypt_binary_t *key,
                               mongocrypt_binary_t *in,
                               mongocrypt_binary_t *out,
                               mongocrypt_status_t *status) {
    const uint8_t *data_to_copy = (const uint8_t *)ctx;
    uint8_t *outdata = mongocrypt_binary_data(out);
    uint32_t outlen = mongocrypt_binary_len(out);

    ASSERT_CMPINT((int)outlen, ==, 32);
    memcpy(outdata, data_to_copy, outlen);
    return true;
}

static void _test_mongocrypt_hmac_sha_256_hook(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    _mongocrypt_crypto_t crypto = {0};
    _mongocrypt_buffer_t key = {0};
    _mongocrypt_buffer_t in = {0};
    _mongocrypt_buffer_t expect;
    _mongocrypt_buffer_t got;
    mongocrypt_status_t *status;

    /* Create a mongocrypt_t to call _native_crypto_init(). */
    crypt = mongocrypt_new();

    status = mongocrypt_status_new();
    _mongocrypt_buffer_resize(&key, MONGOCRYPT_MAC_KEY_LEN);
    _mongocrypt_buffer_copy_from_hex(&expect,
                                     "000102030405060708090A0B0C0D0E0F"
                                     "101112131415161718191A1B1C1D1E1F");
    _mongocrypt_buffer_init(&got);
    _mongocrypt_buffer_resize(&got, MONGOCRYPT_HMAC_SHA256_LEN);

    crypto.hooks_enabled = true;
    crypto.hmac_sha_256 = _hook_hmac_sha_256;
    crypto.ctx = expect.data;

    ASSERT_OR_PRINT(_mongocrypt_hmac_sha_256(&crypto, &key, &in, &got, status), status);

    ASSERT_CMPBYTES(expect.data, expect.len, got.data, got.len);

    _mongocrypt_buffer_cleanup(&got);
    _mongocrypt_buffer_cleanup(&expect);
    _mongocrypt_buffer_cleanup(&key);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_random_int64(_mongocrypt_tester_t *tester) {
    bool got0 = false, got1 = false, got2 = false;
    int trial;
    const int max_trials = 1000;
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;

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

    for (trial = 0; trial < max_trials; trial++) {
        int64_t got;

        ASSERT_OR_PRINT(_mongocrypt_random_int64(crypt->crypto, 3, &got, status), status);
        switch (got) {
        case 0: got0 = true; break;
        case 1: got1 = true; break;
        case 2: got2 = true; break;
        default: TEST_ERROR("Expected random number to be in range [0,3), got: %" PRId64, got);
        }
    }

    ASSERT(got0);
    ASSERT(got1);
    ASSERT(got2);

    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_aes_256_aead_steps_consistent(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    // Tests a key assumption we make that if 16k <= a <= b <= 16k + 15 (a, b, k integers), a plaintext of length a and
    // a plaintext of length b produce a ciphertext of the same length, and a plaintext of length 16k produces a
    // ciphertext 16 bytes longer than one of length 16(k-1). This is very important for the leakage profile of QE text
    // search.
    const _mongocrypt_value_encryption_algorithm_t *alg = _mcFLE2v2AEADAlgorithm();
    size_t ciphertext_len = 0;
    for (int i = 0; i <= 16; i++) {
        size_t new_ct_len = alg->get_ciphertext_len(i * 16, status);
        if (new_ct_len == 0) {
            TEST_ERROR("get_ciphertext_len failed");
        }
        if (i != 0) {
            ASSERT_CMPSIZE_T(new_ct_len, ==, ciphertext_len + 16);
        }
        ciphertext_len = new_ct_len;
        for (int j = 1; j < 16; j++) {
            size_t ct_len = alg->get_ciphertext_len(i * 16 + j, status);
            if (ct_len == 0) {
                TEST_ERROR("get_ciphertext_len failed");
            }
            ASSERT_CMPSIZE_T(ct_len, ==, ciphertext_len);
        }
    }
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_crypto(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_roundtrip);
    INSTALL_TEST(_test_native_crypto_hmac_sha_256);
    INSTALL_TEST_CRYPTO(_test_mongocrypt_hmac_sha_256_hook, CRYPTO_OPTIONAL);
    INSTALL_TEST(_test_random_int64);
    INSTALL_TEST(_test_aes_256_aead_steps_consistent);
}
libmongocrypt-1.19.0/test/test-mongocrypt-csfle-lib.c000066400000000000000000000270211521103432300226250ustar00rootroot00000000000000#include "mongocrypt.h"

#include "mongocrypt-util-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"

static mongocrypt_t *get_test_mongocrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();
    mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
    mongocrypt_binary_t *schema_map = TEST_FILE("./test/data/schema-map.json");
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, schema_map), crypt);
    return crypt;
}

static void _test_csfle_no_paths(_mongocrypt_tester_t *tester) {
    /// Test that mongocrypt_init succeeds if we have no search path
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    // No csfle was loaded:
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) == NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) == 0);
    mongocrypt_destroy(crypt);
}

static void _test_csfle_not_found(_mongocrypt_tester_t *tester) {
    /// Test that mongocrypt_init succeeds even if the csfle library was not
    /// found but a search path was specified
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "/no-such-directory");
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    // No csfle was loaded:
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) == NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) == 0);
    mongocrypt_destroy(crypt);
}

static void _test_csfle_load(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "no-such-directory");
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "$ORIGIN");
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "another-no-such-dir");
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    // csfle WAS loaded:
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) != NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) != 0);
    mstr_view version = mstrv_view_cstr(mongocrypt_crypt_shared_lib_version_string(crypt, NULL));
    if (TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        MSTR_ASSERT(true, version, starts_with, mstrv_lit("mongo_crypt_v1-"));
    } else {
        MSTR_ASSERT(true, version, eq, mstrv_lit("stubbed-crypt_shared"));
    }
    mongocrypt_destroy(crypt);
}

static void _test_csfle_load_twice(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt1 = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt1, "$ORIGIN");
    ASSERT_OK(_mongocrypt_init_for_test(crypt1), crypt1);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt1, NULL) != NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt1) != 0);

    // Make another one:
    mongocrypt_t *const crypt2 = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt2, "$ORIGIN");
    ASSERT_OK(_mongocrypt_init_for_test(crypt2), crypt2);
    // csfle was loaded again:
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt2, NULL) != NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt2) != 0);

    mstr_view version = mstrv_view_cstr(mongocrypt_crypt_shared_lib_version_string(crypt1, NULL));
    if (TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        MSTR_ASSERT(true, version, starts_with, mstrv_lit("mongo_crypt_v1-"));
    } else {
        MSTR_ASSERT(true, version, eq, mstrv_lit("stubbed-crypt_shared"));
    }

    version = mstrv_view_cstr(mongocrypt_crypt_shared_lib_version_string(crypt2, NULL));
    if (TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        MSTR_ASSERT(true, version, starts_with, mstrv_lit("mongo_crypt_v1-"));
    } else {
        MSTR_ASSERT(true, version, eq, mstrv_lit("stubbed-crypt_shared"));
    }

    mongocrypt_destroy(crypt1);
    mongocrypt_destroy(crypt2);
}

static void _test_csfle_load_twice_fail(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt1 = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt1, "$ORIGIN");
    ASSERT_OK(_mongocrypt_init_for_test(crypt1), crypt1);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt1, NULL) != NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt1) != 0);

    // Make another one, but finding a different dynamic library:
    mongocrypt_t *const crypt2 = get_test_mongocrypt(tester);
    mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt2, "$ORIGIN/stubbed-crypt_shared-2.dll");
    // Loading a second different library is an error:
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt2), crypt2, "attempted to load a second crypt_shared library");

    mstr_view version = mstrv_view_cstr(mongocrypt_crypt_shared_lib_version_string(crypt1, NULL));
    if (TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        MSTR_ASSERT(true, version, starts_with, mstrv_lit("mongo_crypt_v1-"));
    } else {
        MSTR_ASSERT(true, version, eq, mstrv_lit("stubbed-crypt_shared"));
    }

    mongocrypt_destroy(crypt1);
    mongocrypt_destroy(crypt2);
}

static void _test_csfle_path_override_okay(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    // Set to the absolute path to the DLL we use for testing:
    mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt, "$ORIGIN/mongo_crypt_v1" MCR_DLL_SUFFIX);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    // csfle WAS loaded:
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) != NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) != 0);
    mstr_view version = mstrv_view_cstr(mongocrypt_crypt_shared_lib_version_string(crypt, NULL));
    if (TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        MSTR_ASSERT(true, version, starts_with, mstrv_lit("mongo_crypt_v1-"));
    } else {
        MSTR_ASSERT(true, version, eq, mstrv_lit("stubbed-crypt_shared"));
    }
    mongocrypt_destroy(crypt);
}

static void _test_csfle_path_override_fail(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    // Set to the absolute path to a file that does not exist
    mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt,
                                                         "/no-such-file-or-directory/mongo_crypt_v1" MCR_DLL_SUFFIX);
    // This *would* succeed, but we don't use the search paths if an absolute
    // override was specified:
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "$ORIGIN");
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, "but we failed to open a dynamic library at that location");
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) == NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) == 0);
    mongocrypt_destroy(crypt);
}

static void _test_cur_exe_path(_mongocrypt_tester_t *tester) {
    current_module_result self = current_module_path();
    BSON_ASSERT(self.error == 0);
    BSON_ASSERT(self.path.raw.len != 0);
    mstr_free(self.path);
}

static void _test_csfle_not_loaded_with_bypassqueryanalysis(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "$ORIGIN");
    mongocrypt_setopt_bypass_query_analysis(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) == NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) == 0);

    mongocrypt_destroy(crypt);
}

// _test_override_error_includes_reason test changes of MONGOCRYPT-576: the error message from mcr_dll_open is
// propagated.
static void _test_override_error_includes_reason(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = get_test_mongocrypt(tester);
    // Set an incorrect override path.
    mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt, "invalid_path_to_crypt_shared.so");
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, "Error while opening candidate");
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version_string(crypt, NULL) == NULL);
    BSON_ASSERT(mongocrypt_crypt_shared_lib_version(crypt) == 0);
    mongocrypt_destroy(crypt);
}

static void _test_lookup_version_check(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

#define CRYPT_SHARED_8_1 0x0008000100000000ull
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
    uint64_t version = crypt->csfle.get_version();
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_binary_t *cmd = TEST_FILE("./test/data/lookup/csfle/cmd.json");
    if (version >= CRYPT_SHARED_8_1) {
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
    } else {
        ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx, "Upgrade crypt_shared");
    }
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_loading_libmongocrypt_fails(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = get_test_mongocrypt(tester);
    const char *path_to_libmongocrypt = TEST_MONGOCRYPT_MONGOCRYPT_SHARED_PATH;
    mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt, path_to_libmongocrypt);
    bool ok = mongocrypt_init(crypt);
    ASSERT_FAILS(ok, crypt, "detected libmongocrypt");
    mongocrypt_destroy(crypt);
}

static void _test_error_includes_version(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
    const char *version_str = crypt->csfle.get_version_str();
    // Strip leading mongo_crypt_v1-dev-
    if (strstr(version_str, "mongo_crypt_v1-dev-") == version_str) {
        version_str += strlen("mongo_crypt_v1-dev-");
    }

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"insert" : "test", "bad_field" : 1}));
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/collection-info.json")), ctx);
    char *pattern = bson_strdup_printf("[crypt_shared %s] \"analyze_query\" failed", version_str);
    ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, pattern);
    bson_free(pattern);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_csfle_lib(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_csfle_no_paths);
    INSTALL_TEST(_test_csfle_not_found);
    INSTALL_TEST(_test_csfle_load);
    INSTALL_TEST(_test_csfle_load_twice);
    INSTALL_TEST(_test_csfle_load_twice_fail);
    INSTALL_TEST(_test_csfle_path_override_okay);
    INSTALL_TEST(_test_csfle_path_override_fail);
    INSTALL_TEST(_test_cur_exe_path);
    INSTALL_TEST(_test_csfle_not_loaded_with_bypassqueryanalysis);
    INSTALL_TEST(_test_override_error_includes_reason);
    INSTALL_TEST(_test_lookup_version_check);
    INSTALL_TEST(_test_loading_libmongocrypt_fails);
    INSTALL_TEST(_test_error_includes_version);
}
libmongocrypt-1.19.0/test/test-mongocrypt-ctx-decrypt.c000066400000000000000000001511311521103432300232330ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "kms_message/kms_b64.h" // kms_message_raw_to_b64
#include "mongocrypt-ctx-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"

static void _test_explicit_decrypt_init(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *msg;
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    msg = TEST_BSON("{ 'v': { '$binary': { 'subType': '06', 'base64': "
                    "'AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs"
                    "9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/"
                    "iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=' } } }");

    /* NULL document. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_explicit_decrypt_init(ctx, NULL), ctx, "invalid msg");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* Success. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, msg), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

/* Test individual ctx states. */
static void _test_decrypt_init(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *encrypted;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    encrypted = _mongocrypt_tester_encrypted_doc(tester);

    /* Success. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, encrypted), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);

    /* NULL document. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_decrypt_init(ctx, NULL), ctx, "invalid doc");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_binary_destroy(encrypted);
    mongocrypt_destroy(crypt);
}

static void _test_decrypt_need_keys(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *encrypted;

    encrypted = _mongocrypt_tester_encrypted_doc(tester);

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, encrypted), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* TODO: CDRIVER-3044 test that decryption warns when keys are not
     * found/inactive. */

    mongocrypt_binary_destroy(encrypted);
}

static void _test_decrypt_ready(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *encrypted, *decrypted;
    bson_t as_bson;
    bson_iter_t iter;

    encrypted = _mongocrypt_tester_encrypted_doc(tester);
    decrypted = mongocrypt_binary_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Success. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, encrypted), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, decrypted), ctx);
    BSON_ASSERT(_mongocrypt_binary_to_bson(decrypted, &as_bson));
    bson_iter_init(&iter, &as_bson);
    bson_iter_find_descendant(&iter, "filter.ssn", &iter);
    BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
    BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), _mongocrypt_tester_plaintext(tester)));
    mongocrypt_binary_destroy(decrypted);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    mongocrypt_binary_destroy(encrypted);
}

/* Test with empty AWS credentials. */
static void _test_decrypt_empty_aws(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "", -1, "", -1), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, TEST_FILE("./test/data/encrypted-cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")),
                 ctx,
                 "failed to create KMS message");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_decrypt_empty_binary(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    _mongocrypt_buffer_t encrypted;

    bin = mongocrypt_binary_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    /* Encrypt an empty binary value. */
    mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'keyDocumentName'}"));
    mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1);
    mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': { '$binary': { 'base64': '', 'subType': '00' } } }"));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);
    /* Copy the encrypted ciphertext since it is tied to the lifetime of ctx. */
    _mongocrypt_buffer_copy_from_binary(&encrypted, bin);
    mongocrypt_ctx_destroy(ctx);

    /* Decrypt it back. */
    ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&encrypted));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    _mongocrypt_buffer_cleanup(&encrypted);
    mongocrypt_destroy(crypt);
}

static void _test_decrypt_per_ctx_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    _mongocrypt_buffer_t encrypted;

    bin = mongocrypt_binary_new();
    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}"));
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);

    /* Encrypt an empty binary value. */
    mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'keyDocumentName'}"));
    mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1);
    mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': { '$binary': { 'base64': '', 'subType': '00' } } }"));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'aws':{'accessKeyId': 'example',"
                                                             "'secretAccessKey': 'example'}}")),
              ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);
    /* Copy the encrypted ciphertext since it is tied to the lifetime of ctx. */
    _mongocrypt_buffer_copy_from_binary(&encrypted, bin);
    mongocrypt_ctx_destroy(ctx);

    /* Decrypt it back. */
    ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&encrypted));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    _mongocrypt_buffer_cleanup(&encrypted);
    mongocrypt_destroy(crypt);
}

static void _test_decrypt_per_ctx_credentials_local(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    _mongocrypt_buffer_t encrypted;
    /* local_kek is the KEK used to encrypt the keyMaterial in
     * ./test/data/key-document-local.json */
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));

    /* local_uuid is the hex of the UUID of the key in
     * ./test/data/key-document-local.json */
    const char *local_uuid = "61616161616161616161616161616161";
    _mongocrypt_buffer_t local_uuid_buf;

    bin = mongocrypt_binary_new();
    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'local': {}}"));
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);

    /* Encrypt an empty binary value. */
    _mongocrypt_buffer_copy_from_hex(&local_uuid_buf, local_uuid);
    mongocrypt_ctx_setopt_key_id(ctx, _mongocrypt_buffer_as_binary(&local_uuid_buf));
    mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1);
    mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': { '$binary': { 'base64': '', 'subType': '00' } } }"));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'local':{'key': { '$binary': {'base64': '%s', "
                                                             "'subType': '00'}}}}",
                                                             local_kek)),
              ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);
    /* Copy the encrypted ciphertext since it is tied to the lifetime of ctx. */
    _mongocrypt_buffer_copy_from_binary(&encrypted, bin);
    mongocrypt_ctx_destroy(ctx);

    /* Decrypt it back. */
    ctx = mongocrypt_ctx_new(crypt);
    mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&encrypted));
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);

    _mongocrypt_buffer_cleanup(&local_uuid_buf);
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    _mongocrypt_buffer_cleanup(&encrypted);
    mongocrypt_destroy(crypt);
    bson_free(local_kek);
}

static void _test_decrypt_fle2(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t S_KeyId;
    _mongocrypt_buffer_t K_KeyId;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

#define TEST_IEEV_BASE64                                                                                               \
    "BxI0VngSNJh2EjQSNFZ4kBICQ7uhTd9C2oI8M1afRon0ZaYG0s6oTmt0aBZ9kO4S4mm5vId01"                                        \
    "BsW7tBHytA8pDJ2IiWBCmah3OGH2M4ET7PSqekQD4gkUCo4JeEttx4yj05Ou4D6yZUmYfVKmE"                                        \
    "ljge16NCxKm7Ir9gvmQsp8x1wqGBzpndA6gkqFxsxfvQ/"                                                                    \
    "cIqOwMW9dGTTWsfKge+jYkCUIFMfms+XyC/8evQhjjA+qR6eEmV+N/"                                                           \
    "kwpR7Q7TJe0lwU5kw2kSe3/KiPKRZZTbn8znadvycfJ0cCWGad9SQ=="

    _mongocrypt_buffer_copy_from_hex(&S_KeyId, "12345678123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&K_KeyId, "ABCDEFAB123498761234123456789012");

    /* Test success with an FLE2IndexedEqualityEncryptedValue payload. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-decrypt-ieev/first-filter.json"), filter);
            mongocrypt_binary_destroy(filter);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-decrypt-ieev/second-filter.json"), filter);
            mongocrypt_binary_destroy(filter);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 'value123'}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test success with a non-local KMS provider. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-aws-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        {
            mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kms_ctx);
            ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                              TEST_FILE("./test/data/keys/"
                                                        "12345678123498761234123456789012-"
                                                        "aws-decrypt-reply.txt")),
                      kms_ctx);
            ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-aws-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        {
            mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kms_ctx);
            ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx,
                                              TEST_FILE("./test/data/keys/"
                                                        "ABCDEFAB123498761234123456789012-"
                                                        "aws-decrypt-reply.txt")),
                      kms_ctx);
            ASSERT(!mongocrypt_ctx_next_kms_ctx(ctx));
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 'value123'}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test success with two FLE2IndexedEqualityEncryptedValue payloads. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted1':{'$binary':{'base64'"
                                                        ":'" TEST_IEEV_BASE64 "','subType':'6'}}, "
                                                        "'encrypted2':{'$binary':{'base64':'" TEST_IEEV_BASE64
                                                        "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson,
                           TMP_BSON("{'plainText': 'sample', 'encrypted1': "
                                    "'value123', 'encrypted2': 'value123'}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test success when S_Key is cached, K_Key is not cached. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "' " TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        mongocrypt_ctx_destroy(ctx);

        /* Create a new context. S_Key is cached in crypt. */
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 'value123' }"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test success when S_Key is cached, K_Key is cached. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_ctx_destroy(ctx);

        /* Create a new ctx. S_Key and K_Key are cached in crypt. */
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        out = mongocrypt_binary_new();
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 'value123'}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test error when S_Key is not provided. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "not all keys requested were satisfied");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test error when K_Key is not provided. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IEEV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "not all keys requested were satisfied");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&K_KeyId);
    _mongocrypt_buffer_cleanup(&S_KeyId);

#undef TEST_IEEV_BASE64
}

#define TEST_IUP_BASE64                                                                                                \
    "BHEBAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/"                                                             \
    "5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+"                                                             \
    "G8PuaaO4EgVjACAAAAAAsZXWOWA+UiCBbrJNB6bHflB/"                                                                     \
    "cn7pWSvwWN2jw4FPeIUFcABQAAAAAMdD1nV2nqeI1eXEQNskDflCy8I7/"                                                        \
    "HvvqDKJ6XxjhrPQWdLqjz+8GosGUsB7A8ee/uG9/"                                                                         \
    "guENuL25XD+"                                                                                                      \
    "Fxxkv1LLXtavHOlLF7iW0u9yabqqBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2A"                                        \
    "E0AAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSFtVvkUcnHWj/"                                                    \
    "rfPW7iJ0G3UJ8zpuBmUM/VjOMJCY4+eDqdTiPIwX+/vNXegc8FZQAgAAAAAOuac/"                                                 \
    "eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsAA=="

static void _test_decrypt_fle2_iup(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    /* Test success with an FLE2InsertUpdatePayload. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IUP_BASE64 "','subType':'6'}}}")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 'value123'}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

#undef TEST_IUP_BASE64

/* Test decrypting a BSON binary non-subtype 6 is an error. */
static void _test_decrypt_wrong_binary_subtype(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    /* Use subtype 0. */
    ASSERT_FAILS(
        mongocrypt_ctx_explicit_decrypt_init(ctx,
                                             TEST_BSON("{'v': { '$binary': { 'base64': 'AAAA', 'subType': '00' }}}")),
        ctx,
        "decryption expected BSON binary subtype 6, got 0");
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

#define TEST_IREV_BASE64                                                                                               \
    "CRI0VngSNJh2EjQSNFZ4kBIQsPF0Ii0Hfv7ZMhnNt/yt+mviydF8EUw0YlO+amC3IF8dX2J/"                                         \
    "GmRZRnihW3VqJYoLMk0BIit0x9YQiQEEQPxPcTXnCx4t1fquOY7cRGqIAWTDHuQ9AdUw94EY1"                                        \
    "J55mq9UhwD7flh0ySR/SbkTwjIU32U1iM6Bv4AriE4smI87Yd0V7Z7kDoDx7afx9vM/"                                              \
    "+h9NWZvpfYcZ+P32sfILb3BdXT5zLrkyc5Xb3myDxE9abTrR8ePG0YuEmeqwGE4bZ6QHKzd/"                                         \
    "RLmHciWstKOtER5uRpo3p570wGO8QE9QtQoJp/7N7Su30dK/"                                                                 \
    "bk59hVvlNO6i3nUPwqMd13DePobNGn84q3Fag5O4Kw8P4EGfomFWzxydlVQ0SppGVfan9tIj1"                                        \
    "CFu5doYYT4adzX7L9HinKsTWE5ctD9Qxhhzb2cVs5JO96j4mpwOaloF/"                                                         \
    "4qJhyqlzTEpoCXGQ0X9aeEplibFxQ7FJkaFfYzIDIxA2d6lzwVel3j+VwQ7zOP/"                                                  \
    "bCnaFu6EP1OQw3ZarsaWGENf45DFuK5RsKX198vZTlqtH24YhAL1+noQTMtpTOp/"                                                 \
    "6vrczOXkr7dJGQ6RAfliq1maD18PN5yjdyNhr7BXsrK6f01DX2Xr4s51AARxQ/"                                                   \
    "0U5hmb4HjKg8Sbno4Th+Wza3I0RMgM0YzhRUJz+BXzx2l9NPdeyuECdQ1Q2wP1MNOCBy/"                                            \
    "QBJmc+"                                                                                                           \
    "RWYK57oWCuv5vmkpN8IlriyRv0PGRhYr4ZcLkDdzmuyfK6SGAvIPD4veDJRj3cXEazyMh6g+"                                         \
    "rvwt70laxo2IOhvUXDc89WvarthTOzlFt5FNrA8uXhUYyL1q1XSWYCiCu5vRv77BvRUJjf2B6"                                        \
    "5kaIKUAGDhYuhch2yU6O9VsHLik3xOGSwIUZJbdMyHY+eA8ZlWZeKJbpjz7a/"                                                    \
    "TyBQU6VNG4+Z5SXJjURogkODNkx21QS0Z1+b7ZnCSXf1OQceomkDrREB7vyD2HX5rN2/"                                             \
    "KIBMgH3J7DnG2VpNhYJ9Ve1hMDGcrQggjkCpdP7lloc6QiH837tD/81gYmr95IbuIHe/"                                             \
    "x20oHh9heGHUELnCQ6hXYWOBvSFlGcqZs/"                                                                               \
    "f0qxn5Fe2OfQPRzEstxoW99IdPvgotDnL2vaz2JNFvFqiofc2pIP7XvpFKIoQO7q8LX9z7ah1"                                        \
    "Yh8cbi6us8g5y9WzOfh882jU7vQT+31a1ZaeDMbFV1Cemc+/"                                                                 \
    "d0HNksi1qMJtcjrH2MQgXJ3BTyAuJH9OFK8iGqSzHhop9hp5z8mvx834PPjgBfZGt4w/"                                             \
    "7qeie+T4sooGVVqA6F3jl8YfFdIUAwkxe5GBQVVvaRLYm/"                                                                   \
    "4SLGBf54Dexi7e0+rL2sG5DeKygNdFzMc6lRO+"                                                                           \
    "gvmAMmDucRm4bxmu7ycNZCQUcuSKoMUWWu6A6eUiyBCQUxrrlX/"                                                              \
    "3CkRXkQQ6JCwZZMvTgBokYx3WQR6LpW70xWLXyQhav4ZnHKzgITSOe7mUkMJ35NDMD+"                                              \
    "qsxXY7sWbGz+b60DWF7yaMVzDPzIGjWLpckMRMxgN3bQ5SE/mFxdjoZD5yYb84q/"                                                 \
    "O7EjwGA9MSTp9MFEZt7VV3f5TDWiNUZKmjUgOdjBoTSAkVzAO2nqqQNg23x6Z6FQDBeefRkfc"                                        \
    "9FoUiBqHuN4fU/zc4Hkthp1McwIkYwRdlgPceD/"                                                                          \
    "BSNbkNRNAnzghBhCquIqpXd8AptBX2qO67rfT7COhpn/"                                                                     \
    "fzVo3ueCRTaM2DtjD3uuH4rMNb3LDjyJFX1DZ5eEWkGq9UE/"                                                                 \
    "AFivfeia4cA0a8Z1LzZcW7WvE5Y1WIZN4gy9SZcNgHEQq8Ad8Q4fAbxe8XJ6/"                                                    \
    "tNvG+AvAuLEvNJtbhC/4Ei/"                                                                                          \
    "JoXplvutDlW5d6g4KEWj4GICqggM5ZSv0TCkbfFkLdaJrOHrn+oI++"                                                           \
    "krv1U4yQk6P18Mg2bE18ibe+LdWNsqn01V7yDmS+"                                                                         \
    "VAvqQF8f2p4rOOyWsGc7CoyXSrq9LCuGq9eMPR6auo+"                                                                      \
    "tyS1Nek2t6SgpOpzBBDdQnC5sHC1OWTW3ui7w4H0NKCuZOiMncbSDOlegn8C0zZa6Z5iYAce8"                                        \
    "a8Ow3jryBEnKBaguhjjOMG8iX/eka8XP+UTxvso4fKVVOXQwobZMdYbf/"                                                        \
    "sXNJbMbWrFc1S9rdlXL/"                                                                                             \
    "nnYvYrRMnOBJ27Mz5vvtOpd4fyQ+wi1q+"                                                                                \
    "5VvuLDM8u51B4oaYqpGUZZ3qVS5BBYm9cDxgMtcdoXjOSopHasdAhron+"                                                        \
    "NdbGFBxyrUGKnnVXYocEuvsvwhBEA3HUUVV94m3C0agh2eVpmCIyWrs+"                                                         \
    "grkpAaNLZwXVuzegttJ0GoTxzQnDIWkvlvkS3ZGo25spfPp+/Nda4SZAYRNmtnGfB2TRl0Wx/"                                        \
    "o/"                                                                                                               \
    "V2vx+9qnGyDq52CSkMftpfnsMXAnAv6ps7U+"                                                                             \
    "mgbgNPUFjv1Y0xKaeJdshu1HyEmq5aYqHJSfF2EzvPfH4d0Ijz1lsxMxL4IsqB7kufcOR4FFn"                                        \
    "aYXKIXLjRwM5VZNAK/3dvCb3l9H7QMOiJPbdoxAd123aymjz9N/"                                                              \
    "2O33wMaG6OE8pXp0iYEaW7DOr0FfT913JeUnPNPcqqsA9YXod2UuNWZElTW/"                                                     \
    "saL32v9akNwA4Jd7Y5VgI4y+XyDH3kAU0Uc8g6YCx/hqcn4pd2+ryH+/"                                                         \
    "5nVQhnCE0KNOjjrFS92RNLD71GUhWR+VXMw2tKXBUSKnt9Ai4LLJrdvFbwrdqK+"                                                  \
    "AjBUVqI3MgylNxRw2395ppAbheE1pAcoqLoDOGyOs66Y8kJGpaqs0AmdmZHw2OA26btw+"                                            \
    "ceBN+UgScsB5P5wNIup1AvU5J7h1vlFBNygg3WO/MJGCz48xgJ/"                                                              \
    "klg9wCLQ+vXtrhYJz15RgguADFLBrTcV/Miel20KulnprI+/"                                                                 \
    "lXtRvEAoGJSc0UZ8J7UVTf8kvYzT3hF7XzZzlhKxPYebjdnp2la4o2PkyZXcc/"                                                   \
    "gFLa7ickR28ZPUigwpW0lK5sJIwWbnZmP5wbQNhiGO8QL9gVpFOnu0xHpu8MqBvfZGf2HiE+"                                         \
    "qBUSR89v88gz6u/TVP9zVH1dnk9PE54Uw3yPdxL/"                                                                         \
    "feukvF71sEI6WWd2fdupgRlDGzASrKSAsFbaZobwUViEIFbWo7zPYVZyMglCrD1Xoxdd6EBeU"                                        \
    "SJDkS1nhiHOR/7FpIhae8fggAD+StXR7725vzcwIOX21ozRcE2iWw6OP99vDoqLQ8VYzYS0/"                                         \
    "f3WMME6b5ndYz25uC0AiULXYI="

/* Test decrypting FLE2IndexedRangeEncryptedValue */
static void _test_decrypt_fle2_irev(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    /* Test success with an FLE2IndexedEqualityEncryptedValue payload. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;
        mongocrypt_binary_t *out;
        bson_t out_bson;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx,
                                              TEST_BSON("{'plainText':'sample','encrypted':{'$binary':{'base64':"
                                                        "'" TEST_IREV_BASE64 "','subType':'6'}}}")),
                  ctx);
        /* The first transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests S_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-decrypt-ieev/first-filter.json"), filter);
            mongocrypt_binary_destroy(filter);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        /* The second transition to MONGOCRYPT_CTX_NEED_MONGO_KEYS requests K_Key.
         */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-decrypt-ieev/second-filter.json"), filter);
            mongocrypt_binary_destroy(filter);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        _assert_match_bson(&out_bson, TMP_BSON("{'plainText': 'sample', 'encrypted': 123456}"));
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

#undef TEST_IREV_BASE64

typedef struct {
    const char *desc;
    mongocrypt_binary_t *msg;
    mongocrypt_binary_t *keys_to_feed[3]; // NULL terminated list.
    mongocrypt_binary_t *expect;
    bool expect_ignored;
} ed_testcase;

static void ed_testcase_run(ed_testcase *tc) {
    printf("  explicit decrypt test: %s ... begin\n", tc->desc);
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, tc->msg), ctx);

    if (tc->expect_ignored) {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            bool ret = mongocrypt_ctx_finalize(ctx, got);
            ASSERT_OK(ret, ctx);
            // Expect input is returned unchanged.
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(tc->msg, got);
            mongocrypt_binary_destroy(got);
        }
        goto cleanup;
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        for (size_t i = 0; i < sizeof(tc->keys_to_feed) / sizeof(tc->keys_to_feed[0]); i++) {
            mongocrypt_binary_t *key_to_feed = tc->keys_to_feed[i];
            if (!key_to_feed) {
                break;
            }
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key_to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *got = mongocrypt_binary_new();

        bool ret = mongocrypt_ctx_finalize(ctx, got);
        ASSERT_OK(ret, ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(tc->expect, got);
        mongocrypt_binary_destroy(got);
    }

cleanup:
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    printf("  explicit decrypt test: %s ... end\n", tc->desc);
}

static void _test_explicit_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *keyABC = TEST_FILE("./test/data/keys/"
                                            "ABCDEFAB123498761234123456789012-local-"
                                            "document.json");
    mongocrypt_binary_t *key123 = TEST_FILE("./test/data/keys/"
                                            "12345678123498761234123456789012-local-"
                                            "document.json");

    // FLE1DeterministicEncryptedValue can be decrypted.
    {
        ed_testcase tc = {
            .desc = "FLE1DeterministicEncryptedValue",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE1DeterministicEncryptedValue.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "foo"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE1EncryptionPlaceholder is ignored.
    // Payload is returned by query analysis and consumed by libmongocrypt. Payload is not expected to be given to user.
    {
        ed_testcase tc = {.desc = "FLE1EncryptionPlaceholder",
                          .msg = TEST_FILE("./test/data/explicit-decrypt/FLE1EncryptionPlaceholder.json"),
                          .expect_ignored = true};
        ed_testcase_run(&tc);
    }

    // FLE1RandomEncryptedValue can be decrypted.
    {
        ed_testcase tc = {
            .desc = "FLE1RandomEncryptedValue",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE1RandomEncryptedValue.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "foo"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2EncryptionPlaceholder is ignored.
    // Payload is returned by query analysis and consumed by libmongocrypt. Payload is not expected to be given to user.
    {
        ed_testcase tc = {.desc = "FLE2EncryptionPlaceholder",
                          .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2EncryptionPlaceholder.json"),
                          .expect_ignored = true};
        ed_testcase_run(&tc);
    }

    // FLE2InsertUpdatePayload can be decrypted.
    // Payload is only used in the QE-V1 protocol removed in MongoDB 7.0. Decrypting is still supported.
    // libmongocrypt no longer produces QE-V1 payloads. Payload is copied from libmongocrypt 1.8.0.
    {
        ed_testcase tc = {
            .desc = "FLE2InsertUpdatePayload",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2InsertUpdatePayload.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "value123"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2InsertUpdatePayload for RangeV1 can be decrypted. Range payloads include additional fields.
    // Payload is only used in the Range-V1 protocol removed in MongoDB 8.0. Decrypting is still supported.
    // libmongocrypt no longer produces Range-V1 payloads. Payload is copied from libmongocrypt 1.11.0.
    {
        ed_testcase tc = {
            .desc = "FLE2InsertUpdatePayload for RangeV1",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2InsertUpdatePayload-RangeV1.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : 123456})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2UnindexedEncryptedValue can be decrypted.
    // Payload is only used in the QE-V1 protocol removed in MongoDB 7.0. Decrypting is still supported.
    // libmongocrypt no longer produces QE-V1 payloads. Payload is copied from libmongocrypt 1.8.0.
    {
        ed_testcase tc = {
            .desc = "FLE2UnindexedEncryptedValue",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2UnindexedEncryptedValue.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "value123"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2IndexedEqualityEncryptedValue can be decrypted.
    // Payload is only used in the QE-V1 protocol removed in MongoDB 7.0. Decrypting is still supported.
    // libmongocrypt no longer produces QE-V1 payloads. Payload is copied from libmongocrypt 1.8.0.
    {
        ed_testcase tc = {
            .desc = "FLE2IndexedEqualityEncryptedValue",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2IndexedEqualityEncryptedValue.json"),
            .keys_to_feed = {key123, keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "value123"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2IndexedRangeEncryptedValue can be decrypted.
    // Payload is only used in the QE-V1 protocol removed in MongoDB 7.0. Decrypting is still supported.
    // libmongocrypt no longer produces QE-V1 payloads. Payload is copied from libmongocrypt 1.8.0.
    {
        ed_testcase tc = {
            .desc = "FLE2IndexedRangeEncryptedValue",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2IndexedRangeEncryptedValue.json"),
            .keys_to_feed = {key123, keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : 123456})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2FindEqualityPayload is ignored.
    // Payload does not contain encrypted ciphertext (only lookup tokens).
    {
        ed_testcase tc = {.desc = "FLE2FindEqualityPayload",
                          .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2FindEqualityPayload.json"),
                          .expect_ignored = true};
        ed_testcase_run(&tc);
    }

    // FLE2InsertUpdatePayloadV2 can be decrypted.
    {
        ed_testcase tc = {
            .desc = "FLE2InsertUpdatePayloadV2",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "value123"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2InsertUpdatePayloadV2 for RangeV1 can be decrypted. Range payloads include additional fields.
    // Payload is only used in the Range-V1 protocol removed in MongoDB 8.0. Decrypting is still supported.
    // libmongocrypt no longer produces Range-V1 payloads. Payload is copied from libmongocrypt 1.11.0.
    {
        ed_testcase tc = {
            .desc = "FLE2InsertUpdatePayloadV2 for RangeV1",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2-RangeV1.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : 123456})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2InsertUpdatePayloadV2 for RangeV2 can be decrypted. Range payloads include additional fields.
    {
        ed_testcase tc = {
            .desc = "FLE2InsertUpdatePayloadV2 for RangeV2",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2InsertUpdatePayloadV2-RangeV2.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : 123456})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2UnindexedEncryptedValueV2 can be decrypted.
    {
        ed_testcase tc = {
            .desc = "FLE2UnindexedEncryptedValueV2",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2UnindexedEncryptedValueV2.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "foo"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2IndexedEqualityEncryptedValueV2 can be decrypted.
    {
        ed_testcase tc = {
            .desc = "FLE2IndexedEqualityEncryptedValueV2",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2IndexedEqualityEncryptedValueV2.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : "foo"})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2IndexedRangeEncryptedValueV2 can be decrypted.
    // Payload did not change between RangeV1 and RangeV2.
    {
        ed_testcase tc = {
            .desc = "FLE2IndexedRangeEncryptedValueV2",
            .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2IndexedRangeEncryptedValueV2.json"),
            .keys_to_feed = {keyABC},
            .expect = TEST_BSON(BSON_STR({"v" : 123})),
        };
        ed_testcase_run(&tc);
    }

    // FLE2FindEqualityPayloadV2 is ignored.
    // Payload does not contain encrypted ciphertext (only lookup tokens).
    {
        ed_testcase tc = {.desc = "FLE2FindEqualityPayloadV2",
                          .msg = TEST_FILE("./test/data/explicit-decrypt/FLE2FindEqualityPayloadV2.json"),
                          .expect_ignored = true};
        ed_testcase_run(&tc);
    }
}

void _mongocrypt_tester_install_ctx_decrypt(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_explicit_decrypt_init);
    INSTALL_TEST(_test_decrypt_init);
    INSTALL_TEST(_test_decrypt_need_keys);
    INSTALL_TEST(_test_decrypt_ready);
    INSTALL_TEST(_test_decrypt_empty_aws);
    INSTALL_TEST(_test_decrypt_empty_binary);
    INSTALL_TEST(_test_decrypt_per_ctx_credentials);
    INSTALL_TEST(_test_decrypt_per_ctx_credentials_local);
    INSTALL_TEST(_test_decrypt_fle2);
    INSTALL_TEST(_test_decrypt_fle2_iup);
    INSTALL_TEST(_test_decrypt_wrong_binary_subtype);
    INSTALL_TEST(_test_decrypt_fle2_irev);
    INSTALL_TEST(_test_explicit_decrypt);
}
libmongocrypt-1.19.0/test/test-mongocrypt-ctx-encrypt.c000066400000000000000000011516241521103432300232550ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 

#include "kms_message/kms_b64.h"
#include "mc-fle2-insert-update-payload-private-v2.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt-crypto-private.h" // MONGOCRYPT_KEY_LEN
#include "mongocrypt.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt-crypto-std-hooks.h"
#include "test-mongocrypt.h"

static void _test_explicit_encrypt_init(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *string_msg;
    mongocrypt_binary_t *no_v_msg;
    mongocrypt_binary_t *bad_name;
    mongocrypt_binary_t *name;
    mongocrypt_binary_t *msg;
    mongocrypt_binary_t *key_id;
    mongocrypt_binary_t *tmp;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    bson_t *bson_msg;
    bson_t *bson_msg_no_v;
    bson_t *bson_name;
    bson_t *bson_bad_name;

    char *random = MONGOCRYPT_ALGORITHM_RANDOM_STR;
    char *deterministic = MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR;

    bson_msg = BCON_NEW("v", "hello");
    msg = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(bson_msg), bson_msg->len);

    bson_msg_no_v = BCON_NEW("a", "hello");
    no_v_msg = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(bson_msg_no_v), bson_msg_no_v->len);

    bson_name = BCON_NEW("keyAltName", "Rebekah");
    name = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(bson_name), bson_name->len);

    bson_bad_name = BCON_NEW("noAltName", "Barry");
    bad_name = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(bson_bad_name), bson_bad_name->len);

    string_msg = mongocrypt_binary_new_from_data(MONGOCRYPT_DATA_AND_LEN("hello"));
    key_id = mongocrypt_binary_new_from_data(MONGOCRYPT_DATA_AND_LEN("2395340598345034"));

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Initting with no options will fail (need key_id). */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, msg), ctx, "either key id or key alt name required");
    mongocrypt_ctx_destroy(ctx);

    /* Initting with only key_id will not succeed, we also
       need an algorithm. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, msg), ctx, "algorithm or index type required");
    mongocrypt_ctx_destroy(ctx);

    /* Test null msg input. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, NULL), ctx, "msg required for explicit encryption");
    mongocrypt_ctx_destroy(ctx);

    /* Test with string msg input (no bson) */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, string_msg), ctx, "msg must be bson");
    mongocrypt_ctx_destroy(ctx);

    /* Test with input bson that has no "v" field */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, no_v_msg), ctx, "invalid msg, must contain 'v'");
    mongocrypt_ctx_destroy(ctx);

    /* Initting with RANDOM passes */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, msg), ctx);
    BSON_ASSERT(ctx->opts.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM);
    mongocrypt_ctx_destroy(ctx);

    /* Test that bad algorithm input fails */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, "nonexistent algorithm", -1), ctx, "unsupported algorithm");
    mongocrypt_ctx_destroy(ctx);

    /* Test with badly formatted key alt name */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_setopt_key_alt_name(ctx, bad_name), ctx, "must have field");
    mongocrypt_ctx_destroy(ctx);

    /* Test with key alt name */
    ctx = mongocrypt_ctx_new(crypt);
    BSON_ASSERT(mongocrypt_ctx_setopt_key_alt_name(ctx, name));
    BSON_ASSERT(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1));
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, msg), ctx);

    /* After initing, we should be at NEED_KEYS */
    BSON_ASSERT(ctx->type == _MONGOCRYPT_TYPE_ENCRYPT);
    BSON_ASSERT(ctx->state == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);

    /* double succeeds for random. */
    tmp = TEST_BSON("{'v': { '$double': '1.23'} }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* double fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* decimal128 succeeds for random. */
    tmp = TEST_BSON("{'v': {'$numberDecimal': '1.23'} }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* decimal128 fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* document succeeds for random. */
    tmp = TEST_BSON("{'v': { 'x': 1 } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* document fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* array succeeds for random. */
    tmp = TEST_BSON("{'v': [1,2,3] }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* document fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* codewscope succeeds for random. */
    tmp = TEST_BSON("{'v': {'$code': 'var x = 1;', '$scope': {} } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* codewscope fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* bool succeeds for random. */
    tmp = TEST_BSON("{'v': true }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* bool fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for deterministic encryption");
    mongocrypt_ctx_destroy(ctx);

    /* null fails for deterministic. */
    tmp = TEST_BSON("{'v': null }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* null fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* minkey fails for deterministic. */
    tmp = TEST_BSON("{'v': { '$minKey': 1 } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* minkey fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* maxkey fails for deterministic. */
    tmp = TEST_BSON("{'v': { '$maxKey': 1 } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* maxkey fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* undefined fails for deterministic. */
    tmp = TEST_BSON("{'v': { '$undefined': true } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* undefined fails for deterministic. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx, "BSON type invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* dbpointer succeeds for deterministic. */
    tmp = TEST_BSON("{'v': { '$dbPointer': {'$ref': 'ns', '$id': {'$oid': "
                    "'AAAAAAAAAAAAAAAAAAAAAAAA'} } } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* dbpointer succeeds for random. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* binary subtype 6 fails for deterministic. */
    tmp = TEST_BSON("{'v': { '$binary': { 'base64': 'AAAA', 'subType': '06' } } }");
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp),
                 ctx,
                 "BSON binary subtype 6 is invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    /* binary subtype 6 fails for random. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, random, -1), ctx);
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, tmp),
                 ctx,
                 "BSON binary subtype 6 is invalid for encryption");
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);

    mongocrypt_binary_destroy(msg);
    mongocrypt_binary_destroy(bad_name);
    mongocrypt_binary_destroy(name);
    mongocrypt_binary_destroy(string_msg);
    mongocrypt_binary_destroy(no_v_msg);
    mongocrypt_binary_destroy(key_id);
    bson_destroy(bson_bad_name);
    bson_destroy(bson_name);
    bson_destroy(bson_msg);
    bson_destroy(bson_msg_no_v);
}

/* Test individual ctx states. */
static void _test_encrypt_init(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Success. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    mongocrypt_ctx_destroy(ctx);

    /* NULL namespace. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, NULL, 0, TEST_FILE("./test/example/cmd.json")), ctx, "invalid db");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* Wrong state. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")),
                 ctx,
                 "cannot double initialize");
    mongocrypt_ctx_destroy(ctx);

    /* Empty db name is an error. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "", -1, TEST_FILE("./test/example/cmd.json")), ctx, "invalid db");
    mongocrypt_ctx_destroy(ctx);

    /* Empty coll name is an error. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "", -1, TEST_BSON("{'find': ''}")), ctx, "invalid db");
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_need_collinfo(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/collection-info.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Coll info with no schema. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */
    /* Coll info with NULL schema. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, NULL), ctx, "invalid NULL");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* No coll info. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    /* No call to ctx_mongo_feed. */
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Wrong state. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/collection-info.json")), ctx, "wrong state");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_need_markings(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;

    bin = mongocrypt_binary_new();

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    mongocrypt_ctx_mongo_op(ctx, bin);
    ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/mongocryptd-cmd.json"), bin);

    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Key alt name. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-key-alt-name.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* No placeholders. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-no-markings.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* No encryption in schema. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-no-encryption-needed.json")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Invalid marking. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-invalid.json")),
                 ctx,
                 "no 'v'");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* NULL markings. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, NULL), ctx, "invalid NULL");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Wrong state. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")),
                 ctx,
                 "wrong state");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_csfle_no_needs_markings(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

    /* Success. */
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_need_keys(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */

    /* Did not provide all keys. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "not all keys requested were satisfied");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* recreate crypt because of caching. */
}

static void _test_encrypt_ready(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *encrypted_cmd;
    _mongocrypt_buffer_t ciphertext_buf;
    _mongocrypt_ciphertext_t ciphertext;
    bson_t as_bson;
    bson_iter_t iter;
    bool ret;
    mongocrypt_status_t *status;

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

    ASSERT_OR_PRINT(crypt, status);

    /* Success. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, encrypted_cmd), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_DONE);

    /* check that the encrypted command has a valid ciphertext. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(encrypted_cmd, &as_bson));
    CRYPT_TRACEF(&crypt->log, "encrypted doc: %s", tmp_json(&as_bson));
    bson_iter_init(&iter, &as_bson);
    bson_iter_find_descendant(&iter, "filter.ssn", &iter);
    BSON_ASSERT(BSON_ITER_HOLDS_BINARY(&iter));
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&ciphertext_buf, &iter));
    ret = _mongocrypt_ciphertext_parse_unowned(&ciphertext_buf, &ciphertext, status);
    ASSERT_OR_PRINT(ret, status);

    /* check that encrypted command matches. */
    ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/encrypted-cmd.json"), encrypted_cmd);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_binary_destroy(encrypted_cmd);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_missing_region(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-no-region.json")),
                 ctx,
                 "expected UTF-8 region");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test that attempting to auto encrypt on a view is disallowed. */
static void _test_view(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON(BSON_STR({"find" : "v", "filter" : {}}))), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/collection-info-view.json")),
                 ctx,
                 "cannot auto encrypt a view");
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_ERROR);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Check that the schema identified in the schema_map by 'ns' matches the
 * 'jsonSchema' of the mongocryptd command. */
static void
_assert_schema_compares(mongocrypt_binary_t *schema_map, const char *ns, mongocrypt_binary_t *mongocryptd_cmd) {
    bson_t schema_map_bson, mongocryptd_cmd_bson, expected_schema, actual_schema;
    uint32_t len;
    const uint8_t *data;
    bson_iter_t iter;

    /* Get the schema from the map. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(schema_map, &schema_map_bson));
    BSON_ASSERT(bson_iter_init_find(&iter, &schema_map_bson, ns));
    bson_iter_document(&iter, &len, &data);
    bson_init_static(&expected_schema, data, len);

    /* Get the schema from the mongocryptd command. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(mongocryptd_cmd, &mongocryptd_cmd_bson));
    BSON_ASSERT(bson_iter_init_find(&iter, &mongocryptd_cmd_bson, "jsonSchema"));
    bson_iter_document(&iter, &len, &data);
    BSON_ASSERT(bson_init_static(&actual_schema, data, len));

    BSON_ASSERT(bson_equal(&expected_schema, &actual_schema));
}

static void _test_local_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *schema_map, *mongocryptd_cmd;

    crypt = mongocrypt_new();
    schema_map = TEST_FILE("./test/data/schema-map.json");
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, schema_map), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    /* Schema map has test.test, we should jump right to NEED_MONGO_MARKINGS */
    ctx = mongocrypt_ctx_new(crypt);
    mongocryptd_cmd = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, mongocryptd_cmd), ctx);

    /* We should get back the schema we gave. */
    _assert_schema_compares(schema_map, "test.test", mongocryptd_cmd);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_binary_destroy(mongocryptd_cmd);

    /* Schema map has test.test2, we should jump right to NEED_MONGO_MARKINGS */
    ctx = mongocrypt_ctx_new(crypt);
    mongocryptd_cmd = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON("{'find': 'test2'}")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, mongocryptd_cmd), ctx);

    /* We should get back the schema we gave. */
    _assert_schema_compares(schema_map, "test.test2", mongocryptd_cmd);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_binary_destroy(mongocryptd_cmd);

    /* Database that does not match should not get from the map. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "mismatch", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    mongocrypt_ctx_destroy(ctx);

    /* Collection that does not match should not get from the map. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON("{'find': 'mismatch'}")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_caches_collinfo(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    bson_t *cached_collinfo;
    mongocrypt_status_t *status;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    status = mongocrypt_status_new();
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/collection-info.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    /* The next ctx has the schema cached. */
    BSON_ASSERT(_mongocrypt_cache_get(&crypt->cache_collinfo, "test.test", (void **)&cached_collinfo));
    BSON_ASSERT(cached_collinfo != NULL);
    bson_destroy(cached_collinfo);
    mongocrypt_ctx_destroy(ctx);

    /* The next context enters the NEED_MONGO_MARKINGS state immediately. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _test_encrypt_caches_keys(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);
    /* The next context skips needing keys after being supplied mark documents.
     */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_cache_expiration(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_SHORT_CACHE);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    // Sleep to trigger cache expiration.
    // Cache entries expire after 1ms, but use 20ms to avoid timing errors observed on Windows distros: CDRIVER-4526
    _usleep(20 * 1000);
    /* The next context requests keys again
     */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_caches_keys_by_alt_name(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-key-alt-name.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-with-alt-name.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    /* The next context skips needing keys after being supplied mark documents.
     */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-key-alt-name.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    /* But a context requesting a different key alt name does not get it from the
     * cache. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-key-alt-name2.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-with-alt-name2.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_random(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-random.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_is_remote_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    bson_t as_bson;
    bson_iter_t iter;

    bin = mongocrypt_binary_new();

    /* isRemoteSchema = true for a remote schema. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, bin), ctx);
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &as_bson));
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "isRemoteSchema"));
    BSON_ASSERT(bson_iter_bool(&iter) == true);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);

    /* isRemoteSchema = false for a local schema. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_FILE("./test/data/schema-map.json")), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, bin), ctx);
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &as_bson));
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "isRemoteSchema"));
    BSON_ASSERT(bson_iter_bool(&iter) == false);

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _init_fails(_mongocrypt_tester_t *tester, const char *json, const char *msg) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON_STR(json)), ctx, msg);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _init_ok(_mongocrypt_tester_t *tester, const char *json) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON_STR(json)), ctx);

    if (MONGOCRYPT_CTX_NEED_MONGO_COLLINFO == mongocrypt_ctx_state(ctx)) {
        mongocrypt_binary_t *filter;
        /* verify the collection in the filter is 'coll' */
        filter = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'name': 'coll'}"), filter);

        mongocrypt_binary_destroy(filter);
    } else {
        // The "create" command transitions directly to
        // MONGOCRYPT_CTX_NEED_MONGO_MARKINGS.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _init_bypass(_mongocrypt_tester_t *tester, const char *json) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;

    bin = mongocrypt_binary_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON_STR(json)), ctx);
    BSON_ASSERT(MONGOCRYPT_CTX_READY == mongocrypt_ctx_state(ctx));
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON_STR(json), (bin));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_init_each_cmd(_mongocrypt_tester_t *tester) {
    /* collection aggregate is ok */
    _init_ok(tester, "{'aggregate': 'coll'}");
    /* db agg is not ok */
    _init_fails(tester, "{'aggregate': 1}", "non-collection command not supported for auto encryption: aggregate");
    _init_ok(tester, "{'count': 'coll'}");
    _init_ok(tester, "{'distinct': 'coll'}");
    _init_ok(tester, "{'delete': 'coll'}");
    _init_ok(tester, "{'find': 'coll'}");
    _init_ok(tester, "{'findAndModify': 'coll'}");
    _init_bypass(tester, "{'getMore': 'coll'}");
    _init_ok(tester, "{'insert': 'coll'}");
    _init_ok(tester, "{'update': 'coll'}");
    _init_bypass(tester, "{'authenticate': 1}");
    _init_bypass(tester, "{'getnonce': 1}");
    _init_bypass(tester, "{'logout': 1}");
    _init_bypass(tester, "{'isMaster': 1}");
    _init_bypass(tester, "{'abortTransaction': 1}");
    _init_bypass(tester, "{'commitTransaction': 1}");
    _init_bypass(tester, "{'endSessions': 1}");
    _init_bypass(tester, "{'startSession': 1}");
    _init_ok(tester, "{'create': 'coll'}");
    _init_ok(tester, "{'createIndexes': 'coll'}");
    _init_bypass(tester, "{'drop': 1}");
    _init_bypass(tester, "{'dropDatabase': 1}");
    _init_bypass(tester, "{'killCursors': 1}");
    _init_bypass(tester, "{'listCollections': 1}");
    _init_bypass(tester, "{'listDatabases': 1}");
    _init_bypass(tester, "{'listIndexes': 1}");
    _init_bypass(tester, "{'renameCollection': 'coll'}");
    _init_ok(tester, "{'explain': { 'find': 'coll' }}");
    _init_fails(tester, "{'explain': { } }", "invalid empty BSON");
    _init_fails(tester, "{'explain': { 'aggregate': 1 }}", "non-collection command not supported for auto encryption");
    _init_bypass(tester, "{'ping': 1}");
    _init_bypass(tester, "{'saslStart': 1}");
    _init_bypass(tester, "{'saslContinue': 1}");
    _init_fails(tester, "{'fakecmd': 'coll'}", "command not supported for auto encryption: fakecmd");
    /* fails for eligible command with no collection name. */
    _init_fails(tester, "{'insert': 1}", "non-collection command not supported for auto encryption: insert");
    _init_fails(tester, "{}", "unexpected empty BSON for command");
    _init_bypass(tester, "{'isMaster': 1}");
    _init_bypass(tester, "{'ismaster': 1}");
    _init_bypass(tester, "{'killAllSessions': 1}");
    _init_bypass(tester, "{'killSessions': 1}");
    _init_bypass(tester, "{'killAllSessionsByPattern': 1}");
    _init_bypass(tester, "{'refreshSessions': 1}");
    _init_ok(tester, "{'cleanupStructuredEncryptionData': 'coll'}");
    _init_ok(tester, "{'compactStructuredEncryptionData': 'coll'}");
    _init_bypass(tester, "{'hello': 1}");
    _init_bypass(tester, "{'buildInfo': 1}");
    _init_bypass(tester, "{'buildinfo': 1}");
    _init_bypass(tester, "{'getCmdLineOpts': 1}");
    _init_bypass(tester, "{'getLog': 1}");
    _init_ok(tester, "{'collMod': 'coll'}");
    _init_bypass(tester, "{'listSearchIndexes': 'coll' }");
    _init_bypass(tester, "{'createSearchIndexes': 'coll' }");
    _init_bypass(tester, "{'dropSearchIndex': 'coll' }");
    _init_bypass(tester, "{'updateSearchIndex': 'coll' }");
    _init_bypass(tester, "{'serverStatus': 1 }");
}

static void _test_encrypt_invalid_siblings(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);

    BSON_ASSERT(MONGOCRYPT_CTX_NEED_MONGO_COLLINFO == mongocrypt_ctx_state(ctx));
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/collinfo-siblings.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    BSON_ASSERT(MONGOCRYPT_CTX_NEED_MONGO_MARKINGS == mongocrypt_ctx_state(ctx));
    // MONGOCRYPT-771 removes checks for sibling validators.
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/mongocryptd-reply.json")), ctx);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypting_with_explicit_encryption(_mongocrypt_tester_t *tester) {
    /* Test that we do not strip existing ciphertexts when automatically
     * encrypting a document. */
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    bson_iter_t iter;
    bson_t tmp;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-reply-existing-ciphertext.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    mongocrypt_ctx_finalize(ctx, bin);
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &tmp));
    BSON_ASSERT(bson_iter_init(&iter, &tmp));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "filter.existing_ciphertext", &iter));
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_explicit_encryption(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    _mongocrypt_buffer_t from_key_id, from_key_altname;
    mongocrypt_binary_t *bin, *key_id;
    char *deterministic = MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    ctx = mongocrypt_ctx_new(crypt);
    key_id = mongocrypt_binary_new_from_data(MONGOCRYPT_DATA_AND_LEN("aaaaaaaaaaaaaaaa"));

    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 123}")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    _mongocrypt_buffer_copy_from_binary(&from_key_id, bin);
    mongocrypt_binary_destroy(bin);

    mongocrypt_binary_destroy(key_id);
    mongocrypt_ctx_destroy(ctx);

    ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, deterministic, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'keyDocumentName'}")), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 123}")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    _mongocrypt_buffer_copy_from_binary(&from_key_altname, bin);
    mongocrypt_binary_destroy(bin);

    mongocrypt_ctx_destroy(ctx);

    BSON_ASSERT(0 == _mongocrypt_buffer_cmp(&from_key_id, &from_key_altname));

    _mongocrypt_buffer_cleanup(&from_key_id);
    _mongocrypt_buffer_cleanup(&from_key_altname);

    mongocrypt_destroy(crypt);
}

/* Test with empty AWS credentials. */
static void _test_encrypt_empty_aws(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "", -1, "", -1), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document.json")),
                 ctx,
                 "failed to create KMS message");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_custom_endpoint(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms_ctx;
    mongocrypt_binary_t *bin;
    const char *endpoint;

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document-custom-endpoint.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
    BSON_ASSERT(kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(kms_ctx, &endpoint), ctx);
    BSON_ASSERT(0 == strcmp("example.com:443", endpoint));
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), ctx);
    BSON_ASSERT(NULL != strstr((char *)bin->data, "Host:example.com"));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_per_ctx_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms_ctx;
    mongocrypt_binary_t *bin;
    const char *endpoint;

    /* Success. */
    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}"));
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'aws':{'accessKeyId': 'example',"
                                                             "'secretAccessKey': 'example'}}")),
              ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/example/key-document-custom-endpoint.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
    BSON_ASSERT(kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(kms_ctx, &endpoint), ctx);
    BSON_ASSERT(0 == strcmp("example.com:443", endpoint));
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), ctx);
    BSON_ASSERT(NULL != strstr((char *)bin->data, "Host:example.com"));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Regression test for MONGOCRYPT-488.
static void _test_encrypt_per_ctx_credentials_given_empty(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}"));
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_FAILS(mongocrypt_ctx_provide_kms_providers(ctx, TEST_BSON("{}")), ctx, "no kms provider set");

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_per_ctx_credentials_local(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    /* local_kek is the KEK used to encrypt the keyMaterial in
     * ./test/data/key-document-local.json */
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'local': {}}"));
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'local':{'key': { '$binary': {'base64': '%s', "
                                                             "'subType': '00'}}}}",
                                                             local_kek)),
              ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(local_kek);
}

static void _test_encrypt_with_aws_session_token(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_binary_t *bin;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms_ctx;
    char *http_req;

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt,
                                              TEST_BSON("{'aws': {'sessionToken': 'mySessionToken', "
                                                        "'accessKeyId': 'myAccessKeyId', "
                                                        "'secretAccessKey': 'mySecretAccessKey'}}")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
    kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
    BSON_ASSERT(NULL != kms_ctx);

    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), kms_ctx);
    http_req = (char *)mongocrypt_binary_data(bin);
    ASSERT_STRCONTAINS(http_req, "X-Amz-Security-Token:mySessionToken");

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_caches_empty_collinfo(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    /* Do not feed anything for collinfo. */
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    /* Create another encryption context on the same namespace test.test. It
     * should not transition to the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO state. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_caches_collinfo_without_jsonschema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/collection-info-no-validator.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    /* Create another encryption context on the same namespace test.test. It
     * should not transition to the MONGOCRYPT_CTX_NEED_MONGO_COLLINFO state. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_with_encrypted_field_config_map(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll': {'fields': []}}")), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    /* Test encrypting a command on a collection present in the encrypted field
     * config map. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd;

        cmd_to_mongocryptd = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        ASSERT_OK(
            mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-find-explicit/reply-from-mongocryptd.json")),
            ctx);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *cmd_to_mongod;

        cmd_to_mongod = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongod.json"),
                                            cmd_to_mongod);
        mongocrypt_binary_destroy(cmd_to_mongod);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test encrypting a bypassed command on a collection present in the encrypted
 * field config map. Expect no encryptionInformation. */
static void _test_encrypt_with_encrypted_field_config_map_bypassed(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll': {'fields': []}}")), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    /* 'drop' is bypassed. Expect that no 'encryptionInformation' is appended. */
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'drop': 'coll'}")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *cmd_to_mongod;

        cmd_to_mongod = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'drop': 'coll'}"), cmd_to_mongod);
        mongocrypt_binary_destroy(cmd_to_mongod);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test that an empty jsonSchema document is appended to the command sent to
 * mongocryptd when no encryptedFieldConfig or jsonSchema is found for the
 * collection.
 *
 * This is a regression test for PYTHON-3188. */
static void _test_encrypt_no_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(
        mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'find': 'collection_without_schema', 'filter': {}}")),
        ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    /* Give no collection info. */
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd;

        cmd_to_mongocryptd = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'find': 'collection_without_schema', 'filter': {}, "
                                                      "'jsonSchema': {}, 'isRemoteSchema': true}"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_remote_encryptedfields(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    /* Test success. */
    {
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_BSON("{'name': 'coll', 'options': "
                                                          "{'encryptedFields': {'fields': []}}}")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* Check that command to mongocryptd includes "encryptionInformation". */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            /* "encryptionInformation.schema" must be the document from
             * "encryptedFields" fed from MONGOCRYPT_CTX_NEED_MONGO_COLLINFO. */
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        mongocrypt_ctx_destroy(ctx);
    }

    /* Test that the previous 'encryptedFields' is cached. */
    {
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);

        /* Check that command to mongocryptd includes "encryptionInformation". */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            /* "encryptionInformation.schema" must be the document from
             * "encryptedFields" fed from MONGOCRYPT_CTX_NEED_MONGO_COLLINFO. */
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        mongocrypt_ctx_destroy(ctx);
    }

    /* Test that "encryptedFields" is preferred over "$jsonSchema". */
    {
        /* Recreate crypt to clear cache. */
        mongocrypt_destroy(crypt);
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_BSON("{'name': 'coll', 'options': { 'validator': { '$jsonSchema': "
                                                          "{'baz': 'qux' }}, 'encryptedFields': {'fields': []}}}")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* Check that command to mongocryptd includes "encryptionInformation". */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd;

            cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            /* "encryptionInformation.schema" must be the document from
             * "encryptedFields" fed from MONGOCRYPT_CTX_NEED_MONGO_COLLINFO. */
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        mongocrypt_ctx_destroy(ctx);
    }

    mongocrypt_destroy(crypt);
}

static void _test_encrypt_with_bypassqueryanalysis(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Test with EncryptedFieldConfig from map. */
    {
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll': {'fields': []}}")), crypt);
        mongocrypt_setopt_bypass_query_analysis(crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);

        /* Should transition directly to ready. */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *cmd_to_mongod = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
            /* "encryptionInformation" must be present. */
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongod.json"),
                                                cmd_to_mongod);
            mongocrypt_binary_destroy(cmd_to_mongod);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test with EncryptedFieldConfig from listCollections. */
    {
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        mongocrypt_setopt_bypass_query_analysis(crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(
                          ctx,
                          TEST_BSON("{'name': 'coll', 'options': {'encryptedFields': {'fields': []}}}")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *cmd_to_mongod = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
            /* "encryptionInformation" must be present. */
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongod.json"),
                                                cmd_to_mongod);
            mongocrypt_binary_destroy(cmd_to_mongod);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

// Shared implementation for insert and find tests
typedef struct {
    _mongocrypt_buffer_t buf;
    int pos;
} _test_rng_data_source;

static bool _test_rng_source(void *ctx, mongocrypt_binary_t *out, uint32_t count, mongocrypt_status_t *status) {
    _test_rng_data_source *source = (_test_rng_data_source *)ctx;

    if ((source->pos + count) > source->buf.len) {
        TEST_ERROR("Out of random data, wanted: %" PRIu32, count);
        return false;
    }

    memcpy(out->data, source->buf.data + source->pos, count);
    source->pos += count;
    return true;
}

typedef enum {
    kFLE2v2Default,
    kFLE2v2Enable,
} _test_fle2v2_option;

#define TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, data_path, rng_source, v2_failure)                            \
    if (1) {                                                                                                           \
        (rng_source)->pos = 0;                                                                                         \
        _test_encrypt_fle2_encryption_placeholder(tester, data_path, rng_source, NULL);                                \
    } else                                                                                                             \
        ((void)0)

static void _test_encrypt_fle2_encryption_placeholder(_mongocrypt_tester_t *tester,
                                                      const char *data_path,
                                                      _test_rng_data_source *rng_source,
                                                      const char *finalize_failure) {
    mongocrypt_t *crypt;
    char pathbuf[2048];

#define MAKE_PATH(mypath)                                                                                              \
    if (1) {                                                                                                           \
        int pathbuf_ret = snprintf(pathbuf, sizeof(pathbuf), "./test/data/%s/%s", data_path, mypath);                  \
        ASSERT(pathbuf_ret >= 0 && (size_t)pathbuf_ret < sizeof(pathbuf));                                             \
    } else                                                                                                             \
        ((void)0)

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    /* Create crypt with custom hooks. */
    {
        /* localkey_data is the KEK used to encrypt the keyMaterial
         * in ./test/data/keys/ */
        char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
        mongocrypt_binary_t *localkey;

        crypt = mongocrypt_new();
        mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
        localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
        ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
        ASSERT_OK(mongocrypt_setopt_crypto_hooks(crypt,
                                                 _std_hook_native_crypto_aes_256_cbc_encrypt,
                                                 _std_hook_native_crypto_aes_256_cbc_decrypt,
                                                 _test_rng_source,
                                                 _std_hook_native_hmac_sha512,
                                                 _std_hook_native_hmac_sha256,
                                                 _error_hook_native_sha256,
                                                 rng_source /* ctx */),
                  crypt);

        MAKE_PATH("encrypted-field-map.json");
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_FILE(pathbuf)), crypt);
        mongocrypt_binary_destroy(localkey);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    }

    /* Create encryption context. */
    mongocrypt_ctx_t *ctx;
    {
        ctx = mongocrypt_ctx_new(crypt);
        MAKE_PATH("cmd.json");
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(pathbuf)), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        /* Use a FLE2EncryptionPlaceholder obtained from
         * https://gist.github.com/kevinAlbs/cba611fe0d120b3f67c6bee3195d4ce6. */
        MAKE_PATH("mongocryptd-reply.json");
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(pathbuf)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

#define TEST_KEY_FILE(name) TEST_FILE("./test/data/keys/" name "123498761234123456789012-local-document.json")

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_KEY_FILE("12345678")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_KEY_FILE("ABCDEFAB")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }
#undef TEST_KEY_FILE

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        bool ok = mongocrypt_ctx_finalize(ctx, out);
        if (finalize_failure) {
            ASSERT_FAILS_STATUS(ok, ctx->status, finalize_failure);
        } else {
            ASSERT_OK(ok, ctx);
            MAKE_PATH("encrypted-payload.json");
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(pathbuf), out);
        }
        mongocrypt_binary_destroy(out);
    }
#undef MAKE_PATH

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* First 16 bytes are IV for 'p' field in FLE2InsertUpdatePayload
 * Second 16 bytes are IV for 'v' field in FLE2InsertUpdatePayload
 */
#define RNG_DATA                                                                                                       \
    "\xc7\x43\xd6\x75\x76\x9e\xa7\x88\xd5\xe5\xc4\x40\xdb\x24\x0d\xf9"                                                 \
    "\x4c\xd9\x64\x10\x43\x81\xe6\x61\xfa\x1f\xa0\x5c\x49\x8e\xad\x21"

static void _test_encrypt_fle2_insert_payload(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;

    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-v2", &source, NULL);
}

static void _test_encrypt_fle2_insert_payload_with_str_encode_version(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;

    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-v2-with-str-encode-version", &source, NULL);
}

static void _test_encrypt_fle2_insert_text_search_payload(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;

    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-text-search", &source, NULL);
}

static void _test_encrypt_fle2_insert_text_search_payload_with_str_encode_version(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;

    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-text-search-with-str-encode-version", &source, NULL);
}

#undef RNG_DATA

// FLE2FindEqualityPayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_payload(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-equality-v2", &source, NULL);
}

/* 16 bytes of random data are used for IV. This IV produces the expected test
 * ciphertext. */
#define RNG_DATA "\x4d\x06\x95\x64\xf5\xa0\x5e\x9e\x35\x23\xb9\x8f\x57\x5a\xcb\x15"

static void _test_encrypt_fle2_unindexed_encrypted_payload(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-unindexed-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/int32/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_int32(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/int32-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/int64/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_int64(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/int64-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/date/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_date(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/date-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/double/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_double(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/double-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/double-precision/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_double_precision(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/double-precision-v2", &source, NULL);
}

#undef RNG_DATA

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
#include "./data/fle2-insert-range/decimal128/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_decimal128(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/decimal128-v2", &source, NULL);
}

#undef RNG_DATA

#include "./data/fle2-insert-range/decimal128-precision/RNG_DATA.h"

static void _test_encrypt_fle2_insert_range_payload_decimal128_precision(_mongocrypt_tester_t *tester) {
    uint8_t rng_data[] = RNG_DATA;
    _test_rng_data_source source = {.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-insert-range/decimal128-precision-v2", &source, NULL);
}

#undef RNG_DATA
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_int32(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/int32-v2", &source, NULL);
}

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_int64(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/int64-v2", &source, NULL);
}

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_date(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/date-v2", &source, NULL);
}

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_double(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/double-v2", &source, NULL);
}

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_double_precision(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/double-precision-v2", &source, NULL);
}

#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_decimal128(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/decimal128-v2", &source, NULL);
}

// FLE2FindRangePayload only uses deterministic token generation.
static void _test_encrypt_fle2_find_range_payload_decimal128_precision(_mongocrypt_tester_t *tester) {
    _test_rng_data_source source = {{0}};
    TEST_ENCRYPT_FLE2_ENCRYPTION_PLACEHOLDER(tester, "fle2-find-range/decimal128-precision-v2", &source, NULL);
}
#endif // MONGOCRYPT_HAVE_DECIMAL128_SUPPORT

static mongocrypt_t *_crypt_with_rng(_test_rng_data_source *rng_source) {
    mongocrypt_t *crypt;
    mongocrypt_binary_t *localkey;
    /* localkey_data is the KEK used to encrypt the keyMaterial
     * in ./test/data/keys/ */
    char localkey_data[MONGOCRYPT_KEY_LEN] = {0};

    crypt = mongocrypt_new();
    mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
    localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
    ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
    ASSERT_OK(mongocrypt_setopt_crypto_hooks(crypt,
                                             _std_hook_native_crypto_aes_256_cbc_encrypt,
                                             _std_hook_native_crypto_aes_256_cbc_decrypt,
                                             _test_rng_source,
                                             _std_hook_native_hmac_sha512,
                                             _std_hook_native_hmac_sha256,
                                             _error_hook_native_sha256,
                                             rng_source /* ctx */),
              crypt);

    mongocrypt_binary_destroy(localkey);
    ASSERT_OK(mongocrypt_init(crypt), crypt);
    return crypt;
}

typedef struct {
    const char *desc;
    _test_rng_data_source rng_data;
    const char *algorithm;
    _mongocrypt_buffer_t *user_key_id;
    _mongocrypt_buffer_t *index_key_id;
    mc_optional_int64_t contention_factor;
    mongocrypt_binary_t *range_opts;
    mongocrypt_binary_t *text_opts;
    const char *query_type;
    mongocrypt_binary_t *msg;
    mongocrypt_binary_t *keys_to_feed[3]; // NULL terminated list.
    mongocrypt_binary_t *expect;
    const char *expect_finalize_error;
    const char *expect_init_error;
    bool is_expression;
} ee_testcase;

static void ee_testcase_run(ee_testcase *tc) {
    TEST_PRINTF("  explicit_encryption_finalize test case: %s ... begin\n", tc->desc);
    extern void mc_reset_payloadId_for_testing(void);
    mc_reset_payloadId_for_testing();
    mongocrypt_t *crypt;
    if (tc->rng_data.buf.len > 0) {
        // Use fixed data for random number generation to produce deterministic
        // results.
        crypt = _crypt_with_rng(&tc->rng_data);
    } else {
        tester_mongocrypt_flags flags = TESTER_MONGOCRYPT_DEFAULT;
        crypt = _mongocrypt_tester_mongocrypt(flags);
    }
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    if (tc->algorithm) {
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, tc->algorithm, -1), ctx);
    }
    if (tc->user_key_id) {
        ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, _mongocrypt_buffer_as_binary(tc->user_key_id)), ctx);
    }
    if (tc->index_key_id) {
        ASSERT_OK(mongocrypt_ctx_setopt_index_key_id(ctx, _mongocrypt_buffer_as_binary(tc->index_key_id)), ctx);
    }
    if (tc->contention_factor.set) {
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, tc->contention_factor.value), ctx);
    }
    if (tc->range_opts) {
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, tc->range_opts), ctx);
    }
    if (tc->query_type) {
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, tc->query_type, -1), ctx);
    }
    if (tc->text_opts) {
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, tc->text_opts), ctx);
    }
    BSON_ASSERT(tc->msg);
    {
        bool ret;
        if (tc->is_expression) {
            ret = mongocrypt_ctx_explicit_encrypt_expression_init(ctx, tc->msg);
        } else {
            ret = mongocrypt_ctx_explicit_encrypt_init(ctx, tc->msg);
        }
        if (tc->expect_init_error) {
            ASSERT_FAILS(ret, ctx, tc->expect_init_error);
            goto cleanup;
        } else {
            ASSERT_OK(ret, ctx);
        }
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        for (size_t i = 0; i < sizeof(tc->keys_to_feed) / sizeof(tc->keys_to_feed[0]); i++) {
            mongocrypt_binary_t *key_to_feed = tc->keys_to_feed[i];
            if (!key_to_feed) {
                break;
            }
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key_to_feed), ctx);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *got = mongocrypt_binary_new();

        bool ret = mongocrypt_ctx_finalize(ctx, got);
        if (tc->expect_finalize_error) {
            ASSERT_FAILS(ret, ctx, tc->expect_finalize_error);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
        } else {
            ASSERT_OK(ret, ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(tc->expect, got);
        }
        mongocrypt_binary_destroy(got);
    }

cleanup:
    TEST_PRINTF("  explicit_encryption_finalize test case: %s ... end\n", tc->desc);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Test the finalized output of explicit encryption.
static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t keyABC_id;
    _mongocrypt_buffer_t key123_id;

    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    _mongocrypt_buffer_copy_from_hex(&keyABC_id, "ABCDEFAB123498761234123456789012");
    _mongocrypt_buffer_copy_from_hex(&key123_id, "12345678123498761234123456789012");

    mongocrypt_binary_t *keyABC = TEST_FILE("./test/data/keys/"
                                            "ABCDEFAB123498761234123456789012-local-"
                                            "document.json");
    mongocrypt_binary_t *key123 = TEST_FILE("./test/data/keys/"
                                            "12345678123498761234123456789012-local-"
                                            "document.json");

    {
        ee_testcase tc = {0};
        tc.desc = "Unindexed (v2)";
#define RNG_DATA "\x4d\x06\x95\x64\xf5\xa0\x5e\x9e\x35\x23\xb9\x8f\x57\x5a\xcb\x15"
        uint8_t rng_data[] = RNG_DATA;
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_UNINDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.msg = TEST_BSON("{'v': 'value123'}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_BSON("{'v' : {'$binary' : {'base64': "
                              "'EKvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFVl3jjP90PgD4T+Mtubn/"
                              "mm4CKsKGaV1yxlic9Dty1Adef4Y+bsLGKhBbCa5eojM/A==','subType' : '06'}}}");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "Indexed (v2)";
#define RNG_DATA                                                                                                       \
    "\xc7\x43\xd6\x75\x76\x9e\xa7\x88\xd5\xe5\xc4\x40\xdb\x24\x0d\xf9"                                                 \
    "\x4c\xd9\x64\x10\x43\x81\xe6\x61\xfa\x1f\xa0\x5c\x49\x8e\xad\x21"
        uint8_t rng_data[] = RNG_DATA;
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.msg = TEST_BSON("{'v': 'value123'}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-indexed-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "Indexed with non-zero ContentionFactor. Random number chosen is 0 (v2)";
/* First 8 bytes are for random ContentionFactor.
 * Second 16 bytes are IV for 'p' field in FLE2InsertUpdatePayload
 * Third 16 bytes are IV for 'v' field in FLE2InsertUpdatePayload
 */
#define RNG_DATA                                                                                                       \
    "\x00\x00\x00\x00\x00\x00\x00\x00"                                                                                 \
    "\xc7\x43\xd6\x75\x76\x9e\xa7\x88\xd5\xe5\xc4\x40\xdb\x24\x0d\xf9"                                                 \
    "\x4c\xd9\x64\x10\x43\x81\xe6\x61\xfa\x1f\xa0\x5c\x49\x8e\xad\x21"
        uint8_t rng_data[] = RNG_DATA;
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'value123'}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-indexed-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "Indexed with non-zero ContentionFactor. Random number chosen is 1 (v2)";
/* First 8 bytes are for random ContentionFactor.
 * Second 16 bytes are IV for 'p' field in FLE2InsertUpdatePayload
 * Third 16 bytes are IV for 'v' field in FLE2InsertUpdatePayload
 */
#define RNG_DATA                                                                                                       \
    "\x01\x00\x00\x00\x00\x00\x00\x00"                                                                                 \
    "\xc7\x43\xd6\x75\x76\x9e\xa7\x88\xd5\xe5\xc4\x40\xdb\x24\x0d\xf9"                                                 \
    "\x4c\xd9\x64\x10\x43\x81\xe6\x61\xfa\x1f\xa0\x5c\x49\x8e\xad\x21"
        uint8_t rng_data[] = RNG_DATA;
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'value123'}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/"
                              "insert-indexed-contentionFactor1-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "omitted index_key_id defaults to using user_key_id (v2)";
#define RNG_DATA                                                                                                       \
    "\xc7\x43\xd6\x75\x76\x9e\xa7\x88\xd5\xe5\xc4\x40\xdb\x24\x0d\xf9"                                                 \
    "\x4c\xd9\x64\x10\x43\x81\xe6\x61\xfa\x1f\xa0\x5c\x49\x8e\xad\x21"
        uint8_t rng_data[] = RNG_DATA;
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data, .len = sizeof(rng_data) - 1u}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.msg = TEST_BSON("{'v': 'value123'}");
        tc.keys_to_feed[0] = keyABC;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/"
                              "insert-indexed-same-user-and-index-key-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Indexed' with query type (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_EQUALITY_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-indexed-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Indexed' with query type and non-zero contention factor (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_EQUALITY_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-indexed-contentionFactor1-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "Negative contention factor is an error on insert (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(-1);
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.expect_init_error = "contention must be non-negative";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "INT64_MAX contention factor is an error on insert (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_INDEXED_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(INT64_MAX);
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.expect_init_error = "contention must be < INT64_MAX";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with int32 (v2)";
#include "./data/fle2-insert-range-explicit/int32/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_FILE("./test/data/fle2-insert-range-explicit/"
                                  "int32/rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-insert-range-explicit/int32/"
                           "value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-insert-range-explicit/int32/"
                              "encrypted-payload-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with sparsity=2 with int32 (v2)";
#include "./data/fle2-insert-range-explicit/sparsity-2/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_FILE("./test/data/fle2-insert-range-explicit/"
                                  "sparsity-2/rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-insert-range-explicit/sparsity-2/"
                           "value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-insert-range-explicit/sparsity-2/"
                              "encrypted-payload-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with query_type='range' with int32 (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(4);
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.range_opts = TEST_FILE("./test/data/fle2-find-range-explicit/"
                                  "int32/rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-find-range-explicit/int32/"
                           "value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.expect = TEST_FILE("./test/data/fle2-find-range-explicit/int32/"
                              "encrypted-payload-v2.json");
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "An unsupported range BSON type is an error (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON("{'min': 0, 'max': 1, 'sparsity': {'$numberLong': '1'}}");
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "expected matching 'min' and value type";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with query_type='range' with double with "
                  "precision (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.range_opts = TEST_FILE("./test/data/fle2-find-range-explicit/double-precision/"
                                  "rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-find-range-explicit/"
                           "double-precision/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-find-range-explicit/"
                              "double-precision/encrypted-payload-v2.json");
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with double precision with precision (v2)";
#include "./data/fle2-insert-range-explicit/double-precision/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_FILE("./test/data/fle2-insert-range-explicit/double-precision/"
                                  "rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-insert-range-explicit/"
                           "double-precision/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-insert-range-explicit/double-precision/"
                              "encrypted-payload-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with query_type='range' with double without "
                  "precision (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.range_opts = TEST_FILE("./test/data/fle2-find-range-explicit/double/"
                                  "rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-find-range-explicit/double/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-find-range-explicit/double/encrypted-payload-v2.json");
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "algorithm='Range' with double without precision (v2)";
#include "./data/fle2-insert-range-explicit/double/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.index_key_id = &key123_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_FILE("./test/data/fle2-insert-range-explicit/double/"
                                  "rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-insert-range-explicit/double/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.keys_to_feed[1] = key123;
        tc.expect = TEST_FILE("./test/data/fle2-insert-range-explicit/double/"
                              "encrypted-payload-v2.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "min > max for insert (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON("{'min': 1, 'max': 0, 'sparsity': {'$numberLong': '1'}}");
        tc.msg = TEST_FILE("./test/data/fle2-insert-range-explicit/int32/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "minimum value must be less than the maximum value";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "min > max for find (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON("{'min': 25, 'max': 24, 'sparsity': {'$numberLong': '1'}}");
        tc.msg = TEST_FILE("./test/data/fle2-find-range-explicit/int32/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "minimum value must be less than the maximum value";
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "open interval (v2)";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_FILE("./test/data/fle2-find-range-explicit/"
                                  "int32-openinterval/rangeopts.json");
        tc.msg = TEST_FILE("./test/data/fle2-find-range-explicit/"
                           "int32-openinterval/value-to-encrypt.json");
        tc.keys_to_feed[0] = keyABC;
        tc.expect = TEST_FILE("./test/data/fle2-find-range-explicit/"
                              "int32-openinterval/encrypted-payload-v2.json");
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

#define RAW_STRING(...) #__VA_ARGS__

    {
        ee_testcase tc = {0};
        tc.desc = "min is required to insert int for range";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON(RAW_STRING({"max" : {"$numberInt" : "200"}, "sparsity" : {"$numberLong" : "1"}}));
        tc.msg = TEST_BSON(RAW_STRING({"v" : {"$numberInt" : "1"}}));
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "Range option 'min' is required";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "max is required to insert int for range";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON(RAW_STRING({"min" : {"$numberInt" : "0"}, "sparsity" : {"$numberLong" : "1"}}));
        tc.msg = TEST_BSON(RAW_STRING({"v" : {"$numberInt" : "1"}}));
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "Range option 'max' is required";
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "min is required to find int for range";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON(RAW_STRING({"max" : {"$numberInt" : "200"}, "sparsity" : {"$numberLong" : "1"}}));
        tc.msg = TEST_BSON(RAW_STRING({
            "v" : {"$and" :
                       [ {"age" : {"$gte" : {"$numberInt" : "23"}}}, {"age" : {"$lte" : {"$numberInt" : "35"}}} ]}
        }));
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "Range option 'min' is required";
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "max is required to find int for range";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON(RAW_STRING({"min" : {"$numberInt" : "0"}, "sparsity" : {"$numberLong" : "1"}}));
        tc.msg = TEST_BSON(RAW_STRING({
            "v" : {"$and" :
                       [ {"age" : {"$gte" : {"$numberInt" : "23"}}}, {"age" : {"$lte" : {"$numberInt" : "35"}}} ]}
        }));
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "Range option 'max' is required";
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "crossed bounds fails";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.user_key_id = &keyABC_id;
        tc.contention_factor = OPT_I64(0);
        tc.range_opts = TEST_BSON(RAW_STRING({"min" : {"$numberInt" : "0"}, "max" : {"$numberInt" : "100"}}));
        tc.msg = TEST_BSON(RAW_STRING({
            "v" :
                {"$and" : [ {"age" : {"$gt" : {"$numberInt" : "25"}}}, {"age" : {"$lt" : {"$numberInt" : "15"}}} ]}
        }));
        tc.keys_to_feed[0] = keyABC;
        tc.expect_finalize_error = "must be less than or equal to range max";
        tc.is_expression = true;
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "find suffix";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-suffix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "find prefix";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-prefix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "find suffix";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-suffix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "find prefix";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-prefix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert substring";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "substring" : {"strMaxLength" : 100, "strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-substring.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert suffix";
#include "./data/fle2-insert-text-search/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-suffix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert prefix";
#include "./data/fle2-insert-text-search/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-prefix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert substring";
#include "./data/fle2-insert-text-search/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "substring" : {"strMaxLength" : 100, "strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-substring.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert prefix + suffix";
#include "./data/fle2-insert-text-search/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100},
            "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-prefix-suffix.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "insert casef + diacf";
#include "./data/fle2-insert-text-search/RNG_DATA.h"
        tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}};
#undef RNG_DATA
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/insert-casef-diacf.json");
        ee_testcase_run(&tc);
    }

    {
        ee_testcase tc = {0};
        tc.desc = "find prefix on a field with prefix+suffix";
        tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR;
        tc.contention_factor = OPT_I64(1);
        tc.msg = TEST_BSON("{'v': 'abc'}");
        tc.user_key_id = &keyABC_id;
        tc.keys_to_feed[0] = keyABC;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR;
        tc.text_opts = TEST_BSON(RAW_STRING({
            "caseSensitive" : true,
            "diacriticSensitive" : true,
            "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100},
            "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100}
        }));
        tc.expect = TEST_FILE("./test/data/fle2-explicit/find-prefix.json");
        ee_testcase_run(&tc);
    }

    _mongocrypt_buffer_cleanup(&keyABC_id);
    _mongocrypt_buffer_cleanup(&key123_id);
}

static void _test_encrypt_applies_default_state_collections(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    /* Defaults are applied */
    {
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll': {'fields': []}}")), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'find': 'coll'}")), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            const char *expect_schema = "{ 'fields': [], 'escCollection': "
                                        "'enxcol_.coll.esc', 'ecocCollection': "
                                        "'enxcol_.coll.ecoc' }";
            mongocrypt_binary_t *cmd_to_mongocryptd;

            cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'find': 'coll', 'encryptionInformation': { 'type': 1, "
                                                          "'schema': { 'db.coll':  %s }}}",
                                                          expect_schema),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    /* Defaults do not override. */
    {
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_BSON("{'db.coll': { 'fields': [], 'escCollection': 'esc', 'ecocCollection': 'ecoc'}}")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'find': 'coll'}")), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            const char *expect_schema = "{'fields': [], 'escCollection': 'esc', 'ecocCollection': 'ecoc' }";
            mongocrypt_binary_t *cmd_to_mongocryptd;

            cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'find': 'coll', 'encryptionInformation': { 'type': 1, "
                                                          "'schema': { 'db.coll':  %s }}}",
                                                          expect_schema),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    /* Test with some defaults. */
    {
        crypt = mongocrypt_new();
        ASSERT_OK(
            mongocrypt_setopt_kms_providers(crypt,
                                            TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
            crypt);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt,
                                                               TEST_BSON("{'fields': [], 'db.coll': {'escCollection': "
                                                                         "'esc', 'fields': []}}")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'find': 'coll'}")), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            const char *expect_schema =
                "{'escCollection': 'esc', 'fields': [], 'ecocCollection': 'enxcol_.coll.ecoc' }";
            mongocrypt_binary_t *cmd_to_mongocryptd;

            cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'find': 'coll', 'encryptionInformation': { 'type': 1, "
                                                          "'schema': { 'db.coll': %s }}}",
                                                          expect_schema),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

/* Test encrypting an empty 'delete' command without values to be encrypted.
 * Expect deleteTokens to not be applied. */
static void _test_encrypt_fle2_delete_v2(_mongocrypt_tester_t *tester) {
    tester_mongocrypt_flags flags = TESTER_MONGOCRYPT_DEFAULT;

    /* Test success. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(flags);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-delete/success/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/success/collinfo.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/success/mongocryptd-reply.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "ABCDEFAB123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-delete/success/encrypted-payload-v2.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    /* Test with no encrypted values. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(flags);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-delete/empty/cmd.json")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/empty/collinfo.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/empty/mongocryptd-reply.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* We do not need delete tokens in v2 so we skip need keys state. */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-delete/empty/encrypted-payload-v2.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test that deleteTokens are not appended when bypassQueryAnalysis is true in v2. */
    {
        mongocrypt_t *crypt = mongocrypt_new();
        /* Configure crypt. */
        {
            char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
            mongocrypt_binary_t *localkey;
            localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
            ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
            mongocrypt_binary_destroy(localkey);
            mongocrypt_setopt_bypass_query_analysis(crypt);
            ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        }

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-delete/empty/cmd.json")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/empty/collinfo.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* We do not need delete tokens in v2 so we skip need keys state. */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-delete/empty/encrypted-payload-v2.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test that deleteTokens are not appended when using an
     * encrypted_field_config_map in v2. */
    {
        mongocrypt_t *crypt = mongocrypt_new();
        /* Configure crypt. */
        {
            char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
            mongocrypt_binary_t *localkey;
            localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
            ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
            mongocrypt_binary_destroy(localkey);
            ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt,
                                                                   TEST_FILE("./test/data/fle2-delete/success/"
                                                                             "encrypted-field-config-map.json")),
                      crypt);
            ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        }

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-delete/success/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-delete/success/mongocryptd-reply.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "ABCDEFAB123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-delete/success/encrypted-payload-v2.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Test that deleteTokens are not appended when using an
     * encrypted_field_config_map and bypass_query_analysis in v2. */
    {
        mongocrypt_t *crypt = mongocrypt_new();
        /* Configure crypt. */
        {
            char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
            mongocrypt_binary_t *localkey;
            localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
            ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
            mongocrypt_binary_destroy(localkey);
            ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt,
                                                                   TEST_FILE("./test/data/fle2-delete/empty/"
                                                                             "encrypted-field-config-map.json")),
                      crypt);
            mongocrypt_setopt_bypass_query_analysis(crypt);
            ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        }

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-delete/empty/cmd.json")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-delete/empty/encrypted-payload-v2.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_encrypt_fle2_delete(_mongocrypt_tester_t *tester) {
    _test_encrypt_fle2_delete_v2(tester);
}

/* Test behavior introduced in MONGOCRYPT-423: "encryptionInformation" is
 * omitted when no values are encrypted for eligible commands.*/
static void _test_encrypt_fle2_omits_encryptionInformation(_mongocrypt_tester_t *tester) {
    /* 'find' does not include 'encryptionInformation' if no fields are
     * encrypted. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'find': 'coll'}")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_BSON("{'name': 'coll', 'options': "
                                                          "{'encryptedFields': {'fields': []}}}")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* Check that command to mongocryptd includes "encryptionInformation". */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/find-with-encryptionInformation.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *cmd_to_mongod;

            cmd_to_mongod = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON("{'find': 'coll'}"), cmd_to_mongod);
            mongocrypt_binary_destroy(cmd_to_mongod);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* 'find' includes encryptionInformation if the initial command includes an
     * explicitly encrypted payload. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx;

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-find-explicit/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_BSON("{'name': 'coll', 'options': "
                                                          "{'encryptedFields': {'fields': []}}}")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        /* Check that command to mongocryptd includes "encryptionInformation". */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *cmd_to_mongod;

            cmd_to_mongod = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, cmd_to_mongod), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-find-explicit/cmd-to-mongod.json"),
                                                cmd_to_mongod);
            mongocrypt_binary_destroy(cmd_to_mongod);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_encrypt_fle2_explain_with_mongocryptd(_mongocrypt_tester_t *tester) {
    /* Test with an encrypted value. Otherwise 'encryptionInformation' is not
     * appended. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(
            mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-explain/with-mongocryptd/cmd.json")),
            ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-explain/with-mongocryptd/collinfo.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-explain/with-mongocryptd/"
                                                          "cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);

            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/fle2-explain/with-mongocryptd/"
                                                          "mongocryptd-reply.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "ABCDEFAB123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-explain/with-mongocryptd/"
                                                          "encrypted-payload.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_encrypt_fle2_explain_with_csfle(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

    /* Test with an encrypted value. Otherwise 'encryptionInformation' is not
     * appended. */
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-explain/with-csfle/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-explain/with-csfle/collinfo.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/keys/"
                                                          "12345678123498761234123456789012-local-document.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-explain/with-csfle/"
                                                          "encrypted-payload.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_encrypt_fle1_explain_with_mongocryptd(_mongocrypt_tester_t *tester) {
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(
            mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-explain/with-mongocryptd/cmd.json")),
            ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle1-explain/with-mongocryptd/collinfo.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-explain/with-mongocryptd/"
                                                          "cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);

            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                                TEST_FILE("./test/data/fle1-explain/with-mongocryptd/"
                                                          "mongocryptd-reply.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-explain/with-mongocryptd/"
                                                          "encrypted-payload.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_encrypt_fle1_explain_with_csfle(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-explain/with-csfle/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle1-explain/with-csfle/collinfo.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-explain/with-csfle/"
                                                          "encrypted-payload.json"),
                                                out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

// Test that an input command with $db preserves $db in the output.
static void _test_dollardb_preserved(_mongocrypt_tester_t *tester) {
    /* Test with an encrypted value. */
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/dollardb/preserved/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/dollardb/preserved/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved/"
                                                      "cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);

        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/dollardb/preserved/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved/"
                                                      "encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Test that an input command with $db preserves $db in the output, when no
// values are encrypted.
static void _test_dollardb_preserved_empty(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/dollardb/preserved_empty/cmd.json")),
              ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/dollardb/preserved_empty/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved_empty/"
                                                      "cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);

        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/dollardb/preserved_empty/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved_empty/"
                                                      "encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Test that an input command with no $db does not include $db in the output.
static void _test_dollardb_omitted(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/dollardb/omitted/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/dollardb/omitted/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/omitted/"
                                                      "cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);

        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/dollardb/omitted/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/omitted/"
                                                      "encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Test that an input command with $db does includes $db in the output for FLE1.
static void _test_dollardb_preserved_fle1(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/dollardb/preserved_fle1/cmd.json")),
              ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/dollardb/preserved_fle1/collinfo.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved_fle1/"
                                                      "cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);

        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/dollardb/preserved_fle1/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "12345678123498761234123456789012-local-document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/dollardb/preserved_fle1/"
                                                      "encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

#define expect_mongo_op(ctx, expect)                                                                                   \
    if (1) {                                                                                                           \
        mongocrypt_binary_t *got = mongocrypt_binary_new();                                                            \
        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, got), ctx);                                                             \
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON((expect), got);                                                            \
        mongocrypt_binary_destroy(got);                                                                                \
    } else                                                                                                             \
        ((void)0)

#define expect_and_reply_to_ismaster(ctx)                                                                              \
    if (1) {                                                                                                           \
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);                             \
        expect_mongo_op(ctx, TEST_BSON("{'isMaster': 1}"));                                                            \
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-ismaster-27.json")), ctx);         \
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);                                                                \
    } else                                                                                                             \
        ((void)0)

#define expect_and_reply_to_ismaster_26(ctx)                                                                           \
    if (1) {                                                                                                           \
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);                             \
        expect_mongo_op(ctx, TEST_BSON("{'isMaster': 1}"));                                                            \
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-ismaster-26.json")), ctx);         \
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);                                                                \
    } else                                                                                                             \
        ((void)0)

static void _test_fle1_create_without_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-create/without-schema/cmd.json")),
              ctx);

    expect_and_reply_to_ismaster(ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/without-schema/cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/fle1-create/without-schema/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/without-schema/encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test encrypting a "create" command with a schema from the schema map. */
static void _test_fle1_create_with_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_FILE("./test/data/fle1-create/with-schema/schema-map.json")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-create/with-schema/cmd.json")),
              ctx);

    expect_and_reply_to_ismaster(ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/with-schema/cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/fle1-create/with-schema/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/with-schema/encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test encrypting a "create" command with a schema included in the "create"
 * command. This is a regression test for MONGOCRYPT-436. */
static void _test_fle1_create_with_cmd_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-create/with-cmd-schema/cmd.json")),
              ctx);

    expect_and_reply_to_ismaster(ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
            TEST_FILE("./test/data/fle1-create/with-cmd-schema/cmd-to-mongocryptd.json"),
            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/fle1-create/with-cmd-schema/"
                                                      "mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/with-cmd-schema/encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test encrypting the "create" command with mongocryptd version < 6.0.0.
 * Expect the "create" command not to be sent to mongocryptd. */
static void _test_fle1_create_old_mongocryptd(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-create/old-mongocryptd/cmd.json")),
              ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/old-mongocryptd/"
                                                      "ismaster-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/fle1-create/old-mongocryptd/"
                                                      "mongocryptd-ismaster.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/old-mongocryptd/encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_fle1_create_with_csfle(_mongocrypt_tester_t *tester) {
    if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
        TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
        return;
    }

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-create/with-schema/cmd.json")),
              ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-create/with-schema/encrypted-payload.json"),
                                            out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void test_successful_fle2_create(_mongocrypt_tester_t *tester,
                                        const char *efc_map_path,
                                        const char *cmd_path,
                                        const char *cmd_to_cryptd_path,
                                        const char *cryptd_reply_path,
                                        const char *encrypted_payload_path) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_FILE(efc_map_path)), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE(cmd_path)), ctx);

    expect_and_reply_to_ismaster(ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(cmd_to_cryptd_path), cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(cryptd_reply_path)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE(encrypted_payload_path), out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

#define TEST_SUCCESSFUL_FLE2_CREATE(efc_path, cmd_path, cryptd_path, payload_path)                                     \
    test_successful_fle2_create(tester,                                                                                \
                                "./test/data/" efc_path "/encrypted-field-config-map.json",                            \
                                "./test/data/" cmd_path "/cmd.json",                                                   \
                                "./test/data/" cryptd_path "/cmd-to-mongocryptd.json",                                 \
                                "./test/data/" cryptd_path "/mongocryptd-reply.json",                                  \
                                "./test/data/" payload_path "/encrypted-payload.json")

#define TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR(path) TEST_SUCCESSFUL_FLE2_CREATE(path, path, path, path)

static void _test_fle2_create(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR("fle2-create");
}

static void _test_fle2_create_with_encrypted_fields(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR("fle2-create-encrypted-collection");
}

static void _test_fle2_create_with_encrypted_fields_and_str_encode_version(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR("fle2-create-encrypted-collection-with-str-encode-version");
}

static void _test_fle2_create_with_encrypted_fields_unset_str_encode_version(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE("fle2-create-encrypted-collection-with-str-encode-version",
                                "fle2-create-encrypted-collection",
                                "fle2-create-encrypted-collection-encrypted-fields-unset-str-encode-version",
                                "fle2-create-encrypted-collection-with-str-encode-version");
}

static void _test_fle2_text_search_create_with_encrypted_fields(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR("fle2-text-search-create-encrypted-collection");
}

static void _test_fle2_text_search_create_with_encrypted_fields_and_str_encode_version(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE_ONEDIR("fle2-text-search-create-encrypted-collection-with-str-encode-version");
}

static void _test_fle2_text_search_create_with_encrypted_fields_unset_str_encode_version(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE("fle2-text-search-create-encrypted-collection-with-str-encode-version",
                                "fle2-text-search-create-encrypted-collection",
                                "fle2-text-search-create-encrypted-collection",
                                "fle2-text-search-create-encrypted-collection-with-str-encode-version");
}

static void
_test_fle2_text_search_create_with_encrypted_fields_unmatching_str_encode_version(_mongocrypt_tester_t *tester) {
    TEST_SUCCESSFUL_FLE2_CREATE("fle2-text-search-create-encrypted-collection",
                                "fle2-text-search-create-encrypted-collection-with-str-encode-version",
                                "fle2-text-search-create-encrypted-collection-with-str-encode-version",
                                "fle2-text-search-create-encrypted-collection-with-str-encode-version");
}

// Test that the JSON Schema found from a "create" command is not cached.
static void _test_fle2_create_does_not_cache_empty_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    // Auto encrypt a "create" to "db.coll".
    {
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-create/cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);

        // Expect MONGOCRYPT_CTX_NEED_MONGO_COLLINFO is skipped since no server-side schema is expected for "create".
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        mongocrypt_ctx_destroy(ctx);
    }

    // Auto encrypt "find" to "db.coll". Expect server-side schema is requested.
    {
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON(BSON_STR({"find" : "coll", "filter" : {}}))),
                  ctx);
        // The MONGOCRYPT_CTX_NEED_COLLINFO state is entered to request a server-side schema.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        mongocrypt_ctx_destroy(ctx);
    }

    mongocrypt_destroy(crypt);
}

/* Regression test for MONGOCRYPT-435 */
static void _test_fle2_create_bypass_query_analysis(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                  crypt,
                  TEST_FILE("./test/data/fle2-create/encrypted-field-config-map.json")),
              crypt);
    mongocrypt_setopt_bypass_query_analysis(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-create/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle2-create/cmd.json"), out);
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// Test the error message returned on macOS versions that do not support AES-CTR
// mode. This tests behavior changed in MONGOCRYPT-440.
static void _test_encrypt_macos_no_ctr(_mongocrypt_tester_t *tester) {
    _mongocrypt_buffer_t key_id;

    if (_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with CTR support detected. Skipping.");
        return;
    }

    _mongocrypt_buffer_copy_from_hex(&key_id, "ABCDEFAB123498761234123456789012");

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, _mongocrypt_buffer_as_binary(&key_id)), ctx);
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 'value123'}")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx,
                                            TEST_FILE("./test/data/keys/"
                                                      "ABCDEFAB123498761234123456789012-local-"
                                                      "document.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *got = mongocrypt_binary_new();

        ASSERT_FAILS(mongocrypt_ctx_finalize(ctx, got), ctx, "CTR mode is only supported on macOS 10.15+");
        mongocrypt_binary_destroy(got);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* If collMod contains a $jsonSchema, expect the same $jsonSchema to be used in
 * the command to mongocryptd. This is a regression test for MONGOCRYPT-463. */
static void _test_fle1_collmod_with_jsonSchema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle1-collMod/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/fle1-collMod/cmd-to-mongocryptd.json"),
                                            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle1-collMod/mongocryptd-reply.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* If collMod does not contain a $jsonSchema, expect a schema to be requested.
 */
static void _test_fle1_collmod_without_jsonSchema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON("{'collMod': 'encryptedCollection'}")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

#define BSON_STR(...) #__VA_ARGS__

static void _test_bulkWrite(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Required by QEv2 encryption. Skipping.");
        return;
    }

    // local_kek is the KEK used to encrypt the keyMaterial in ./test/data/key-document-local.json
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));

    // Test initializing bulkWrite commands.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(crypt);
        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/bulkWrite/simple/encrypted-field-map.json")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Successful case.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"bulkWrite" : 1, "nsInfo" : [ {"ns" : "db.coll"} ]}));
            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx);
            mongocrypt_ctx_destroy(ctx);
        }

        // No `nsInfo`.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"bulkWrite" : 1}));
            ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx, "failed to find namespace");
            mongocrypt_ctx_destroy(ctx);
        }

        // `nsInfo` is not an array.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"bulkWrite" : 1, "nsInfo" : {"foo" : "bar"}}));
            ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx, "failed to find namespace");
            mongocrypt_ctx_destroy(ctx);
        }

        // `nsInfo.ns` is not correct form.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"bulkWrite" : 1, "nsInfo" : [ {"ns" : "invalid"} ]}));
            ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx, "expected namespace to contain dot");
            mongocrypt_ctx_destroy(ctx);
        }

        // `nsInfo` is empty.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd = TEST_BSON(BSON_STR({"bulkWrite" : 1, "nsInfo" : []}));
            ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx, "failed to find namespace");
            mongocrypt_ctx_destroy(ctx);
        }

        // `nsInfo` has more than one entry.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            mongocrypt_binary_t *cmd =
                TEST_BSON(BSON_STR({"bulkWrite" : 1, "nsInfo" : [ {"ns" : "db.coll"}, {"ns" : "db.coll2"} ]}));
            ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, cmd), ctx, "found more than one");
            mongocrypt_ctx_destroy(ctx);
        }

        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite with one namespace.
    {
        mongocrypt_t *crypt = mongocrypt_new();

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/bulkWrite/simple/encrypted-field-map.json")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/simple/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/bulkWrite/simple/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/bulkWrite/simple/mongocryptd-reply.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);

            // Match results.
            bson_t out_bson;
            ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
            mongocrypt_binary_t *pattern = TEST_FILE("./test/data/bulkWrite/simple/encrypted-payload-pattern.json");
            bson_t pattern_bson;
            ASSERT(_mongocrypt_binary_to_bson(pattern, &pattern_bson));
            _assert_match_bson(&out_bson, &pattern_bson);

            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite with remote encryptedFields.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(crypt);

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/simple/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB);
        {
            // Ensure the requested database is obtained from `nsInfo` (and not "admin").
            const char *db = mongocrypt_ctx_mongo_db(ctx);
            ASSERT_OK(db, ctx);
            ASSERT_STREQUAL(db, "db");

            {
                mongocrypt_binary_t *cmd = mongocrypt_binary_new();
                ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
                bson_t cmd_bson;
                ASSERT(_mongocrypt_binary_to_bson(cmd, &cmd_bson));
                _assert_match_bson(&cmd_bson, TMP_BSON(BSON_STR({"name" : "test"})));
                mongocrypt_binary_destroy(cmd);
            }
            // Feed back response.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/bulkWrite/simple/collinfo.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/bulkWrite/simple/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/bulkWrite/simple/mongocryptd-reply.json")),
                      ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);

            // Match results.
            bson_t out_bson;
            ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
            mongocrypt_binary_t *pattern = TEST_FILE("./test/data/bulkWrite/simple/encrypted-payload-pattern.json");
            bson_t pattern_bson;
            ASSERT(_mongocrypt_binary_to_bson(pattern, &pattern_bson));
            _assert_match_bson(&out_bson, &pattern_bson);

            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite with remote schema when MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB is not supported.
    {
        mongocrypt_t *crypt = mongocrypt_new();

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_FAILS(
            mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/simple/cmd.json")),
            ctx,
            "Fetching remote collection information on separate databases is not supported. Try upgrading driver, or "
            "specify a local schemaMap or encryptedFieldsMap.");

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite to an unencrypted collection.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        // Opt-in to handling required state for fetching remote encryptedFields with `bulkWrite`.
        mongocrypt_setopt_use_need_mongo_collinfo_with_db_state(crypt);

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(
            mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/unencrypted/cmd.json")),
            ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB);
        {
            // Do not feed any response.
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/bulkWrite/unencrypted/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/bulkWrite/unencrypted/mongocryptd-reply.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);

            // `expect` excludes `encryptionInformation`.
            mongocrypt_binary_t *expect = TEST_FILE("./test/data/bulkWrite/unencrypted/payload.json");
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, out);

            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);

        // Test again to ensure the cached collinfo produces same result.
        ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(
            mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/unencrypted/cmd.json")),
            ctx);

        // MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB state is not entered. collinfo is loaded from cache.

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/bulkWrite/unencrypted/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);
            ASSERT_OK(
                mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/bulkWrite/unencrypted/mongocryptd-reply.json")),
                ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);

            // `expect` excludes `encryptionInformation`.
            mongocrypt_binary_t *expect = TEST_FILE("./test/data/bulkWrite/unencrypted/payload.json");
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, out);

            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite with bypassQueryAnalysis. Expect `encryptionInformation` is added, but query analysis is not
    // consulted.
    {
        mongocrypt_t *crypt = mongocrypt_new();

        mongocrypt_setopt_bypass_query_analysis(crypt);

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/bulkWrite/simple/encrypted-field-map.json")),
                  crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/simple/cmd.json")),
                  ctx);

        // Query analysis is not consulted. Immediately transitions to MONGOCRYPT_CTX_READY.

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);

            // `expect` excludes `encryptionInformation`.
            mongocrypt_binary_t *expect = TEST_FILE("./test/data/bulkWrite/bypassQueryAnalysis/payload.json");
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, out);
            mongocrypt_binary_destroy(out);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test a bulkWrite with CSFLE (not supported by server)
    {
        mongocrypt_t *crypt = mongocrypt_new();

        mongocrypt_setopt_kms_providers(
            crypt,
            TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek));

        // Associate a JSON schema to the collection to enable CSFLE.
        ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_BSON(BSON_STR({"db.test" : {}}))), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "admin", -1, TEST_FILE("./test/data/bulkWrite/simple/cmd.json")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_FILE("./test/data/bulkWrite/jsonSchema/cmd-to-mongocryptd.json"),
                                                cmd_to_mongocryptd);
            mongocrypt_binary_destroy(cmd_to_mongocryptd);

            // End the test here. At present, an error query analysis returns this error for `bulkWrite` with a
            // `jsonSchema`: `The bulkWrite command only supports Queryable Encryption`.
            // libmongocrypt deliberately does not error to enable possible future server support of CSFLE
            // with bulkWrite without libmongocrypt changes.
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    bson_free(local_kek);
}

// `_test_rangePreview_fails` tests that use of "rangePreview" errors.
static void _test_rangePreview_fails(_mongocrypt_tester_t *tester) {
    // local_kek is the KEK used to encrypt the keyMaterial in ./test/data/key-document-local.json
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));
    mongocrypt_binary_t *kms_providers =
        TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek);

    // Test setting 'rangePreview' as an explicit encryption algorithm results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "Algorithm 'rangePreview' is deprecated");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test setting 'rangePreview' as an explicit encryption queryType results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "Query type 'rangePreview' is deprecated");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test setting 'rangePreview' from encryptedFields results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                      crypt,
                      TEST_FILE("./test/data/fle2-insert-range/int32/encrypted-field-map.json")), // Uses 'rangePreview'
                  crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(
            mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-insert-range/int32/cmd.json")),
            ctx,
            "Cannot use field 'encrypted' with 'rangePreview' queries");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    bson_free(local_kek);
}

// `_test_prefixPreview_suffixPreview_fails` tests that use of "prefixPreview" or "suffixPreview" errors.
static void _test_prefixPreview_suffixPreview_fails(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/fle2-insert-text-search-preview/" suffix)

    // local_kek is the KEK used to encrypt the keyMaterial in ./test/data/key-document-local.json
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));
    mongocrypt_binary_t *kms_providers =
        TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek);

    // Test setting 'prefixPreview' as an explicit encryption queryType results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "Query type 'prefixPreview' is deprecated");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test setting 'suffixPreview' as an explicit encryption queryType results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "Query type 'suffixPreview' is deprecated");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test setting 'prefixPreview' from encryptedFields results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TF("encrypted-field-map-prefixPreview.json")),
                  crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")),
                     ctx,
                     "Cannot use field 'encrypted' with 'prefixPreview' queries");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test setting 'suffixPreview' from encryptedFields results in error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TF("encrypted-field-map-suffixPreview.json")),
                  crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(ctx, crypt);
        ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")),
                     ctx,
                     "Cannot use field 'encrypted' with 'suffixPreview' queries");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    bson_free(local_kek);
#undef TF
}

// `_test_textPreview_fails` tests that using "textPreview" algorithm errors.
static void _test_textPreview_fails(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_TEXTPREVIEW_DEPRECATED_STR, -1),
                 ctx,
                 "unsupported algorithm");
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

// `autoencryption_test` defines a test for the automatic encryption context.
typedef struct {
    const char *desc;
    _test_rng_data_source rng_data;
    mongocrypt_binary_t *cmd;
    mongocrypt_binary_t *encrypted_field_map;
    mongocrypt_binary_t *mongocryptd_reply;
    mongocrypt_binary_t *keys_to_feed[3]; // NULL terminated list.
    mongocrypt_binary_t *expect;
} autoencryption_test;

static void autoencryption_test_run(autoencryption_test *aet) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    TEST_PRINTF("  auto_encryption test: '%s' ... begin\n", aet->desc);

    // Reset global counter for the `payloadId` to produce deterministic payloads.
    extern void mc_reset_payloadId_for_testing(void);
    mc_reset_payloadId_for_testing();

    // Initialize mongocrypt_t.
    mongocrypt_t *crypt = mongocrypt_new();
    {
        mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);

        // Set "local" KMS provider.
        {
            // `localkey_data` is the KEK used to encrypt the keyMaterial in ./test/data/keys/
            char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
            mongocrypt_binary_t *localkey =
                mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
            ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, localkey), crypt);
            mongocrypt_binary_destroy(localkey);
        }

        if (aet->rng_data.buf.len > 0) {
            // Set deterministic random number generator.
            ASSERT_OK(mongocrypt_setopt_crypto_hooks(crypt,
                                                     _std_hook_native_crypto_aes_256_cbc_encrypt,
                                                     _std_hook_native_crypto_aes_256_cbc_decrypt,
                                                     _test_rng_source,
                                                     _std_hook_native_hmac_sha512,
                                                     _std_hook_native_hmac_sha256,
                                                     _error_hook_native_sha256,
                                                     &aet->rng_data /* ctx */),
                      crypt);
        }

        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, aet->encrypted_field_map), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
    }

    // Create the auto encryption context and run.
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, aet->cmd), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, aet->mongocryptd_reply), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        for (mongocrypt_binary_t **iter = aet->keys_to_feed; *iter != NULL; iter++) {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, *iter), ctx);
        }
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *got = mongocrypt_binary_new();

        bool ret = mongocrypt_ctx_finalize(ctx, got);
        ASSERT_OK(ret, ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(aet->expect, got);
        mongocrypt_binary_destroy(got);
    }

    TEST_PRINTF("  auto_encryption test: '%s' ... end\n", aet->desc);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_no_trimFactor(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    mongocrypt_binary_t *key123 = TEST_FILE("./test/data/keys/12345678123498761234123456789012-local-document.json");

    // Test insert.
    {
        autoencryption_test aet = {
            .desc = "missing trimFactor in mongocryptd reply for `insert` is OK",
            .cmd = TEST_FILE("test/data/no-trimFactor/insert/cmd.json"),
            .encrypted_field_map = TEST_FILE("test/data/no-trimFactor/insert/encrypted-field-map.json"),
            .mongocryptd_reply = TEST_FILE("test/data/no-trimFactor/insert/mongocryptd-reply.json"),
            .keys_to_feed = {key123},
            .expect = TEST_FILE("test/data/no-trimFactor/insert/encrypted-payload.json"),
        };

        // Set fixed random data for deterministic results.
        mongocrypt_binary_t *rng_data = TEST_BIN(1024);
        aet.rng_data = (_test_rng_data_source){.buf = {.data = rng_data->data, .len = rng_data->len}};

        autoencryption_test_run(&aet);
    }

    // Test find.
    {
        autoencryption_test aet = {
            .desc = "missing trimFactor in mongocryptd reply for `find` is OK",
            .cmd = TEST_FILE("test/data/no-trimFactor/find/cmd.json"),
            .encrypted_field_map = TEST_FILE("test/data/no-trimFactor/find/encrypted-field-map.json"),
            .mongocryptd_reply = TEST_FILE("test/data/no-trimFactor/find/mongocryptd-reply.json"),
            .keys_to_feed = {key123},
            .expect = TEST_FILE("test/data/no-trimFactor/find/encrypted-payload.json"),
        };

        // Set fixed random data for deterministic results.
        mongocrypt_binary_t *rng_data = TEST_BIN(1024);
        aet.rng_data = (_test_rng_data_source){.buf = {.data = rng_data->data, .len = rng_data->len}};

        autoencryption_test_run(&aet);
    }
}

// `lookup_payload_bson` looks up a payload from the BSON document `result` at path `path`.
// The BSON portion of the payload is parsed into `payload_bson`.
static void lookup_payload_bson(mongocrypt_binary_t *result, char *path, bson_t *payload_bson) {
    bson_t result_bson;
    ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));

    // Iterate to the path.
    bson_iter_t iter;
    ASSERT(bson_iter_init(&iter, &result_bson));
    if (!bson_iter_find_descendant(&iter, path, &iter)) {
        TEST_ERROR("Unable to find path '%s'. Got: %s", path, tmp_json(&result_bson));
    }

    _mongocrypt_buffer_t buf;
    ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    ASSERT_CMPINT((int)buf.subtype, ==, (int)BSON_SUBTYPE_ENCRYPTED);

    // Expect a payload to start with an identifier byte. Expect the remainder to be BSON.
    ASSERT_CMPUINT32(buf.len, >, 0);
    ASSERT(bson_init_static(payload_bson, buf.data + 1, buf.len - 1));
}

// Test that the crypto params added in SERVER-91889 are sent for "range" payloads.
static void _test_range_sends_cryptoParams(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    // Set up key data used for test.
    _mongocrypt_buffer_t key123_id;
    _mongocrypt_buffer_copy_from_hex(&key123_id, "12345678123498761234123456789012");
    mongocrypt_binary_t *key123 = TEST_FILE("./test/data/keys/12345678123498761234123456789012-local-document.json");
    // Use fixed random data for deterministic results.
    mongocrypt_binary_t *rng_data = TEST_BIN(1024);

    // Test explicit insert.
    {
        ee_testcase tc = {0};
        tc.desc = "'range' sends crypto params for insert";
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data->data, .len = rng_data->len}};
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.range_opts = TEST_BSON("{'min': 0, 'max': 1234567, 'sparsity': { '$numberLong': '3' }, 'trimFactor': 4}");
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.keys_to_feed[0] = key123;
        tc.expect = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-insert-int32/expected.json");
        ee_testcase_run(&tc);
        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(tc.expect, "v", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(BSON_STR({"sp" : 3, "tf" : 4, "mn" : 0, "mx" : 1234567, "pn" : {"$exists" : false}})));
        }
    }

    // Test explicit insert with defaults.
    {
        ee_testcase tc = {0};
        tc.desc = "'range' sends crypto params for insert with correct defaults";
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data->data, .len = rng_data->len}};
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        // Use defaults for `sparsity` (2), and `trimFactor` (6).
        tc.range_opts = TEST_BSON("{'min': 0, 'max': 1234567}");
        tc.msg = TEST_BSON("{'v': 123456}");
        tc.keys_to_feed[0] = key123;
        tc.expect = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-insert-int32-defaults/expected.json");
        ee_testcase_run(&tc);
        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(tc.expect, "v", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(BSON_STR({"sp" : 2, "tf" : 6, "mn" : 0, "mx" : 1234567, "pn" : {"$exists" : false}})));
        }
    }

    // Test explicit insert of double.
    {
        ee_testcase tc = {0};
        tc.desc = "'range' sends crypto params for insert for double";
        mongocrypt_binary_t *rng_data = TEST_BIN(1024);
        tc.rng_data = (_test_rng_data_source){.buf = {.data = rng_data->data, .len = rng_data->len}};
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.user_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.range_opts = TEST_BSON(
            "{'min': 0.0, 'max': 1234567.0, 'precision': 2, 'sparsity': { '$numberLong': '3' }, 'trimFactor': 4}");
        tc.msg = TEST_BSON("{'v': 123456.0}");
        tc.keys_to_feed[0] = key123;
        tc.expect = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-insert-double/expected.json");
        ee_testcase_run(&tc);
        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(tc.expect, "v", &payload_bson);
            _assert_match_bson(&payload_bson,
                               TMP_BSON(BSON_STR({"sp" : 3, "tf" : 4, "mn" : 0.0, "mx" : 1234567.0, "pn" : 2})));
        }
    }

    // Test explicit find.
    {
        ee_testcase tc = {0};
        tc.desc = "'range' sends crypto params for find with correct defaults";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.is_expression = true;
        tc.user_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.range_opts =
            TEST_BSON("{'min': 0, 'max': 1234567}"); // Use defaults for `sparsity` (2), and `trimFactor` (6).
        tc.msg = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-find-int32-defaults/to-encrypt.json");
        tc.keys_to_feed[0] = key123;
        tc.expect = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-find-int32-defaults/expected.json");
        ee_testcase_run(&tc);
        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(tc.expect, "v.$and.0.age.$gte", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(BSON_STR({"sp" : 2, "tf" : 6, "mn" : 0, "mx" : 1234567, "pn" : {"$exists" : false}})));
        }
    }

    // Test explicit find with defaults.
    {
        ee_testcase tc = {0};
        tc.desc = "'range' sends crypto params for find";
        tc.algorithm = MONGOCRYPT_ALGORITHM_RANGE_STR;
        tc.query_type = MONGOCRYPT_QUERY_TYPE_RANGE_STR;
        tc.is_expression = true;
        tc.user_key_id = &key123_id;
        tc.contention_factor = OPT_I64(1);
        tc.range_opts = TEST_BSON("{'min': 0, 'max': 1234567, 'sparsity': { '$numberLong': '3' }, 'trimFactor': 4}");
        tc.msg = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-find-int32/to-encrypt.json");
        tc.keys_to_feed[0] = key123;
        tc.expect = TEST_FILE("./test/data/range-sends-cryptoParams/explicit-find-int32/expected.json");
        ee_testcase_run(&tc);
        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(tc.expect, "v.$and.0.age.$gte", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(BSON_STR({"sp" : 3, "tf" : 4, "mn" : 0, "mx" : 1234567, "pn" : {"$exists" : false}})));
        }
    }

    // Test automatic insert of int32.
    {
        autoencryption_test aet = {
            .desc = "'range' sends crypto params for insert",
            .rng_data = {.buf = {.data = rng_data->data, .len = rng_data->len}},
            .cmd = TEST_FILE("./test/data/range-sends-cryptoParams/auto-insert-int32/cmd.json"),
            .encrypted_field_map =
                TEST_FILE("./test/data/range-sends-cryptoParams/auto-insert-int32/encrypted-field-map.json"),
            .mongocryptd_reply =
                TEST_FILE("./test/data/range-sends-cryptoParams/auto-insert-int32/mongocryptd-reply.json"),
            .keys_to_feed = {key123},
            .expect = TEST_FILE("./test/data/range-sends-cryptoParams/auto-insert-int32/encrypted-payload.json")};

        autoencryption_test_run(&aet);

        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(aet.expect, "documents.0.encrypted", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(
                    BSON_STR({"sp" : 2, "tf" : 6, "mn" : -2147483648, "mx" : 2147483647, "pn" : {"$exists" : false}})));
        }
    }

    // Test automatic find of int32.
    {
        autoencryption_test aet = {
            .desc = "'range' sends crypto params for find",
            .cmd = TEST_FILE("./test/data/range-sends-cryptoParams/auto-find-int32/cmd.json"),
            .encrypted_field_map =
                TEST_FILE("./test/data/range-sends-cryptoParams/auto-find-int32/encrypted-field-map.json"),
            .mongocryptd_reply =
                TEST_FILE("./test/data/range-sends-cryptoParams/auto-find-int32/mongocryptd-reply.json"),
            .keys_to_feed = {key123},
            .expect = TEST_FILE("./test/data/range-sends-cryptoParams/auto-find-int32/encrypted-payload.json")};

        autoencryption_test_run(&aet);

        // Check the parameters are present in the final payload.
        {
            bson_t payload_bson;
            lookup_payload_bson(aet.expect, "filter.$and.0.encrypted.$gte", &payload_bson);
            _assert_match_bson(
                &payload_bson,
                TMP_BSON(
                    BSON_STR({"sp" : 2, "tf" : 6, "mn" : -2147483648, "mx" : 2147483647, "pn" : {"$exists" : false}})));
        }
    }

    _mongocrypt_buffer_cleanup(&key123_id);
}

typedef struct _responses {
    const char *key_document;
    const char *oauth_response;
    const char *decrypt_response;
} _responses;

static void _test_encrypt_retry_provider(_responses r, _mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    // Create context.
    {
        // Use explicit encryption to simplify (no schema needed).
        ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "keyDocumentName"}))),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);
    }

    // Feed key.
    {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE(r.key_document)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    // Needs KMS for oauth token.
    {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kctx);
        // Expect KMS request is returned again for a retry.
        kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kctx, ctx);
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kctx), >, 0);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE(r.oauth_response)), kctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    // Needs KMS to decrypt DEK.
    {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kctx);
        // Expect KMS request is returned again for a retry.
        kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kctx, ctx);
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kctx), >, 0);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE(r.decrypt_response)), kctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    mongocrypt_binary_t *bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_encrypt_retry(_mongocrypt_tester_t *tester) {
    // Test that an HTTP error is retried with AWS
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kms_ctx);
        // Expect KMS request is returned again for a retry.
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-aws/decrypt-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    // Azure
    {
        _responses r = {
            "./test/data/key-document-azure.json",
            "./test/data/kms-azure/oauth-response.txt",
            "./test/data/kms-azure/decrypt-response.txt",
        };
        _test_encrypt_retry_provider(r, tester);
    }
    // GCP
    {
        _responses r = {
            "./test/data/key-document-gcp.json",
            "./test/data/kms-gcp/oauth-response.txt",
            "./test/data/kms-gcp/decrypt-response.txt",
        };
        _test_encrypt_retry_provider(r, tester);
    }
    // Multiple keys
    {
        // Create crypt with retry enabled.
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(
                      crypt,
                      TEST_BSON(BSON_STR({"aws" : {"accessKeyId" : "foo", "secretAccessKey" : "bar"}}))),
                  crypt);
        ASSERT_OK(mongocrypt_setopt_retry_kms(crypt, true), crypt);
        ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_FILE("./test/data/multikey/schema_map.json")), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);

        // Encrypt a command requiring two keys.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/multikey/command.json")), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/multikey/mongocryptd_reply.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/multikey/key-document-a.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/multikey/key-document-b.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Expect two keys are needed. Obtain both.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx1 = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx1);
        mongocrypt_kms_ctx_t *kctx2 = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx2);

        // Feed a successful response to the first.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx1, TEST_FILE("./test/data/kms-aws/decrypt-response.txt")), kctx1);

        // Feed a retryable error response to the second.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx2, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kctx2);

        // Expect the retried KMS context is returned again.
        mongocrypt_kms_ctx_t *kctx_retry = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx_retry);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kctx_retry, TEST_FILE("./test/data/kms-aws/decrypt-response.txt")),
                  kctx_retry);

        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    // Test retry does not occur if not enabled.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(
                      crypt,
                      TEST_BSON(BSON_STR({"aws" : {"accessKeyId" : "foo", "secretAccessKey" : "bar"}}))),
                  crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx); // Give a retryable HTTP error. Expect error due to retry disabled.
        ASSERT_FAILS(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")),
                     kms_ctx,
                     "Error in KMS response");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test network retry does not occur if not enabled.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(
                      crypt,
                      TEST_BSON(BSON_STR({"aws" : {"accessKeyId" : "foo", "secretAccessKey" : "bar"}}))),
                  crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx); // Give a retryable network error. Expect error due to retry disabled.
        ASSERT_FAILS(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx, "KMS request failed due to network error");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void capture_logs(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) {
    mc_array_t *log_msgs = ctx;
    char *message_copy = bson_strdup(message);
    _mc_array_append_val(log_msgs, message_copy);
}

// Regression test for: MONGOCRYPT-770
static void _test_does_not_warn_for_empty_local_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(
                  crypt,
                  TEST_BSON(BSON_STR({"aws" : {"accessKeyId" : "foo", "secretAccessKey" : "bar"}}))),
              crypt);

    mc_array_t log_msgs; // Array of char *;
    _mc_array_init(&log_msgs, sizeof(char *));
    ASSERT_OK(mongocrypt_setopt_log_handler(crypt, capture_logs, &log_msgs), crypt);

    // Configure a local schema for "db.coll":
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_BSON(BSON_STR({"db.coll" : {}}))), crypt);

    ASSERT_OK(mongocrypt_init(crypt), crypt);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_BSON(BSON_STR({"find" : "coll", "filter" : {}}))), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);

    // Feed mongocryptd reply indicating `schemaRequiresEncryption: false`.
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_BSON(BSON_STR({
                                            "hasEncryptionPlaceholders" : false,
                                            "schemaRequiresEncryption" : false,
                                            "result" : {"find" : "test", "filter" : {}},
                                            "ok" : {"$numberDouble" : "1.0"}
                                        }))),
              ctx);

    // Expect no warning (passing an empty local schema is a valid use-case).
    if (log_msgs.len > 0) {
        TEST_STDERR_PRINTF("Got unexpected log messages:\n");
        for (size_t i = 0; i < log_msgs.len; i++) {
            TEST_STDERR_PRINTF("> %s\n", _mc_array_index(&log_msgs, char *, i));
        }
        abort();
    }

    for (size_t i = 0; i < log_msgs.len; i++) {
        bson_free(_mc_array_index(&log_msgs, char *, i));
    }
    _mc_array_destroy(&log_msgs);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_fle2_encrypted_field_config_with_bad_str_encode_version(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                  crypt,
                  TEST_FILE("./test/data/fle2-bad-str-encode-version/bad-encrypted-field-config-map.json")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-insert-v2/cmd.json")),
                 ctx,
                 "'strEncodeVersion' of 99 is not supported");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_fle2_encrypted_field_config_with_duplicate_keyaltname(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(
                  crypt,
                  TEST_FILE("./test/data/fle2-duplicate-keyaltname/encrypted-field-config-map.json")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-insert-v2/cmd.json")),
                 ctx,
                 "duplicate keyAltName 'duplicateKey' found in encrypted field config");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_fle2_encrypted_fields_with_unmatching_str_encode_version(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();

    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt,
                                                           TEST_FILE("./test/data/fle2-create-encrypted-collection/"
                                                                     "encrypted-field-config-map.json")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(
                  ctx,
                  "db",
                  -1,
                  TEST_FILE("./test/data/fle2-create-encrypted-collection-with-str-encode-version/cmd.json")),
              ctx);

    expect_and_reply_to_ismaster(ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    {
        mongocrypt_binary_t *cmd_to_mongocryptd = mongocrypt_binary_new();

        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd_to_mongocryptd), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
            TEST_FILE("./test/data/fle2-bad-str-encode-version/bad-create-cmd-to-mongocryptd.json"),
            cmd_to_mongocryptd);
        mongocrypt_binary_destroy(cmd_to_mongocryptd);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(
                      ctx,
                      TEST_FILE("./test/data/fle2-bad-str-encode-version/bad-create-cmd-mongocryptd-reply.json")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_FAILS(mongocrypt_ctx_finalize(ctx, out),
                     ctx,
                     "'strEncodeVersion' of 1 does not match efc->str_encode_version of 0");
        mongocrypt_binary_destroy(out);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_fle2_collinfo_with_bad_str_encode_version(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TEST_FILE("./test/data/fle2-insert-v2/cmd.json")), ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/fle2-bad-str-encode-version/bad-collinfo.json")),
                 ctx,
                 "'strEncodeVersion' of 99 is not supported");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_lookup(_mongocrypt_tester_t *tester) {
    // Test $lookup works.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        expect_and_reply_to_ismaster(ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup errors if multiple-collection support is not opted-in.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        crypt->multiple_collinfo_enabled = false;
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")),
                     ctx,
                     "not configured to support encrypting a command with multiple collections");
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup errors if mongocryptd is too old.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TEST_BSON("{'isMaster': 1}"));
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/mongocryptd-ismaster-17.json")), ctx);
            ASSERT_FAILS(mongocrypt_ctx_mongo_done(ctx), ctx, "Upgrade mongocryptd");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test nested $lookup.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-nested/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        expect_and_reply_to_ismaster(ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2", "c3" ]}})));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup within $unionWith.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-unionWith/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        expect_and_reply_to_ismaster(ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2", "c3" ]}})));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup within $facet.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-facet/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        expect_and_reply_to_ismaster(ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup when one schema is in the schemaMap.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-schemaMap/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);
        ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TF("schemaMap.json")), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        expect_and_reply_to_ismaster(ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
            // Feed remote schema for "c1". "c2" is found in the schemaMap.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup with a self-lookup.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-self/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup when one schema is already cached.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

        // Do a self-lookup to add only "c1" to the cache.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-self/" suffix)
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
            {
                expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
                ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
                ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
            }
            mongocrypt_ctx_destroy(ctx);
        }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
        // Expect "c1" schema is not requested again.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            expect_and_reply_to_ismaster(ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
            {
                expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c2"})));
                // Feed remaining needed schema.
                ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
                ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

#undef TF

    // Test $lookup caches no collinfo results as empty schemas.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

        // Do a self-lookup to add only "c1" to the cache.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            expect_and_reply_to_ismaster(ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
            // Feed no collinfo results. Expect "c1" and "c2" to be cached as empty schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
            mongocrypt_ctx_destroy(ctx);
        }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
        // Expect "c1" schema is not requested again.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            expect_and_reply_to_ismaster(ctx);
            // Expect no more schemas are needed (both empty).
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }
#undef TF
// Test $lookup from a view.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-view/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "v1" ]}})));

            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-v1.json")), ctx, "cannot auto encrypt a view");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
// Test $lookup with feeding the same schema twice.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            // Feed schema for "c2" twice.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx, "unexpected duplicate");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with with feeding a non-matching schema.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-mismatch/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c3.json")), ctx, "got unexpected collinfo");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with only local schemas.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-only-schemaMap/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);
        ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TF("schemaMap.json")), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup from a collection that has no $jsonSchema configured.
#define TF(suffix) TEST_FILE("./test/data/lookup/csfle-sibling/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("reply-from-mongocryptd.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with QE.
#define TF(suffix) TEST_FILE("./test/data/lookup/qe/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            // Expect no `encryptionInformation` since no encryption payloads.
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with QE with an encrypted payload.
#define TF(suffix) TEST_FILE("./test/data/lookup/qe-with-payload/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            // Expect `encryptionInformation` since command has encryption payloads.
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with QE from encryptedFieldsMap.
#define TF(suffix) TEST_FILE("./test/data/lookup/qe-encryptedFieldsMap/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TF("encryptedFieldsMap.json")), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
            // Only feed collinfo for c1. c2 is included in encryptedFieldsMap.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with QE with self-lookup.
#define TF(suffix) TEST_FILE("./test/data/lookup/qe-self/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

    // Test $lookup with QE from from cache.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

        // Do a self-lookup to add only "c1" to the cache.
#define TF(suffix) TEST_FILE("./test/data/lookup/qe-self/" suffix)
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
            {
                expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c1"})));
                ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
                ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
            }
            mongocrypt_ctx_destroy(ctx);
        }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/qe-with-payload/" suffix)
        // Expect "c1" schema is not requested again.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

            ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
            expect_and_reply_to_ismaster(ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
            {
                expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : "c2"})));
                // Feed remaining needed schema.
                ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
                ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));

            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

#undef TF

// Test $lookup with mixed: QE + CSFLE
#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/qe/csfle/" suffix)
    // Schema mixing is supported >=8.2 (wire version >=27)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));

            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            // Stopping here since mongocryptd is expected to error with "Cannot specify both encryptionInformation and
            // csfleEncryptionSchemas unless csfleEncryptionSchemas only contains non-encryption JSON schema validators"
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Schema mixing is unsupported <8.2 (wire version <26)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster_26(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));

            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_FAILS(mongocrypt_ctx_mongo_op(ctx, got), ctx, "not supported");
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with mixed: QE + no-schema
#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/qe/no-schema/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with mixed: QE + QE
#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/qe/qe/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with mixed: QE + non-CSFLE remote JSON schema
#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/qe/non-csfle-schema/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

// Test $lookup with mixed: CSFLE + CSFLE.
#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/csfle/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/csfle/qe/" suffix)
    // Schema mixing is supported >=8.2 (wire version >=27)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            // Stopping here since mongocryptd is expected to error with "Cannot specify both encryptionInformation and
            // csfleEncryptionSchemas unless csfleEncryptionSchemas only contains non-encryption JSON schema validators"
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Schema mixing is unsupported <8.2 (wire version <26)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster_26(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_FAILS(mongocrypt_ctx_mongo_op(ctx, got), ctx, "not supported");
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/csfle/no-schema/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/no-schema/csfle/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/no-schema/no-schema/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF

#define TF(suffix) TEST_FILE("./test/data/lookup/mixed/no-schema/qe/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), ctx);
        expect_and_reply_to_ismaster(ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_COLLINFO);
        {
            expect_mongo_op(ctx, TEST_BSON(BSON_STR({"name" : {"$in" : [ "c1", "c2" ]}})));
            // Feed both needed schemas.
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c1.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TF("collInfo-c2.json")), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            expect_mongo_op(ctx, TF("cmd-to-mongocryptd.json"));
            mongocrypt_binary_t *to_feed = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *to_feed = TF("key-doc.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, to_feed), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *expect = TF("cmd-to-mongod.json");
            mongocrypt_binary_t *got = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, got), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(expect, got);
            mongocrypt_binary_destroy(got);
        }
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static bool _deterministic_contention(int64_t exclusive_upper_bound, int64_t *out) {
    ASSERT(out);
    (void)exclusive_upper_bound;
    *out = 1;
    return true;
}

static void _test_deterministic_contention(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *const status = mongocrypt_status_new();

    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);
    ASSERT_OK(mongocrypt_init(crypt), crypt);
    _mongocrypt_opts_set_contention_factor_fn(crypt, &_deterministic_contention); // register deterministic fn wth crypt

    // Expect the callback returns 1.
    {
        int64_t out;
        ASSERT(crypt->opts.contention_factor_fn(4, &out));
        ASSERT_CMPINT64(out, ==, 1);
    }

    // Start explicit encryption:
    mongocrypt_ctx_t *const ctx = mongocrypt_ctx_new(crypt);

    // Use the QE algorithm "Indexed", which uses a contention factor:
    ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);

    {
        _mongocrypt_buffer_t keyABC_id;
        _mongocrypt_buffer_copy_from_hex(&keyABC_id, "ABCDEFAB123498761234123456789012");
        ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, _mongocrypt_buffer_as_binary(&keyABC_id)), ctx);
        _mongocrypt_buffer_cleanup(&keyABC_id);
    }

    // Set max contention factor of 4:
    ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 4), ctx);

    // Encrypt the value 123:
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON("{'v': 123}")), ctx);

    // Expect key is needed:
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        mongocrypt_binary_t *const keyABC =
            TEST_FILE("./test/data/keys/ABCDEFAB123498761234123456789012-local-document.json");
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, keyABC), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    }

    // Expect ready to encrypt:
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    {
        mongocrypt_binary_t *got = mongocrypt_binary_new();
        bool ret = mongocrypt_ctx_finalize(ctx, got);
        ASSERT_OK(ret, ctx);

        // The result is represented in a BSON document: { "v":  }.
        // Do the long-winded conversion: binary -> BSON -> buffer:
        _mongocrypt_buffer_t got_buffer;
        {
            bson_t got_bson;
            ASSERT(_mongocrypt_binary_to_bson(got, &got_bson));
            bson_iter_t got_iter;
            ASSERT(bson_iter_init_find(&got_iter, &got_bson, "v"));
            ASSERT(_mongocrypt_buffer_from_binary_iter(&got_buffer, &got_iter));
        }

        // Check the contention factor in the resulting payload:
        mc_FLE2InsertUpdatePayloadV2_t got_payload;
        mc_FLE2InsertUpdatePayloadV2_init(&got_payload);
        ASSERT_OK_STATUS(mc_FLE2InsertUpdatePayloadV2_parse(&got_payload, &got_buffer, status), status);
        ASSERT_CMPINT64(got_payload.contentionFactor, ==, 1); // Set by _deterministic_contention.
        mc_FLE2InsertUpdatePayloadV2_cleanup(&got_payload);
        mongocrypt_binary_destroy(got);
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _test_qe_keyAltName(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : "bar"} ]}));

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
            // Command to mongocryptd contains keyId (not keyAltName)
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd);
            mongocrypt_binary_destroy(cmd);

            // Feed command with markings:
            mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }
        mongocrypt_ctx_destroy(ctx);

        // Encrypt again to test flow where key is cached.
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
            // Command to mongocryptd contains keyId (not keyAltName)
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd);
            mongocrypt_binary_destroy(cmd);

            // Feed command with markings:
            mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_cryptShared(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix)
    {
        if (!TEST_MONGOCRYPT_HAVE_REAL_CRYPT_SHARED_LIB) {
            TEST_STDERR_PRINTF("No 'real' csfle library is available. The %s test is a no-op.\n", BSON_FUNC);
            return;
        }
        mongocrypt_t *crypt =
            _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT | TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map);
        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : "bar"} ]}));

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }
        mongocrypt_ctx_destroy(ctx);

        // Encrypt again to test flow where key is cached.
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_create(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/create/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);
        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TF("cmd.json");

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        expect_and_reply_to_ismaster(ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
            // Command to mongocryptd contains keyId (not keyAltName)
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd);
            mongocrypt_binary_destroy(cmd);

            // Feed command with markings:
            mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongod.json"), result);
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_bypassQueryAnalysis(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map);

        // Bypass query analysis (should skip mongocryptd/crypt_shared):
        mongocrypt_setopt_bypass_query_analysis(crypt);

        ASSERT_OK(mongocrypt_init(crypt), crypt);

        // clang-format off
        mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({
            "insert" : "coll",
            "documents" : [ {
                "secret" : {
                    // Simulate explicit encryption by providing a payload in the input:
                    "$binary" : {
                        "base64" : "EGFhYWFhYWFhYWFhYWFhYWECkUA+gYv1gIVjuofdGj59/OkHB79F1ywPjR8aTMbCvVRfmJBNw2nh2/b0tTOhTZkNp/VUSyGycvUxcNFUyh1gmg==",
                        "subType" : "06"
                    }
                }
            } ]
        }));
        // clang-format on

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // Expect mongocryptd/crypt_shared to be bypassed
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_compact(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/compact/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map);

        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TF("cmd.json");

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // Expect mongocryptd/crypt_shared to be bypassed since compact skips it?
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongod.json"), result);
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_cleanup(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/cleanup/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map);

        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TF("cmd.json");

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // Expect mongocryptd/crypt_shared to be bypassed since cleanup skips it?
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongod.json"), result);
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

static void _test_qe_keyAltName_kms(_mongocrypt_tester_t *tester) {
#define TF(suffix) TEST_FILE("./test/data/qe_keyAltName/" suffix)
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_SKIP_INIT);

        // Specify a local encryptedFieldsMap with keyAltName:
        mongocrypt_binary_t *encrypted_fields_map = TEST_BSON_STR(BSON_STR({
            "db.coll" : {"fields" : [ {"path" : "secret", "bsonType" : "string", "keyAltName" : "keyDocumentName"} ]}
        }));
        ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, encrypted_fields_map), crypt);
        ASSERT_OK(mongocrypt_init(crypt), crypt);

        mongocrypt_binary_t *cmd = TEST_BSON_STR(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : "bar"} ]}));

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // Keys requested to translate the keyAltNames:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        {
            mongocrypt_binary_t *filter = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, filter), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(
                TEST_BSON_STR(
                    BSON_STR({"$or" : [ {"_id" : {"$in" : []}}, {"keyAltNames" : {"$in" : ["keyDocumentName"]}} ]})),
                filter);
            mongocrypt_binary_destroy(filter);

            // Feed requested key:
            mongocrypt_binary_t *key = TF("key-document-aws.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, key), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        // MONGOCRYPT_CTX_NEED_KMS is entered to to decrypt key:
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        {
            mongocrypt_kms_ctx_t *kms = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kms);
            _mongocrypt_tester_satisfy_kms(tester, kms);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
            // Command to mongocryptd contains keyId (not keyAltName)
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd);
            mongocrypt_binary_destroy(cmd);

            // Feed command with markings:
            mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }
        mongocrypt_ctx_destroy(ctx);

        // Encrypt again to test flow where key is cached.
        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "db", -1, cmd), ctx);
        // MONGOCRYPT_CTX_MARKINGS is entered to send command to mongocryptd.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
        {
            mongocrypt_binary_t *cmd = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, cmd), ctx);
            // Command to mongocryptd contains keyId (not keyAltName)
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TF("cmd-to-mongocryptd.json"), cmd);
            mongocrypt_binary_destroy(cmd);

            // Feed command with markings:
            mongocrypt_binary_t *reply = TF("reply-from-mongocryptd.json");
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, reply), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        {
            bson_t result_bson;
            mongocrypt_binary_t *result = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, result), ctx);
            ASSERT(_mongocrypt_binary_to_bson(result, &result_bson));
            _assert_match_bson(
                &result_bson,
                TMP_BSON(BSON_STR({"insert" : "coll", "documents" : [ {"secret" : {"$$type" : "binData"}} ]})));
            mongocrypt_binary_destroy(result);
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
#undef TF
}

void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_explicit_encrypt_init);
    INSTALL_TEST(_test_encrypt_init);
    INSTALL_TEST(_test_encrypt_need_collinfo);
    INSTALL_TEST(_test_encrypt_need_markings);
    INSTALL_TEST(_test_encrypt_csfle_no_needs_markings);
    INSTALL_TEST(_test_encrypt_need_keys);
    INSTALL_TEST(_test_encrypt_ready);
    INSTALL_TEST(_test_key_missing_region);
    INSTALL_TEST(_test_view);
    INSTALL_TEST(_test_local_schema);
    INSTALL_TEST(_test_encrypt_caches_collinfo);
    INSTALL_TEST(_test_encrypt_caches_keys);
    INSTALL_TEST(_test_encrypt_cache_expiration);
    INSTALL_TEST(_test_encrypt_caches_keys_by_alt_name);
    INSTALL_TEST(_test_encrypt_random);
    INSTALL_TEST(_test_encrypt_is_remote_schema);
    INSTALL_TEST(_test_encrypt_init_each_cmd);
    INSTALL_TEST(_test_encrypt_invalid_siblings);
    INSTALL_TEST(_test_encrypting_with_explicit_encryption);
    INSTALL_TEST(_test_explicit_encryption);
    INSTALL_TEST(_test_encrypt_empty_aws);
    INSTALL_TEST(_test_encrypt_custom_endpoint);
    INSTALL_TEST(_test_encrypt_with_aws_session_token);
    INSTALL_TEST(_test_encrypt_caches_empty_collinfo);
    INSTALL_TEST(_test_encrypt_caches_collinfo_without_jsonschema);
    INSTALL_TEST(_test_encrypt_per_ctx_credentials);
    INSTALL_TEST(_test_encrypt_per_ctx_credentials_given_empty);
    INSTALL_TEST(_test_encrypt_per_ctx_credentials_local);
    INSTALL_TEST(_test_encrypt_with_encrypted_field_config_map);
    INSTALL_TEST(_test_encrypt_with_encrypted_field_config_map_bypassed);
    INSTALL_TEST(_test_encrypt_no_schema);
    INSTALL_TEST(_test_encrypt_remote_encryptedfields);
    INSTALL_TEST(_test_encrypt_with_bypassqueryanalysis);
    INSTALL_TEST(_test_encrypt_fle2_insert_payload);
    INSTALL_TEST(_test_encrypt_fle2_insert_payload_with_str_encode_version);
    INSTALL_TEST(_test_encrypt_fle2_find_payload);
    INSTALL_TEST(_test_encrypt_fle2_unindexed_encrypted_payload);
    INSTALL_TEST(_test_encrypt_fle2_explicit);
    INSTALL_TEST(_test_encrypt_applies_default_state_collections);
    INSTALL_TEST(_test_encrypt_fle2_delete);
    INSTALL_TEST(_test_encrypt_fle2_omits_encryptionInformation);
    INSTALL_TEST(_test_encrypt_fle2_explain_with_mongocryptd);
    INSTALL_TEST(_test_encrypt_fle2_explain_with_csfle);
    INSTALL_TEST(_test_encrypt_fle1_explain_with_mongocryptd);
    INSTALL_TEST(_test_encrypt_fle1_explain_with_csfle);
    INSTALL_TEST(_test_dollardb_preserved);
    INSTALL_TEST(_test_dollardb_preserved_empty);
    INSTALL_TEST(_test_dollardb_omitted);
    INSTALL_TEST(_test_dollardb_preserved_fle1);
    INSTALL_TEST(_test_fle1_create_without_schema);
    INSTALL_TEST(_test_fle1_create_with_schema);
    INSTALL_TEST(_test_fle1_create_with_cmd_schema);
    INSTALL_TEST(_test_fle1_create_old_mongocryptd);
    INSTALL_TEST(_test_fle1_create_with_csfle);
    INSTALL_TEST(_test_fle2_create);
    INSTALL_TEST(_test_fle2_create_with_encrypted_fields);
    INSTALL_TEST(_test_fle2_create_with_encrypted_fields_and_str_encode_version);
    INSTALL_TEST(_test_fle2_create_with_encrypted_fields_unset_str_encode_version);
    INSTALL_TEST(_test_fle2_text_search_create_with_encrypted_fields);
    INSTALL_TEST(_test_fle2_text_search_create_with_encrypted_fields_and_str_encode_version);
    INSTALL_TEST(_test_fle2_text_search_create_with_encrypted_fields_unset_str_encode_version);
    INSTALL_TEST(_test_fle2_text_search_create_with_encrypted_fields_unmatching_str_encode_version);
    INSTALL_TEST(_test_fle2_create_does_not_cache_empty_schema);
    INSTALL_TEST(_test_fle2_create_bypass_query_analysis);
    INSTALL_TEST(_test_encrypt_macos_no_ctr);
    INSTALL_TEST(_test_fle1_collmod_with_jsonSchema);
    INSTALL_TEST(_test_fle1_collmod_without_jsonSchema);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_int32);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_int64);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_date);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_double);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_double_precision);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_decimal128);
    INSTALL_TEST(_test_encrypt_fle2_insert_range_payload_decimal128_precision);
#endif
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_int32);
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_int64);
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_date);
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_double);
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_double_precision);
#if MONGOCRYPT_HAVE_DECIMAL128_SUPPORT()
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_decimal128);
    INSTALL_TEST(_test_encrypt_fle2_find_range_payload_decimal128_precision);
#endif
    INSTALL_TEST(_test_encrypt_fle2_insert_text_search_payload);
    INSTALL_TEST(_test_encrypt_fle2_insert_text_search_payload_with_str_encode_version);
    INSTALL_TEST(_test_bulkWrite);
    INSTALL_TEST(_test_rangePreview_fails);
    INSTALL_TEST(_test_prefixPreview_suffixPreview_fails);
    INSTALL_TEST(_test_textPreview_fails);
    INSTALL_TEST(_test_no_trimFactor);
    INSTALL_TEST(_test_range_sends_cryptoParams);
    INSTALL_TEST(_test_encrypt_retry);
    INSTALL_TEST(_test_does_not_warn_for_empty_local_schema);
    INSTALL_TEST(_test_fle2_encrypted_field_config_with_bad_str_encode_version);
    INSTALL_TEST(_test_fle2_encrypted_field_config_with_duplicate_keyaltname);
    INSTALL_TEST(_test_fle2_encrypted_fields_with_unmatching_str_encode_version);
    INSTALL_TEST(_test_fle2_collinfo_with_bad_str_encode_version);
    INSTALL_TEST(_test_lookup);
    INSTALL_TEST(_test_deterministic_contention);
    INSTALL_TEST(_test_qe_keyAltName);
    INSTALL_TEST(_test_qe_keyAltName_cryptShared);
    INSTALL_TEST(_test_qe_keyAltName_create);
    INSTALL_TEST(_test_qe_keyAltName_bypassQueryAnalysis);
    INSTALL_TEST(_test_qe_keyAltName_compact);
    INSTALL_TEST(_test_qe_keyAltName_cleanup);
    INSTALL_TEST(_test_qe_keyAltName_kms);
}
libmongocrypt-1.19.0/test/test-mongocrypt-ctx-rewrap-many-datakey.c000066400000000000000000001314001521103432300254400ustar00rootroot00000000000000/*
 * Copyright 2022-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-kms-ctx-private.h"
#include "test-mongocrypt.h"

#define TEST_REWRAP_MASTER_KEY_ID_OLD                                                                                  \
    "arn:aws:kms:us-east-1:579766882180:key/"                                                                          \
    "89fcc2c4-08b0-4bd9-9f25-e30687b580d0"

#define TEST_REWRAP_MASTER_KEY_ID_NEW                                                                                  \
    "arn:aws:kms:us-east-1:579766882180:key/"                                                                          \
    "061334ae-07a8-4ceb-a813-8135540e837d"

typedef struct {
    _mongocrypt_buffer_t id;
    const char *kek_id;
    _mongocrypt_buffer_t key_material;
    int64_t creation_date;
    int64_t update_date;
    _mongocrypt_key_alt_name_t *key_alt_names;
} _test_datakey_fields_t;

static _test_datakey_fields_t *_test_datakey_fields_new(void) {
    return bson_malloc0(sizeof(_test_datakey_fields_t));
}

static void _test_datakey_fields_destroy(_test_datakey_fields_t *fields) {
    if (!fields) {
        return;
    }

    _mongocrypt_key_alt_name_destroy_all(fields->key_alt_names);

    bson_free(fields);
}

static _mongocrypt_buffer_t _find_key_id(mongocrypt_binary_t *key) {
    bson_t bson;
    bson_iter_t iter;
    _mongocrypt_buffer_t buf;

    BSON_ASSERT_PARAM(key);

    ASSERT(_mongocrypt_binary_to_bson(key, &bson));
    ASSERT(bson_iter_init(&iter, &bson));
    ASSERT(bson_iter_find_descendant(&iter, "_id", &iter));
    ASSERT(BSON_ITER_HOLDS_BINARY(&iter));
    ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    ASSERT(buf.subtype == BSON_SUBTYPE_UUID);
    ASSERT(buf.len > 0u);

    return buf;
}

static _mongocrypt_buffer_t _find_key_id_from_iter(bson_iter_t *iter) {
    _mongocrypt_buffer_t buf;

    ASSERT(BSON_ITER_HOLDS_BINARY(iter));
    ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, iter));
    ASSERT(buf.subtype == BSON_SUBTYPE_UUID);
    ASSERT(buf.len > 0u);

    return buf;
}

static const char *_find_masterkey_id(mongocrypt_binary_t *key) {
    bson_t bson;
    bson_iter_t iter;
    const char *res;

    BSON_ASSERT_PARAM(key);

    ASSERT(_mongocrypt_binary_to_bson(key, &bson));
    ASSERT(bson_iter_init(&iter, &bson));
    ASSERT(bson_iter_find_descendant(&iter, "masterKey.key", &iter));
    ASSERT((res = bson_iter_utf8(&iter, NULL)));

    return res;
}

static _mongocrypt_buffer_t _find_key_material(mongocrypt_binary_t *key) {
    bson_t bson;
    bson_iter_t iter;
    _mongocrypt_buffer_t buf;

    BSON_ASSERT_PARAM(key);

    ASSERT(_mongocrypt_binary_to_bson(key, &bson));
    ASSERT(bson_iter_init(&iter, &bson));
    ASSERT(bson_iter_find_descendant(&iter, "keyMaterial", &iter));
    ASSERT(BSON_ITER_HOLDS_BINARY(&iter));
    ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    ASSERT(buf.subtype == BSON_SUBTYPE_BINARY);
    ASSERT(buf.len > 0u);

    return buf;
}

static _mongocrypt_buffer_t _key_material_from_iter(bson_iter_t *iter) {
    _mongocrypt_buffer_t buf;

    ASSERT(BSON_ITER_HOLDS_BINARY(iter));
    ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, iter));
    ASSERT(buf.subtype == BSON_SUBTYPE_BINARY);
    ASSERT(buf.len > 0u);

    return buf;
}

static bool _buffer_cmp_equal(const _mongocrypt_buffer_t *lhs, const _mongocrypt_buffer_t *rhs) {
    BSON_ASSERT_PARAM(lhs);
    BSON_ASSERT_PARAM(rhs);

    return lhs->len == rhs->len && memcmp(lhs->data, rhs->data, lhs->len) == 0;
}

static int64_t _find_date_field(mongocrypt_binary_t *key, const char *dotkey) {
    bson_t bson;
    bson_iter_t iter;
    int64_t res;

    BSON_ASSERT_PARAM(key);

    ASSERT(_mongocrypt_binary_to_bson(key, &bson));
    ASSERT(bson_iter_init(&iter, &bson));
    ASSERT(bson_iter_find_descendant(&iter, dotkey, &iter));
    ASSERT(BSON_ITER_HOLDS_DATE_TIME(&iter));
    ASSERT((res = bson_iter_date_time(&iter)) != 0);

    return res;
}

static int64_t _find_creation_date(mongocrypt_binary_t *key) {
    return _find_date_field(key, "creationDate");
}

static int64_t _find_update_date(mongocrypt_binary_t *key) {
    return _find_date_field(key, "updateDate");
}

static _mongocrypt_key_alt_name_t *_find_key_alt_names(mongocrypt_binary_t *key) {
    bson_t bson;
    bson_iter_t iter;
    _mongocrypt_key_alt_name_t *res;

    BSON_ASSERT_PARAM(key);

    ASSERT(_mongocrypt_binary_to_bson(key, &bson));
    ASSERT(bson_iter_init(&iter, &bson));
    ASSERT(bson_iter_find_descendant(&iter, "keyAltNames", &iter));
    ASSERT(_mongocrypt_key_alt_name_from_iter(&iter, &res, NULL));
    ASSERT(res);

    return res;
}

static _test_datakey_fields_t *_find_datakey_fields(mongocrypt_binary_t *key) {
    _test_datakey_fields_t *res = _test_datakey_fields_new();

    BSON_ASSERT_PARAM(key);

    res->id = _find_key_id(key);
    res->kek_id = _find_masterkey_id(key);
    res->creation_date = _find_creation_date(key);
    res->update_date = _find_update_date(key);
    res->key_material = _find_key_material(key);
    res->key_alt_names = _find_key_alt_names(key);

    return res;
}

static void _assert_aws_kms_request(mongocrypt_kms_ctx_t *kms) {
    BSON_ASSERT_PARAM(kms);

    ASSERT_STREQUAL(mongocrypt_kms_ctx_get_kms_provider(kms, NULL), "aws");

    {
        mongocrypt_binary_t bin;
        ASSERT(mongocrypt_kms_ctx_message(kms, &bin));
        ASSERT(bin.len > 0);
    }

    {
        const char *endpoint;
        ASSERT(mongocrypt_kms_ctx_endpoint(kms, &endpoint));
        ASSERT(endpoint);
    }

    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) > 0);
}

static void _assert_aws_kms_endpoint(mongocrypt_kms_ctx_t *kms, const char *expected) {
    const char *endpoint = NULL;
    ASSERT((mongocrypt_kms_ctx_endpoint(kms, &endpoint)));
    ASSERT_STREQUAL(expected, endpoint);
}

static void _test_rewrap_many_datakey_init(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;

    /* No context, nothing to init. */
    ASSERT(!mongocrypt_ctx_rewrap_many_datakey_init(NULL, NULL));

    /* Filter argument required. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_FAILS(mongocrypt_ctx_rewrap_many_datakey_init(ctx, NULL), ctx, "filter must not be null");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* Irrelevant options should trigger initialization error. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'test'}")), ctx);
    ASSERT_FAILS(mongocrypt_ctx_rewrap_many_datakey_init(ctx, TEST_BSON("{}")), ctx, "key id and alt name prohibited");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* rewrapManyDataKeyOpts.newProvider and rewrapManyDataKeyOpts.newMasterKey
     * should be provided via mongocrypt_ctx_setopt_key_encryption_key. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                       TEST_BSON("{'provider': 'aws',"
                                                                 " 'region': 'us-east-1',"
                                                                 " 'key': '" TEST_REWRAP_MASTER_KEY_ID_NEW "'}")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, TEST_BSON("{}")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);

    /* Not providing rewrapManyDataKeyOpts is OK. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, TEST_BSON("{}")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_need_mongo_keys(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *const filter = TEST_BSON("{'keyAltName': {'$in': ['keyDocumentA', 'keyDocumentB']}}");

    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;

    /* Filter should be the same as what was provided in call to init. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    {
        mongocrypt_binary_t *const op = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_mongo_op(ctx, op), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(filter, op);
        mongocrypt_binary_destroy(op);
    }
    mongocrypt_ctx_destroy(ctx);

    /* No key documents is OK, no work to be done. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);
    mongocrypt_ctx_destroy(ctx);

    /* Any number of key documents can be given. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    mongocrypt_ctx_destroy(ctx);

    /* Key documents must not have duplicate key ID or alt names. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-with-alt-name.json")), ctx);
    ASSERT_FAILS(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-with-alt-name-duplicate-id.json")),
                 ctx,
                 "keys returned have duplicate keyAltNames or _id");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_need_kms_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *const filter = TEST_BSON("{'keyAltName': {'$in': ['keyDocumentA', 'keyDocumentB']}}");

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_kms_ctx_t *kms = NULL;

    /* AWS */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-full.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("aws", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Azure */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-azure.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("azure", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* GCP */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-gcp.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("gcp", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* KMIP */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-kmip.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("kmip", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Local: no KMS required. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Number of KMS requests should match number of keys that require it. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("aws", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("aws", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Ensure keys that don't require KMS do not request it. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("aws", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_STREQUAL("aws", mongocrypt_kms_ctx_get_kms_provider(kms, NULL));
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Ensure number of KMS requests matches number of keys that require it. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    /* Implementation detail: decryption KMS requests are issued in reverse order
     * of provided key documents. */
    _assert_aws_kms_endpoint(kms, "example.com:443");
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "kms.us-east-1.amazonaws.com:443");
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Ensure all KMS requests have a corresponding KMS response. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) > 0); /* "Oops." */
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_FAILS(mongocrypt_ctx_kms_done(ctx), ctx, "KMS response unfinished");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Skip KMS for keys with cached decrypted key material. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    /* Cache decrypted key material for datakey B. */
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    /* Only datakey A should make a KMS request. */
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "kms.us-east-1.amazonaws.com:443");
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_need_kms_retry(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *const filter = TEST_BSON("{'keyAltName': {'$in': ['keyDocumentA', 'keyDocumentB']}}");
    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_kms_ctx_t *kms = NULL;

    /* Ensure KMS decrypt requests retry for HTTP errors */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS); // To decrypt.
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    for (int i = 0; i < kms_max_attempts; i++) {
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kms);
        ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
        ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
        ASSERT(mongocrypt_kms_ctx_usleep(kms) > 0);
    }
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS); // To encrypt.
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Ensure KMS decrypt requests retried for HTTP errors fail after max attempts */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    for (int i = 0; i <= kms_max_attempts; i++) {
        if (i == kms_max_attempts) {
            ASSERT_FAILS(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")),
                         kms,
                         "retries");
            break;
        }
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kms);
        ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
        ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
        ASSERT(mongocrypt_kms_ctx_usleep(kms) > 0);
    }
    ASSERT_FAILS(mongocrypt_ctx_kms_done(ctx), ctx, "retries");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Ensure KMS decrypt requests retry for network errors */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT(mongocrypt_kms_ctx_fail(kms)); //  Simulate driver-side network failure
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT(mongocrypt_kms_ctx_usleep(kms) > 0);
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS); // To encrypt.
    mongocrypt_ctx_destroy(ctx);

    /* Clear key cache. */
    mongocrypt_destroy(crypt);
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Ensure KMS encrypt requests retry for network errors */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS); // To decrypt.
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS); // To encrypt.
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT(mongocrypt_kms_ctx_fail(kms));             // Simulate driver-side network failure for an encrypt request.
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx))); // Assert fails. Expected KMS request to retry but did not.
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_need_kms_encrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *const filter = TEST_BSON("{'keyAltName': {'$in': ['keyDocumentA', 'keyDocumentB']}}");

    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_kms_ctx_t *kms = NULL;

    /* If no new provider is given, encryption should reuse current KMS provider
     * for each key. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    /* These decrypt replies should cache key material used by later blocks. */
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    /* Implementation detail: encryption KMS requests are issued in same order as
     * provided key documents. */
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "kms.us-east-1.amazonaws.com:443");
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "example.com:443");
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    /* If new provider is given, encryption should use new KMS provider for all
     * keys. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                       TEST_BSON("{'provider': 'aws',"
                                                                 " 'region': 'us-east-2',"
                                                                 " 'key': '" TEST_REWRAP_MASTER_KEY_ID_NEW "'}")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    /* Skip decryption, key material should have been cached. */
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "kms.us-east-2.amazonaws.com:443");
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_endpoint(kms, "kms.us-east-2.amazonaws.com:443");
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    mongocrypt_ctx_destroy(ctx);

    /* If no encryption KMS required, should skip straight to READY state. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON("{'provider': 'local'}")), ctx);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    /* Skip decryption, key material should have been cached. */
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_destroy(ctx);

    /* Ensure all KMS requests have a corresponding KMS response. */
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                       TEST_BSON("{'provider': 'aws',"
                                                                 " 'region': 'us-east-1',"
                                                                 " 'key': '" TEST_REWRAP_MASTER_KEY_ID_NEW "'}")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    /* Skip decryption, key material should have been cached. */
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-a.txt")), kms);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) > 0); /* "Oops." */
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_FAILS(mongocrypt_ctx_kms_done(ctx), ctx, "KMS response unfinished");
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_ERROR);
    mongocrypt_ctx_destroy(ctx);

    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_finalize(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *const ctx = mongocrypt_ctx_new(crypt);

    mongocrypt_binary_t *const filter = TEST_BSON("{'keyAltName': {'$in': ['keyDocumentA', 'keyDocumentB']}}");

    mongocrypt_binary_t *const key_doc_a = TEST_FILE("./test/data/rmd/key-document-a.json");
    mongocrypt_binary_t *const key_doc_b = TEST_FILE("./test/data/rmd/key-document-b.json");

    /* Save current key fields for comparison with rewrapped keys. */
    _test_datakey_fields_t *const fields_a = _find_datakey_fields(key_doc_a);
    _test_datakey_fields_t *const fields_b = _find_datakey_fields(key_doc_b);

    mongocrypt_kms_ctx_t *kms = NULL;

    ASSERT_OK(ctx, crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                       TEST_BSON("{'provider': 'aws',"
                                                                 " 'region': 'us-east-1',"
                                                                 " 'key': '" TEST_REWRAP_MASTER_KEY_ID_NEW "'}")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-b.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_request(kms);
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);

    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_request(kms);
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_request(kms);
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-a.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);

    ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
    _assert_aws_kms_request(kms);
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-b.txt")), kms);
    ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);
    ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

    {
        mongocrypt_binary_t res;
        bson_t bson;
        bson_iter_t iter;
        bson_iter_t a_iter;
        bson_iter_t b_iter;

        ASSERT_OK(mongocrypt_ctx_finalize(ctx, &res), ctx);
        ASSERT(_mongocrypt_binary_to_bson(&res, &bson));

        /* There should be exactly 2 documents. */
        ASSERT(bson_iter_init(&iter, &bson));
        ASSERT(bson_iter_find_descendant(&iter, "v.1", &iter));
        ASSERT(!bson_iter_find_descendant(&iter, "v.2", &iter));

        /* Both keys should have the same ID as prior to rewrap, but may be
         * returned in a different order from order they were fed. */
        {
            _mongocrypt_buffer_t id;

            ASSERT(bson_iter_init(&a_iter, &bson));
            ASSERT(bson_iter_init(&b_iter, &bson));

            /* Find first keyDocument. */
            ASSERT(bson_iter_init(&iter, &bson));
            ASSERT(bson_iter_find_descendant(&iter, "v.0._id", &iter));
            id = _find_key_id_from_iter(&iter);
            if (_buffer_cmp_equal(&fields_a->id, &id)) {
                ASSERT(bson_iter_init(&iter, &bson));
                ASSERT(bson_iter_find_descendant(&iter, "v.0", &iter));
                ASSERT(bson_iter_recurse(&iter, &a_iter));
            } else if (_buffer_cmp_equal(&fields_b->id, &id)) {
                ASSERT(bson_iter_init(&iter, &bson));
                ASSERT(bson_iter_find_descendant(&iter, "v.0", &iter));
                ASSERT(bson_iter_recurse(&iter, &b_iter));
            }

            /* Find second keyDocument. */
            ASSERT(bson_iter_init(&iter, &bson));
            ASSERT(bson_iter_find_descendant(&iter, "v.1._id", &iter));
            id = _find_key_id_from_iter(&iter);
            if (_buffer_cmp_equal(&fields_a->id, &id)) {
                ASSERT(bson_iter_init(&iter, &bson));
                ASSERT(bson_iter_find_descendant(&iter, "v.1", &iter));
                ASSERT(bson_iter_recurse(&iter, &a_iter));
            } else if (_buffer_cmp_equal(&fields_b->id, &id)) {
                ASSERT(bson_iter_init(&iter, &bson));
                ASSERT(bson_iter_find_descendant(&iter, "v.1", &iter));
                ASSERT(bson_iter_recurse(&iter, &b_iter));
            }

            ASSERT(bson_iter_init(&iter, &bson));
            ASSERT(iter.raw != a_iter.raw || iter.off != a_iter.off);
            ASSERT(iter.raw != b_iter.raw || iter.off != b_iter.off);
        }

        /* Both keys should be rewrapped with new masterKey. */
        iter = a_iter;
        ASSERT(bson_iter_find_descendant(&iter, "masterKey.key", &iter));
        ASSERT_STREQUAL(TEST_REWRAP_MASTER_KEY_ID_NEW, bson_iter_utf8(&iter, NULL));
        iter = b_iter;
        ASSERT(bson_iter_find_descendant(&iter, "masterKey.key", &iter));
        ASSERT_STREQUAL(TEST_REWRAP_MASTER_KEY_ID_NEW, bson_iter_utf8(&iter, NULL));

        /* Both keys should have new key material. */
        {
            _mongocrypt_buffer_t key_material_a;
            _mongocrypt_buffer_t key_material_b;

            iter = a_iter;
            ASSERT(bson_iter_find_descendant(&iter, "keyMaterial", &iter));
            key_material_a = _key_material_from_iter(&iter);
            ASSERT(!_buffer_cmp_equal(&fields_a->key_material, &key_material_a));

            iter = b_iter;
            ASSERT(bson_iter_find_descendant(&iter, "keyMaterial", &iter));
            key_material_b = _key_material_from_iter(&iter);
            ASSERT(!_buffer_cmp_equal(&fields_b->key_material, &key_material_b));

            /* Key materials should differ. */
            ASSERT(!_buffer_cmp_equal(&key_material_a, &key_material_b));
        }

        bson_destroy(&bson);
    }

    /* No more work to be done for RewrapManyDatakey. */
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

    _test_datakey_fields_destroy(fields_b);
    _test_datakey_fields_destroy(fields_a);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_rewrap_many_datakey_kms_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt = NULL;
    mongocrypt_ctx_t *ctx = NULL;

    /* Ensure rewrapManyDataKey correctly handles need KMS credentials option. */
    {
        crypt = mongocrypt_new();
        mongocrypt_setopt_use_need_kms_credentials_state(crypt);
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}")), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        ctx = mongocrypt_ctx_new(crypt);

        ASSERT_OK(ctx, crypt);

        ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, TEST_BSON("{}")), ctx);

        /* NEED_KMS_CREDENTIALS comes before NEED_MONGO_KEYS. */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
        ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                       TEST_BSON("{'aws': {"
                                                                 "   'accessKeyId': 'example',"
                                                                 "   'secretAccessKey': 'example'}}")),
                  ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/rmd/key-document-a.json")), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        {
            mongocrypt_kms_ctx_t *kms = NULL;

            ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
            _assert_aws_kms_request(kms);
            ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-decrypt-reply-a.txt")), kms);
            ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);

            ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        /* KMS credentials provided before decryption should be reused here. */
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        {
            mongocrypt_kms_ctx_t *kms = NULL;

            ASSERT((kms = mongocrypt_ctx_next_kms_ctx(ctx)));
            _assert_aws_kms_request(kms);
            ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/rmd/kms-encrypt-reply-a.txt")), kms);
            ASSERT(mongocrypt_kms_ctx_bytes_needed(kms) == 0);

            ASSERT_OK(!mongocrypt_ctx_next_kms_ctx(ctx), ctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);

        {
            mongocrypt_binary_t res;
            bson_t bson;
            bson_iter_t iter;

            ASSERT_OK(mongocrypt_ctx_finalize(ctx, &res), ctx);
            ASSERT(_mongocrypt_binary_to_bson(&res, &bson));
            ASSERT(bson_iter_init(&iter, &bson));
            ASSERT(bson_iter_find_descendant(&iter, "v.0.masterKey.key", &iter));
            ASSERT_STREQUAL(TEST_REWRAP_MASTER_KEY_ID_OLD, bson_iter_utf8(&iter, NULL));
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_DONE);

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    /* Should not enter NEED_KMS_CREDENTIALS state if credentials already
     * provided. */
    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt,
                                              TEST_BSON("{'aws': {"
                                                        "   'accessKeyId': 'example',"
                                                        "   'secretAccessKey': 'example'}}")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(ctx, crypt);
    ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, TEST_BSON("{}")), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_ctx_rewrap_many_datakey(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_rewrap_many_datakey_init);
    INSTALL_TEST(_test_rewrap_many_datakey_need_mongo_keys);
    INSTALL_TEST(_test_rewrap_many_datakey_need_kms_decrypt);
    INSTALL_TEST(_test_rewrap_many_datakey_need_kms_retry);
    INSTALL_TEST(_test_rewrap_many_datakey_need_kms_encrypt);
    INSTALL_TEST(_test_rewrap_many_datakey_finalize);
    INSTALL_TEST(_test_rewrap_many_datakey_kms_credentials);
}
libmongocrypt-1.19.0/test/test-mongocrypt-ctx-setopt.c000066400000000000000000001504171521103432300231050ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Test option preconditions of all context functions. */

#include 

#include "mc-range-encoding-private.h"
#include "mongocrypt-binary-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt.h"

#define RAW_STRING(...) #__VA_ARGS__

/* An orphaned UTF-8 continuation byte (10xxxxxx) is malformed UTF-8. */
static char invalid_utf8[] = {(char)0x80, (char)0x00};

/* Convenience macros for setting options */
#define ASSERT_MASTERKEY_AWS_OK(region, region_len, cmk, cmk_len)                                                      \
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, region, region_len, cmk, cmk_len), ctx)
#define ASSERT_MASTERKEY_AWS_FAILS(region, region_len, cmk, cmk_len, msg)                                              \
    ASSERT_FAILS(mongocrypt_ctx_setopt_masterkey_aws(ctx, region, region_len, cmk, cmk_len), ctx, msg)

#define ASSERT_MASTERKEY_LOCAL_OK ASSERT_OK(mongocrypt_ctx_setopt_masterkey_local(ctx), ctx)
#define ASSERT_MASTERKEY_LOCAL_FAILS(msg) ASSERT_FAILS(mongocrypt_ctx_setopt_masterkey_local(ctx), ctx, msg)

#define ASSERT_KEY_ENCRYPTION_KEY_OK(bin) ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, bin), ctx)
#define ASSERT_KEY_ENCRYPTION_KEY_FAILS(bin, msg)                                                                      \
    ASSERT_FAILS(mongocrypt_ctx_setopt_key_encryption_key(ctx, bin), ctx, msg)

#define ASSERT_KEY_ID_OK(key_id) ASSERT_OK(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx)
#define ASSERT_KEY_ID_FAILS(key_id, msg) ASSERT_FAILS(mongocrypt_ctx_setopt_key_id(ctx, key_id), ctx, msg)

#define ASSERT_KEY_ALT_NAME_OK(key_alt_name) ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, key_alt_name), ctx)
#define ASSERT_KEY_ALT_NAME_FAILS(key_alt_name, msg)                                                                   \
    ASSERT_FAILS(mongocrypt_ctx_setopt_key_alt_name(ctx, key_alt_name), ctx, msg)

#define ASSERT_KEY_MATERIAL_OK(key_material) ASSERT_OK(mongocrypt_ctx_setopt_key_material(ctx, key_material), ctx)
#define ASSERT_KEY_MATERIAL_FAILS(key_material, msg)                                                                   \
    ASSERT_FAILS(mongocrypt_ctx_setopt_key_material(ctx, key_material), ctx, msg)

#define ASSERT_ALGORITHM_OK(algo, algo_len) ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, algo, algo_len), ctx)
#define ASSERT_ALGORITHM_FAILS(algo, algo_len, msg)                                                                    \
    ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, algo, algo_len), ctx, msg)

#define ASSERT_QUERY_TYPE_OK(qt, qt_len) ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, qt, qt_len), ctx)
#define ASSERT_QUERY_TYPE_FAILS(qt, qt_len, msg)                                                                       \
    ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, qt, qt_len), ctx, msg)

#define ASSERT_ENDPOINT_OK(endpoint, endpoint_len)                                                                     \
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws_endpoint(ctx, endpoint, endpoint_len), ctx)
#define ASSERT_ENDPOINT_FAILS(endpoint, endpoint_len, msg)                                                             \
    ASSERT_FAILS(mongocrypt_ctx_setopt_masterkey_aws_endpoint(ctx, endpoint, endpoint_len), ctx, msg)

#define ASSERT_DATAKEY_INIT_OK ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx)
#define ASSERT_DATAKEY_INIT_FAILS(msg) ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx), ctx, msg)

#define ASSERT_ENCRYPT_INIT_OK(db, db_len, cmd) ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, db, db_len, cmd), ctx)
#define ENCRYPT_INIT_FAILS(db, db_len, cmd, msg)                                                                       \
    ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, db, db_len, cmd), ctx, msg)

#define ASSERT_EX_ENCRYPT_INIT_OK(bin) ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, bin), ctx)
#define ASSERT_EX_ENCRYPT_INIT_FAILS(bin, msg) ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_init(ctx, bin), ctx, msg)

#define ASSERT_EX_ENCRYPT_EXPRESSION_INIT_OK(bin)                                                                      \
    ASSERT_OK(mongocrypt_ctx_explicit_encrypt_expression_init(ctx, bin), ctx)
#define ASSERT_EX_ENCRYPT_EXPRESSION_INIT_FAILS(bin, msg)                                                              \
    ASSERT_FAILS(mongocrypt_ctx_explicit_encrypt_expression_init(ctx, bin), ctx, msg)

#define ASSERT_DECRYPT_INIT_OK(bin) ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, bin), ctx)
#define ASSERT_DECRYPT_INIT_FAILS(bin, msg) ASSERT_FAILS(mongocrypt_ctx_decrypt_init(ctx, bin), ctx, msg)

#define ASSERT_EX_DECRYPT_INIT_OK(bin) ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, bin), ctx)
#define ASSERT_EX_DECRYPT_INIT_FAILS(bin, msg) ASSERT_FAILS(mongocrypt_ctx_explicit_decrypt_init(ctx, bin), ctx, msg)

#define REFRESH                                                                                                        \
    do {                                                                                                               \
        mongocrypt_destroy(crypt);                                                                                     \
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);                                              \
        REFRESH_CTX;                                                                                                   \
    } while (0)

#define REFRESH_CTX                                                                                                    \
    do {                                                                                                               \
        mongocrypt_ctx_destroy(ctx);                                                                                   \
        ctx = mongocrypt_ctx_new(crypt);                                                                               \
    } while (0)

#define DET MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR
#define RAND MONGOCRYPT_ALGORITHM_RANDOM_STR

/* Test valid and invalid options */
static void _test_setopt_masterkey_aws(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS(NULL, 0, "cmk", 3, "invalid region");
    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS("region", 6, NULL, 0, "invalid cmk");
    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS("region", 0, "cmk", 0, "invalid region");
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS("region", -2, "cmk", -1, "invalid region");
    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS("region", -1, "cmk", -2, "invalid cmk");

    /* Test invalid UTF 8 */
    REFRESH;
    ASSERT_MASTERKEY_AWS_FAILS(invalid_utf8, -1, "cmk", -2, "invalid region");

    /* Test double setting. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_MASTERKEY_AWS_FAILS("region", -1, "cmk", -1, "master key already set");

    /* Cannot be set with local masterkey. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_MASTERKEY_LOCAL_FAILS("master key already set");

    /* Cannot be set after entering error state. */
    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_MASTERKEY_AWS_FAILS("region", -1, "cmk", -1, "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_masterkey_local(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test double setting. */
    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_MASTERKEY_LOCAL_FAILS("master key already set");

    /* Cannot be set with aws masterkey. */
    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_MASTERKEY_AWS_FAILS("region", -1, "cmk", -1, "master key already set");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_MASTERKEY_LOCAL_FAILS("test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_key_encryption_key_azure(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test double setting. */
    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                              "'keyVaultEndpoint': 'example.com' }"),
                                    "key encryption key already set");

    /* Cannot be set when another masterkey is set. */
    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                              "'keyVaultEndpoint': 'example.com' }"),
                                    "key encryption key already set");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                              "'keyVaultEndpoint': 'example.com' }"),
                                    "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_key_encryption_key_gcp(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test double setting. */
    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'gcp', 'projectId': 'proj', 'location': "
                                           "'google.com', 'keyRing': 'ring', 'keyName': 'key' }"));
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'gcp', 'projectId': 'proj', 'location': "
                                              "'google.com', 'keyRing': 'ring', 'keyName': 'key' }"),
                                    "key encryption key already set");

    /* Cannot be set when another masterkey is set. */
    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'gcp', 'projectId': 'proj', 'location': "
                                              "'google.com', 'keyRing': 'ring', 'keyName': 'key' }"),
                                    "key encryption key already set");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_KEY_ENCRYPTION_KEY_FAILS(TEST_BSON("{'provider': 'gcp', 'projectId': 'proj', 'location': "
                                              "'google.com', 'keyRing': 'ring', 'keyName': 'key' }"),
                                    "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_key_id(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test double setting. */
    REFRESH;
    ASSERT_KEY_ID_OK(TEST_BIN(16));
    ASSERT_KEY_ID_FAILS(TEST_BIN(16), "option already set");

    /* Test NULL/empty input */
    REFRESH;
    ASSERT_KEY_ID_FAILS(NULL, "option must be non-NULL");

    REFRESH;
    ASSERT_KEY_ID_FAILS(TEST_BIN(0), "option must be non-NULL");

    /* Test wrong length */
    REFRESH;
    ASSERT_KEY_ID_FAILS(TEST_BIN(5), "expected 16 byte UUID");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_KEY_ID_FAILS(TEST_BIN(16), "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_key_alt_name(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test double setting - actually succeeds since multiple key alt names
     * allowed for data keys. */
    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'def'}"));

    /* Test NULL/empty input */
    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(NULL, "option must be non-NULL");

    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BIN(0), "option must be non-NULL");

    /* Test wrong type */
    REFRESH;
    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BSON("{'keyAltName': 1}"), "keyAltName expected to be UTF8");

    /* Test missing key */
    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BSON("{'keyAltNames': 'abc'}"), "keyAltName must have field 'keyAltName'");

    /* Test extra key */
    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BSON("{'keyAltName': 'abc', 'extra': 1}"), "unrecognized field");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_key_material(_mongocrypt_tester_t *tester) {
#define KEY_MATERIAL_PATTERN "{'keyMaterial': {'$binary': {'base64': '%s', 'subType': '00'}}%s}"

    /* "0123456789abcef", repeated 6 times. */
    const char *const material = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1"
                                 "Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVm";

    mongocrypt_binary_t *const valid = TEST_BSON(KEY_MATERIAL_PATTERN, material, "");

    mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    mongocrypt_ctx_t *ctx = NULL;

    /* Test double setting. */
    REFRESH;
    ASSERT_KEY_MATERIAL_OK(valid);
    ASSERT_KEY_MATERIAL_FAILS(valid, "keyMaterial already set");

    /* Test NULL input. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(NULL, "option must be non-NULL");

    /* Test empty input. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BIN(0), "option must be non-NULL");

    /* Test empty key material. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON(KEY_MATERIAL_PATTERN, "", ""),
                              "keyMaterial should have length 96, but has length 0");

    /* Test too short key material. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON(KEY_MATERIAL_PATTERN,
                                        "dG9vc2hvcnQ=", /* "tooshort" */
                                        ""),
                              "keyMaterial should have length 96, but has length 8");

    /* Test too long key material. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON(KEY_MATERIAL_PATTERN,
                                        /* "0123456789abcdef", repeated 6 times, followed by "toolong". */
                                        "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyM"
                                        "zQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmdG9vbG9uZw"
                                        "==",
                                        ""),
                              "keyMaterial should have length 96, but has length 103");

    /* Test invalid keyMaterial options. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON("{}"), "invalid bson");

    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON("{'a': 1}"), "keyMaterial must have field 'keyMaterial'");

    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON("{'keyMaterial': 1}"), "keyMaterial must be binary data");

    /* Test extra key. */
    REFRESH;
    ASSERT_KEY_MATERIAL_FAILS(TEST_BSON(KEY_MATERIAL_PATTERN, material, ", 'a': 1"),
                              "unrecognized field, only keyMaterial expected");

    /* Test error propagation. */
    REFRESH;
    ASSERT(!_mongocrypt_ctx_fail_w_msg(ctx, "test"));
    ASSERT_KEY_MATERIAL_FAILS(valid, "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);

#undef KEY_MATERIAL_PATTERN
}

static void _test_setopt_algorithm(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    REFRESH;
    ASSERT_ALGORITHM_FAILS(DET, -2, "invalid algorithm length");

    REFRESH;
    ASSERT_ALGORITHM_OK(DET, 43);

    REFRESH;
    ASSERT_ALGORITHM_FAILS(DET, 42, "unsupported algorithm");

    /* Check for prior bug. It's "Random", not "Randomized" */
    REFRESH;
    ASSERT_ALGORITHM_FAILS(RAND "ized", -1, "unsupported algorithm");

    /* Test double setting. */
    REFRESH;
    ASSERT_ALGORITHM_OK(DET, -1);
    ASSERT_ALGORITHM_FAILS(DET, -1, "already set algorithm");

    /* Test NULL input */
    REFRESH;
    ASSERT_ALGORITHM_FAILS(NULL, 0, "passed null algorithm");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_ALGORITHM_FAILS(RAND, -1, "test");

    /* Test case insensitive. */
    REFRESH;
    ASSERT_ALGORITHM_OK("aEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", -1);
    REFRESH;
    ASSERT_ALGORITHM_OK("aEAD_AES_256_CBC_HMAC_SHA_512-Random", -1);
    REFRESH;
    ASSERT_ALGORITHM_OK("indexed", -1);
    REFRESH;
    ASSERT_ALGORITHM_OK("unindexed", -1);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_query_type(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* Test valid input. */
    REFRESH;
    ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, (int)strlen(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR));

    /* Test invalid length. */
    REFRESH;
    ASSERT_QUERY_TYPE_FAILS("foo", -2, "Invalid query_type string length");

    /* Test double setting. */
    REFRESH;
    ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1);
    ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1);

    /* Test NULL input */
    REFRESH;
    ASSERT_QUERY_TYPE_FAILS(NULL, 0, "Invalid null query_type string");

    /* Test with failed context. */
    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_QUERY_TYPE_FAILS(MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1, "test");

    /* Test case insensitive. */
    REFRESH;
    ASSERT_QUERY_TYPE_OK("Equality", -1);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test required and prohibited options on a datakey context. */
static void _test_setopt_for_datakey(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_binary_t *uuid;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    uuid = TEST_BIN(16);

    /* Test required and prohibited options. */
    REFRESH;
    ASSERT_DATAKEY_INIT_FAILS("master key required");

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_DATAKEY_INIT_OK;

    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ASSERT_DATAKEY_INIT_OK;

    /* Test optional key alt names. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_DATAKEY_INIT_OK;

    /* Multiple key alt names are okay. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'def'}"));
    ASSERT_DATAKEY_INIT_OK;

    /* But duplicates are not. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BSON("{'keyAltName': 'abc'}"), "duplicate keyAltNames found");

    /* Key Material is okay. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_KEY_MATERIAL_OK(TEST_BSON("{'keyMaterial': {'$binary': {'base64': "
                                     "'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJj"
                                     "ZGVmMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5Y"
                                     "WJjZGVm', 'subType': '00'}}}"));
    ASSERT_DATAKEY_INIT_OK;

    /* Test each prohibited option. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_DATAKEY_INIT_FAILS("key id and alt name prohibited");

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_ALGORITHM_OK(MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1);
    ASSERT_DATAKEY_INIT_FAILS("algorithm prohibited");

    /* Test setting options after init. */
    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_DATAKEY_INIT_OK;
    ASSERT_MASTERKEY_AWS_FAILS("region", -1, "cmk", -1, "cannot set options after init");

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_ENDPOINT_OK("example.com:80", -1);
    ASSERT_DATAKEY_INIT_OK;

    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_ENDPOINT_FAILS("example.com:80", -1, "endpoint prohibited");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_for_encrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_binary_t *uuid, *cmd;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    cmd = TEST_FILE("./test/example/cmd.json");
    uuid = TEST_BIN(16);

    /* Test required and prohibited options. */
    REFRESH;
    ASSERT_ENCRYPT_INIT_OK("a", -1, cmd);

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ENCRYPT_INIT_FAILS("a", -1, cmd, "master key prohibited");

    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ENCRYPT_INIT_FAILS("a", -1, cmd, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ENCRYPT_INIT_FAILS("a", -1, cmd, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ENCRYPT_INIT_FAILS("a", -1, cmd, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ENCRYPT_INIT_FAILS("a", -1, cmd, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_MATERIAL_OK(TEST_BSON("{'keyMaterial': {'$binary': {'base64': "
                                     "'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJj"
                                     "ZGVmMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5Y"
                                     "WJjZGVm', 'subType': '00'}}}"));
    ENCRYPT_INIT_FAILS("a", -1, cmd, "key material prohibited");

    REFRESH;
    ASSERT_ALGORITHM_OK(DET, -1);
    ENCRYPT_INIT_FAILS("a", -1, cmd, "algorithm prohibited");

    REFRESH;
    ENCRYPT_INIT_FAILS("a", -1, NULL, "invalid command");

    /* Test setting options after init. */
    REFRESH;
    ASSERT_ENCRYPT_INIT_OK("a", -1, cmd);
    ASSERT_MASTERKEY_LOCAL_FAILS("cannot set options after init");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_binary_t *bson, *uuid, *rangeopts, *textopts;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    uuid = TEST_BIN(16);
    bson = TEST_BSON("{'v': 'hello'}");
    rangeopts = TEST_BSON("{'min': 0, 'max': 1, 'sparsity': {'$numberLong': '1'}}");
    textopts = TEST_BSON(RAW_STRING({
        "caseSensitive" : true,
        "diacriticSensitive" : false,
        "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 2}
    }));

    /* Test required and prohibited options. */
    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_OK(bson);

    /* Just keyAltName is ok */
    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_OK(bson);

    /* Two keyAltNames is invalid */
    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'def'}"));
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "must not specify multiple key alt names");

    /* Both keyAltName and keyId is invalid */
    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot have both key id and key alt name");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_KEY_MATERIAL_OK(TEST_BSON("{'keyMaterial': {'$binary': {'base64': "
                                     "'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJj"
                                     "ZGVmMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5Y"
                                     "WJjZGVm', 'subType': '00'}}}"));
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "key material prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "algorithm or index type required");

    REFRESH;
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "key id or key alt name required");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(DET, -1);
    ASSERT_EX_ENCRYPT_INIT_OK(bson);

    /* Just key alt name is ok */
    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(DET, -1);
    ASSERT_EX_ENCRYPT_INIT_OK(bson);

    /* Test setting options after init. */
    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_OK(RAND, -1);
    ASSERT_EX_ENCRYPT_INIT_OK(bson);
    ASSERT_ALGORITHM_FAILS(RAND, -1, "cannot set options after init");

    /* Test that an option failure validated at the time of 'setopt' persists
     * upon init. */
    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_ALGORITHM_FAILS("bad-algo", -1, "unsupported algorithm");
    ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "unsupported algorithm");

    /* It is an error to set the FLE 1 keyAltName option with any of the FLE 2
     * options (index_type, index_key_id, contention_factor, query_type, or
     * range opts). */
    {
        REFRESH;
        ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both key alt name and index type");

        REFRESH;
        ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
        ASSERT_OK(mongocrypt_ctx_setopt_index_key_id(ctx, uuid), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both key alt name and index key id");

        REFRESH;
        ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 123), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both key alt name and contention factor");

        REFRESH;
        ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both key alt name and query type");

        REFRESH;
        ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both key alt name and range opts");
    }

    /* It is an error to set the FLE 1 algorithm option with any of the FLE 2
     * options (index_type, index_key_id, contention_factor, query_type, or
     * range opts). */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(RAND, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_index_key_id(ctx, uuid), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both algorithm and index key id");

        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(RAND, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 123), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both algorithm and contention factor");
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(RAND, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both algorithm and query type");
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(RAND, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set both algorithm and range opts");
    }

    /* Require either index_type or algorithm */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "algorithm or index type required");
    }

    /* It is an error to set contention_factor with index_type ==
     * MONGOCRYPT_INDEX_TYPE_NONE */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set contention factor with no index type");
    }

    /* It is an error to set range opts with index_type ==
     * MONGOCRYPT_INDEX_TYPE_NONE */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set range opts with no index type");
    }

    /* It is an error to set range opts with index_type ==
     * MONGOCRYPT_INDEX_TYPE_EQUALITY */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set range opts with equality index type");
    }

    /* It is an error to set query_type with index_type ==
     * MONGOCRYPT_INDEX_TYPE_NONE */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_EQUALITY_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set query type with no index type");
    }

    /* Contention factor is required for "Indexed" algorithm. */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "contention factor is required");
    }

    /* Contention factor is required for "range" algorithm. */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "contention factor is required");
    }

    /* Range opts is required for "range" algorithm. */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "range opts are required");
    }

    /* Sparsity is optional for rangeV2. */
    {
        // Create a crypt with rangeV2 enabled.
        mongocrypt_destroy(crypt);
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT | TESTER_MONGOCRYPT_DEFAULT);
        REFRESH_CTX;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, TEST_BSON("{'min': 0, 'max': 1}")), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT(ctx->opts.rangeopts.set);
        ASSERT_CMPINT64(ctx->opts.rangeopts.value.sparsity, ==, mc_FLERangeSparsityDefault);
        ASSERT_EX_ENCRYPT_INIT_OK(bson);
    }

    /* Negative sparsity is prohibited. */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm_range(
                         ctx,
                         TEST_BSON("{'min': 0, 'max': 1, 'sparsity': { '$numberLong': '-1'}}")),
                     ctx,
                     "sparsity must be non-negative");
    }

    /* Error if query_type == "range" and algorithm != "range". */
    {
        REFRESH;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(MONGOCRYPT_ALGORITHM_INDEXED_STR, -1);
        ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_RANGE_STR, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "must match index_type");
    }

    /* Error if query_type == "range" for
     * mongocrypt_ctx_explicit_encrypt_init. */
    {
        REFRESH;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(MONGOCRYPT_ALGORITHM_RANGE_STR, -1);
        ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_RANGE_STR, -1);
        ASSERT_OK(
            mongocrypt_ctx_setopt_algorithm_range(ctx,
                                                  TEST_BSON("{'min': 0, 'max': 1, 'sparsity': {'$numberLong': '1'}}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "Encrypt may not be used for range queries. Use EncryptExpression.");
    }

    // Can't use "rangePreview" algorithm or query type with range V2.
    {
        mongocrypt_destroy(crypt);
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        REFRESH_CTX;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "'rangePreview' is deprecated");

        mongocrypt_destroy(crypt);
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        REFRESH_CTX;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_RANGEPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "'rangePreview' is deprecated");
    }

    /* It is an error to set range opts with the 'string' algorithm */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set range opts with 'string' algorithm");
    }

    /* If query type == algorithm == "range", succeeds. */
    {
        REFRESH;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx);
        ASSERT_ALGORITHM_OK(MONGOCRYPT_ALGORITHM_RANGE_STR, -1);
        ASSERT_QUERY_TYPE_OK(MONGOCRYPT_QUERY_TYPE_RANGE_STR, -1);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_EX_ENCRYPT_EXPRESSION_INIT_OK(bson);
    }

    /* Error if query_type is unset for
     * mongocrypt_ctx_explicit_encrypt_expression_init. */
    {
        REFRESH;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_ALGORITHM_OK(MONGOCRYPT_ALGORITHM_RANGE_STR, -1);
        ASSERT_OK(
            mongocrypt_ctx_setopt_algorithm_range(ctx,
                                                  TEST_BSON("{'min': 0, 'max': 1, 'sparsity': {'$numberLong': '1'}}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_EX_ENCRYPT_EXPRESSION_INIT_FAILS(bson, "EncryptExpression may only be used for range queries.");
    }

    /* It is an error to set text opts with index_type ==
     * MONGOCRYPT_INDEX_TYPE_NONE */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type");
    }

    /* It is an error to set text opts with index_type ==
     * MONGOCRYPT_INDEX_TYPE_EQUALITY */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type");
    }

    /* It is an error to set text opts with index_type ==
     * MONGOCRYPT_INDEX_TYPE_RANGE */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type");
    }

    /* It is an error to set a text query_type with index_type !=
     * MONGOCRYPT_INDEX_TYPE_STRING */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "substringPreview query type requires string index type");

        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIX_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires string index type");

        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIX_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires string index type");

        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIX_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires string index type");

        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required'
         * error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIX_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires string index type");
    }

    // Can't use "prefixPreview" or "suffixPreview".
    {
        mongocrypt_destroy(crypt);
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        REFRESH_CTX;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "'prefixPreview' is deprecated");

        mongocrypt_destroy(crypt);
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        REFRESH_CTX;
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR, -1),
                     ctx,
                     "'suffixPreview' is deprecated");
    }

    /* It is an error to set a string algorithm without setting text options */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required' error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "string opts are required for string algorithm");
    }

    /* It is an error to set a string algorithm without setting contention */
    {
        REFRESH;
        /* Set key ID to get past the 'either key id or key alt name required' error */
        ASSERT_KEY_ID_OK(uuid);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx);
        ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "contention factor is required for string algorithm");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_for_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_binary_t *bson, *uuid;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    uuid = TEST_BIN(16);
    bson = TEST_BSON("{'a': 1}");

    /* Test required and prohibited options. */
    REFRESH;
    ASSERT_DECRYPT_INIT_OK(bson);

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ASSERT_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_DECRYPT_INIT_FAILS(bson, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_DECRYPT_INIT_FAILS(bson, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_MATERIAL_OK(TEST_BSON("{'keyMaterial': {'$binary': {'base64': "
                                     "'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJj"
                                     "ZGVmMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5Y"
                                     "WJjZGVm', 'subType': '00'}}}"));
    ASSERT_DECRYPT_INIT_FAILS(bson, "key material prohibited");

    REFRESH;
    ASSERT_ALGORITHM_OK(DET, -1);
    ASSERT_DECRYPT_INIT_FAILS(bson, "algorithm prohibited");

    /* Test setting options after init. */
    REFRESH;
    ASSERT_DECRYPT_INIT_OK(bson);
    ASSERT_MASTERKEY_LOCAL_FAILS("cannot set options after init");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_for_explicit_decrypt(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_binary_t *bson, *uuid;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    uuid = TEST_BIN(16);
    bson = TEST_FILE("./test/data/explicit-decryption-input.json");

    /* Test required and prohibited options. */
    REFRESH;
    ASSERT_EX_DECRYPT_INIT_OK(bson);

    REFRESH;
    ASSERT_MASTERKEY_AWS_OK("region", -1, "cmk", -1);
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_MASTERKEY_LOCAL_OK;
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ENCRYPTION_KEY_OK(TEST_BSON("{'provider': 'azure', 'keyName': '', "
                                           "'keyVaultEndpoint': 'example.com' }"));
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "master key prohibited");

    REFRESH;
    ASSERT_KEY_ID_OK(uuid);
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_ALT_NAME_OK(TEST_BSON("{'keyAltName': 'abc'}"));
    ASSERT_DECRYPT_INIT_FAILS(bson, "key id and alt name prohibited");

    REFRESH;
    ASSERT_KEY_MATERIAL_OK(TEST_BSON("{'keyMaterial': {'$binary': {'base64': "
                                     "'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJj"
                                     "ZGVmMDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5Y"
                                     "WJjZGVm', 'subType': '00'}}}"));
    ASSERT_DECRYPT_INIT_FAILS(bson, "key material prohibited");

    REFRESH;
    ASSERT_ALGORITHM_OK(DET, -1);
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "algorithm prohibited");

    // Range opts are prohibited.
    REFRESH;
    ASSERT_OK(
        mongocrypt_ctx_setopt_algorithm_range(ctx, TEST_BSON("{'min': 0, 'max': 1, 'sparsity': {'$numberLong': '1'}}")),
        ctx);
    ASSERT_EX_DECRYPT_INIT_FAILS(bson, "range opts are prohibited");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_failure_uninitialized(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;
    mongocrypt_status_t *status;

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

    REFRESH;
    ASSERT_KEY_ALT_NAME_FAILS(TEST_BSON("{'fake': 'abc'}"), "keyAltName must have field 'keyAltName'");
    /* Though mongocrypt_ctx_t is uninitialized, we should still get failure
     * status. */
    ASSERT_FAILS_STATUS(mongocrypt_ctx_status(ctx, status), status, "keyAltName must have field 'keyAltName'");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

static void _test_setopt_endpoint(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx = NULL;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    REFRESH;
    ASSERT_ENDPOINT_FAILS("example.com", -2, "Invalid endpoint");

    REFRESH;
    ASSERT_ENDPOINT_OK("example.com", -1);
    BSON_ASSERT(0 == strcmp(ctx->opts.kek.provider.aws.endpoint->host_and_port, "example.com"));

    /* Including a port is ok. */
    REFRESH;
    ASSERT_ENDPOINT_OK("example.com:80", -1);
    BSON_ASSERT(0 == strcmp(ctx->opts.kek.provider.aws.endpoint->host_and_port, "example.com:80"));

    /* Test double setting. */
    REFRESH;
    ASSERT_ENDPOINT_OK("example.com", -1);
    ASSERT_ENDPOINT_FAILS("example.com", -1, "already set masterkey endpoint");

    /* Test NULL input */
    REFRESH;
    ASSERT_ENDPOINT_FAILS(NULL, 0, "Invalid endpoint");

    REFRESH;
    _mongocrypt_ctx_fail_w_msg(ctx, "test");
    ASSERT_ENDPOINT_FAILS(RAND, -1, "test");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_createdatakey_with_wrong_kms_provider_helper(_mongocrypt_tester_t *tester,
                                                               mongocrypt_binary_t *kms_provider) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    const char *const kek = "{"
                            "'provider': 'azure',"
                            "'keyName': 'foo',"
                            "'keyVaultEndpoint': 'example.com'"
                            "}";

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_provider), crypt);
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON_STR(kek)), ctx);
    ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx), ctx, "kms provider required by datakey is not configured");

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_setopt_createdatakey_wrong_kms_provider_configured(_mongocrypt_tester_t *tester) {
    _test_createdatakey_with_wrong_kms_provider_helper(tester, TEST_BSON("{'gcp': { 'accessToken': '1234' } }"));
}

static void _test_setopt_createdatakey_wrong_kms_provider_empty(_mongocrypt_tester_t *tester) {
    _test_createdatakey_with_wrong_kms_provider_helper(tester, TEST_BSON("{'gcp': {}}"));
}

static void _test_options(_mongocrypt_tester_t *tester) {
    /* Test individual options */
    _test_setopt_masterkey_aws(tester);
    _test_setopt_masterkey_local(tester);
    _test_setopt_key_id(tester);
    _test_setopt_algorithm(tester);
    _test_setopt_key_alt_name(tester);
    _test_setopt_key_material(tester);
    _test_setopt_endpoint(tester);
    _test_setopt_key_encryption_key_azure(tester);
    _test_setopt_key_encryption_key_gcp(tester);
    _test_setopt_query_type(tester);
    _test_setopt_createdatakey_wrong_kms_provider_empty(tester);
    _test_setopt_createdatakey_wrong_kms_provider_configured(tester);

    /* Test options on different contexts */
    _test_setopt_for_datakey(tester);
    _test_setopt_for_encrypt(tester);
    _test_setopt_for_explicit_encrypt(tester);
    _test_setopt_for_decrypt(tester);
    _test_setopt_for_explicit_decrypt(tester);

    /* Test that failure to set an option on an uninitialized context is returned
     * through mongocrypt_ctx_status */
    _test_setopt_failure_uninitialized(tester);
}

void _mongocrypt_tester_install_ctx_setopt(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_options);
}
libmongocrypt-1.19.0/test/test-mongocrypt-datakey.c000066400000000000000000001044311521103432300224100ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 

#include "kms_message/kms_b64.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-private.h"
#include "test-mongocrypt.h"

static void _init_buffer_with_count(_mongocrypt_buffer_t *out, uint32_t count) {
    out->len = count;
    out->data = bson_malloc0(out->len);
    BSON_ASSERT(out->data);

    out->owned = true;
}

static void _test_random_generator(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    _mongocrypt_buffer_t out;
    mongocrypt_status_t *status;
#define TEST_COUNT 32
    int mid = TEST_COUNT / 2;
    char zero[TEST_COUNT];

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    /* _mongocrypt_random handles the case where the count size is greater
     * than the buffer by throwing an error. Because of that, no additional tests
     * for this case is needed here. */

    memset(zero, 0, TEST_COUNT);
    status = mongocrypt_status_new();
    _init_buffer_with_count(&out, TEST_COUNT);

    BSON_ASSERT(_mongocrypt_random(crypt->crypto, &out, TEST_COUNT, status));
    BSON_ASSERT(0 != memcmp(zero, out.data, TEST_COUNT)); /* initialized */

    mongocrypt_status_destroy(status);
    _mongocrypt_buffer_cleanup(&out);

    status = mongocrypt_status_new();
    _init_buffer_with_count(&out, TEST_COUNT);

    ASSERT_FAILS_STATUS(_mongocrypt_random(crypt->crypto, &out, mid, status), status, "out should have length 16");

    mongocrypt_status_destroy(status);
    _mongocrypt_buffer_cleanup(&out);
    mongocrypt_destroy(crypt);
}

static void _test_create_data_key_with_provider(_mongocrypt_tester_t *tester,
                                                _mongocrypt_kms_provider_t provider,
                                                bool with_alt_name) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_binary_t *bin;
    bson_t as_bson;
    bson_iter_t iter;
    _mongocrypt_buffer_t buf;
    int64_t created_date;
    const int64_t current_epoch_time_ms = 1565097975532ll; /* the time this code was written */
    const int64_t one_hundred_years_ms = (int64_t)1000ll * 60ll * 60ll * 24ll * 365ll * 100ll;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    if (provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "region", -1, "cmk", -1), ctx);
    } else {
        ASSERT_OK(mongocrypt_ctx_setopt_masterkey_local(ctx), ctx);
    }

    if (with_alt_name) {
        ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'b'}")), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON("{'keyAltName': 'a'}")), ctx);
    }

    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
    if (provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
        kms = mongocrypt_ctx_next_kms_ctx(ctx);
        BSON_ASSERT(kms);
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-aws/encrypt-response.txt")), kms);
        BSON_ASSERT(0 == mongocrypt_kms_ctx_bytes_needed(kms));
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    /* Check the BSON document created. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &as_bson));
    CRYPT_TRACEF(&crypt->log, "created data key: %s\n", tmp_json(&as_bson));
    /* _id is a UUID */
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "_id"));
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    BSON_ASSERT(buf.subtype == BSON_SUBTYPE_UUID);
    /* keyMaterial is a binary blob of >= KEYMATERIAL_LEN bytes. */
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "keyMaterial"));
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    BSON_ASSERT(buf.subtype == BSON_SUBTYPE_BINARY);
    BSON_ASSERT(buf.len >= MONGOCRYPT_KEY_LEN);
    /* creationDate and updatedDate exist and have the same value. */
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "creationDate"));
    BSON_ASSERT(BSON_ITER_HOLDS_DATE_TIME(&iter));
    created_date = bson_iter_date_time(&iter);
    BSON_ASSERT(created_date > current_epoch_time_ms && created_date < current_epoch_time_ms + one_hundred_years_ms);
    BSON_ASSERT(bson_iter_init_find(&iter, &as_bson, "updateDate"));
    BSON_ASSERT(BSON_ITER_HOLDS_DATE_TIME(&iter));
    BSON_ASSERT(created_date == bson_iter_date_time(&iter));
    if (with_alt_name) {
        BSON_ASSERT(bson_iter_init(&iter, &as_bson));
        BSON_ASSERT(bson_iter_find_descendant(&iter, "keyAltNames.0", &iter));
        BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
        BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), "a"));
        BSON_ASSERT(bson_iter_init(&iter, &as_bson));
        BSON_ASSERT(bson_iter_find_descendant(&iter, "keyAltNames.1", &iter));
        BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
        BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), "b"));
        BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
    } else {
        BSON_ASSERT(!bson_iter_init_find(&iter, &as_bson, "keyAltNames"));
    }

    /* masterKey matches set options. */
    BSON_ASSERT(bson_iter_init(&iter, &as_bson));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.provider", &iter));
    BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
    if (provider == MONGOCRYPT_KMS_PROVIDER_AWS) {
        BSON_ASSERT(0 == strcmp("aws", bson_iter_utf8(&iter, NULL)));
        BSON_ASSERT(bson_iter_init(&iter, &as_bson));
        BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.region", &iter));
        BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
        BSON_ASSERT(0 == strcmp("region", bson_iter_utf8(&iter, NULL)));
        BSON_ASSERT(bson_iter_init(&iter, &as_bson));
        BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.key", &iter));
        BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
        BSON_ASSERT(0 == strcmp("cmk", bson_iter_utf8(&iter, NULL)));
    } else {
        BSON_ASSERT(0 == strcmp("local", bson_iter_utf8(&iter, NULL)));
    }
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_datakey_custom_endpoint(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms_ctx;
    mongocrypt_binary_t *bin;
    const char *endpoint;
    bson_t key_bson;
    bson_iter_t iter;

    /* Success. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "region", -1, "cmk", -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws_endpoint(ctx, "example.com", -1), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
    BSON_ASSERT(kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(kms_ctx, &endpoint), ctx);
    BSON_ASSERT(0 == strcmp("example.com:443", endpoint));
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), ctx);
    BSON_ASSERT(NULL != strstr((char *)bin->data, "Host:example.com"));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-aws/encrypt-response.txt")), kms_ctx);
    BSON_ASSERT(0 == mongocrypt_kms_ctx_bytes_needed(kms_ctx));
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);

    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    /* Check the BSON document created. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &key_bson));
    BSON_ASSERT(bson_iter_init(&iter, &key_bson));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.endpoint", &iter));
    BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), "example.com"));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void _test_datakey_kms_per_ctx_credentials(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_kms_ctx_t *kms_ctx;
    mongocrypt_binary_t *bin;
    const char *endpoint;
    bson_t key_bson;
    bson_iter_t iter;

    /* Success. */
    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {}}")), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "region", -1, "cmk", -1), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws_endpoint(ctx, "example.com", -1), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);
    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'aws':{'accessKeyId': 'example',"
                                                             "'secretAccessKey': 'example'}}")),
              ctx);
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_KMS);
    kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
    BSON_ASSERT(kms_ctx);
    ASSERT_OK(mongocrypt_kms_ctx_endpoint(kms_ctx, &endpoint), ctx);
    BSON_ASSERT(0 == strcmp("example.com:443", endpoint));
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), ctx);
    BSON_ASSERT(NULL != strstr((char *)bin->data, "Host:example.com"));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-aws/encrypt-response.txt")), kms_ctx);
    BSON_ASSERT(0 == mongocrypt_kms_ctx_bytes_needed(kms_ctx));
    ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);

    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    /* Check the BSON document created. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &key_bson));
    BSON_ASSERT(bson_iter_init(&iter, &key_bson));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.endpoint", &iter));
    BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), "example.com"));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test creating a data key with "azure" when only "aws" credentials are needed.
 * Expect the context not to enter the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS
 * state. */
static void _test_datakey_kms_per_ctx_credentials_not_requested(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt,
                                              TEST_BSON("{'aws': {}, 'azure': {'tenantId': '', 'clientId': "
                                                        "'', 'clientSecret': '' }}")),
              crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                       TEST_BSON("{'provider': 'azure', 'keyVaultEndpoint': "
                                                                 "'example.vault.azure.net', 'keyName': 'test'}")),
              ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

/* Test creating a data key with "local" when "local" credentials are required.
 * Expect the context to enter the MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS
 * state. */
static void _test_datakey_kms_per_ctx_credentials_local(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    bson_t key_bson;
    bson_iter_t iter;
    uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0};
    char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw));

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'local': {}}")), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON("{'provider': 'local' }")), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS);

    ASSERT_OK(mongocrypt_ctx_provide_kms_providers(ctx,
                                                   TEST_BSON("{'local':{'key': { '$binary': {'base64': '%s', "
                                                             "'subType': '00'}}}}",
                                                             local_kek)),
              ctx);

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    /* Check the BSON document created. */
    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &key_bson));
    BSON_ASSERT(bson_iter_init(&iter, &key_bson));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "masterKey.provider", &iter));
    BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), "local"));

    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    bson_free(local_kek);
}

static void _test_datakey_custom_key_material(_mongocrypt_tester_t *tester) {
    const uint8_t expected_dek[MONGOCRYPT_KEY_LEN] = "0123456789abcdef"
                                                     "0123456789abcdef"
                                                     "0123456789abcdef"
                                                     "0123456789abcdef"
                                                     "0123456789abcdef"
                                                     "0123456789abcdef";

    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    _mongocrypt_buffer_t encrypted_dek_buf;

    {
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

        /* Generate encrypted DEK using custom key material. */
        {
            mongocrypt_binary_t *const kek = TEST_BSON("{'provider': 'local'}");

            bson_t bson;
            _mongocrypt_buffer_t key_material_buf;
            mongocrypt_binary_t key_material;

            bson_init(&bson);
            ASSERT(BSON_APPEND_BINARY(&bson, "keyMaterial", BSON_SUBTYPE_BINARY, expected_dek, MONGOCRYPT_KEY_LEN));
            _mongocrypt_buffer_from_bson(&key_material_buf, &bson);
            _mongocrypt_buffer_to_binary(&key_material_buf, &key_material);

            ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, kek), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_key_material(ctx, &key_material), ctx);
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

            bson_destroy(&bson);
        }

        /* Extract encrypted DEK from datakey. */
        {
            mongocrypt_binary_t *const datakey = mongocrypt_binary_new();

            bson_t bson;
            bson_iter_t iter;
            const uint8_t *binary;
            uint32_t len;
            bson_subtype_t subtype;

            _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, datakey), ctx);
            ASSERT(_mongocrypt_binary_to_bson(datakey, &bson));

            ASSERT(bson_iter_init_find(&iter, &bson, "keyMaterial"));
            ASSERT(BSON_ITER_HOLDS_BINARY(&iter));
            bson_iter_binary(&iter, &subtype, &len, &binary);
            ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&encrypted_dek_buf, binary, len));

            mongocrypt_binary_destroy(datakey);
        }

        mongocrypt_ctx_destroy(ctx);
    }

    /* Decrypt key material and confirm it matches original DEK. */
    {
        _mongocrypt_buffer_t decrypted_dek_buf;
        mongocrypt_binary_t decrypted_dek;

        mc_kms_creds_t kc;
        ASSERT(_mongocrypt_opts_kms_providers_lookup(&crypt->opts.kms_providers, "local", &kc));

        ASSERT(_mongocrypt_unwrap_key(crypt->crypto,
                                      &kc.value.local.key,
                                      &encrypted_dek_buf,
                                      &decrypted_dek_buf,
                                      crypt->status));

        _mongocrypt_buffer_to_binary(&decrypted_dek_buf, &decrypted_dek);

        ASSERT_CMPBYTES(expected_dek, MONGOCRYPT_KEY_LEN, decrypted_dek.data, decrypted_dek.len);

        _mongocrypt_buffer_cleanup(&decrypted_dek_buf);
    }

    _mongocrypt_buffer_cleanup(&encrypted_dek_buf);
    mongocrypt_destroy(crypt);
}

static void _test_create_datakey_with_retry(_mongocrypt_tester_t *tester) {
    // Test that an HTTP error is retried.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kms_ctx);
        // Expect KMS request is returned again for a retry.
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-aws/encrypt-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that an HTTP error is retried in-place.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        bool should_retry;
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt"),
                                                     &should_retry),
                  kms_ctx);
        // In-place retry is indicated.
        ASSERT(should_retry);
        // Feed another retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt"),
                                                     &should_retry),
                  kms_ctx);
        // Expect some sleep is requested
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), >=, 0);
        // In-place retry is indicated.
        ASSERT(should_retry);
        ASSERT(kms_ctx->attempts == 2);

        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/kms-aws/encrypt-response.txt"),
                                                     &should_retry),
                  kms_ctx);
        ASSERT(!should_retry);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that a network error is retried.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Mark a network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Expect KMS request is returned again for a retry.
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-aws/encrypt-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that a network error is retried in-place.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        bool should_retry;
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Mark a network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Feed a partial response
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/kms-aws/encrypt-response-partial.txt"),
                                                     &should_retry),
                  kms_ctx);
        ASSERT(!should_retry);
        // Mark another network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Expect some sleep is requested
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), >=, 0);
        ASSERT(kms_ctx->attempts == 2);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/kms-aws/encrypt-response.txt"),
                                                     &should_retry),
                  kms_ctx);
        ASSERT(!should_retry);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    // Test that subsequent network and HTTP errors can be retried in-place
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        bool should_retry;
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Mark a network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt"),
                                                     &should_retry),
                  kms_ctx);
        // In-place retry is indicated.
        ASSERT(should_retry);
        // Expect some sleep is requested
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), >=, 0);
        ASSERT(kms_ctx->attempts == 2);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/kms-aws/encrypt-response.txt"),
                                                     &should_retry),
                  kms_ctx);
        ASSERT(!should_retry);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that subsequent HTTP and network errors can be retried in-place
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        bool should_retry;
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'aws', 'key': 'foo', 'region': 'bar'}")),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt"),
                                                     &should_retry),
                  kms_ctx);
        // In-place retry is indicated.
        ASSERT(should_retry);
        // Mark a network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Expect some sleep is requested
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), >=, 0);
        ASSERT(kms_ctx->attempts == 2);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed_with_retry(kms_ctx,
                                                     TEST_FILE("./test/data/kms-aws/encrypt-response.txt"),
                                                     &should_retry),
                  kms_ctx);
        ASSERT(!should_retry);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that an oauth request is retried for a network error.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(
                      ctx,
                      TEST_BSON("{'provider': 'azure', 'keyVaultEndpoint': 'example.com', 'keyName': 'foo' }")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect an oauth request.
        {
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), kms_ctx);
            const char *str = (const char *)mongocrypt_binary_data(bin);
            ASSERT_STRCONTAINS(str, "oauth2");
            mongocrypt_binary_destroy(bin);
        }
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Mark a network error.
        ASSERT_OK(mongocrypt_kms_ctx_fail(kms_ctx), kms_ctx);
        // Expect KMS request is returned again for a retry.
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect an oauth request.
        {
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), kms_ctx);
            const char *str = (const char *)mongocrypt_binary_data(bin);
            ASSERT_STRCONTAINS(str, "oauth2");
            mongocrypt_binary_destroy(bin);
        }
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);

        // Expect KMS request for encrypt.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test that an oauth request is retried for an HTTP error.
    {
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(
                      ctx,
                      TEST_BSON("{'provider': 'azure', 'keyVaultEndpoint': 'example.com', 'keyName': 'foo' }")),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect an oauth request.
        {
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), kms_ctx);
            const char *str = (const char *)mongocrypt_binary_data(bin);
            ASSERT_STRCONTAINS(str, "oauth2");
            mongocrypt_binary_destroy(bin);
        }
        // Expect no sleep is requested before any error.
        ASSERT_CMPINT64(mongocrypt_kms_ctx_usleep(kms_ctx), ==, 0);
        // Feed a retryable HTTP error.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/rmd/kms-decrypt-reply-429.txt")), kms_ctx);
        // Expect KMS request is returned again for a retry.
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Expect an oauth request.
        {
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_kms_ctx_message(kms_ctx, bin), kms_ctx);
            const char *str = (const char *)mongocrypt_binary_data(bin);
            ASSERT_STRCONTAINS(str, "oauth2");
            mongocrypt_binary_destroy(bin);
        }

        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);

        // Expect KMS request for encrypt.
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT_OK(kms_ctx, ctx);
        // Feed a successful response.
        ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kms_ctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_create_data_key(_mongocrypt_tester_t *tester) {
    _test_create_data_key_with_provider(tester, MONGOCRYPT_KMS_PROVIDER_AWS, false /* with_alt_name */);
    _test_create_data_key_with_provider(tester, MONGOCRYPT_KMS_PROVIDER_LOCAL, false /* with_alt_name */);
    _test_create_data_key_with_provider(tester, MONGOCRYPT_KMS_PROVIDER_AWS, true /* with_alt_name */);
    _test_create_data_key_with_provider(tester, MONGOCRYPT_KMS_PROVIDER_LOCAL, true /* with_alt_name */);
}

void _mongocrypt_tester_install_data_key(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_random_generator);
    INSTALL_TEST(_test_create_data_key);
    INSTALL_TEST(_test_datakey_custom_endpoint);
    INSTALL_TEST(_test_datakey_custom_key_material);
    INSTALL_TEST(_test_datakey_kms_per_ctx_credentials);
    INSTALL_TEST(_test_datakey_kms_per_ctx_credentials_not_requested);
    INSTALL_TEST(_test_datakey_kms_per_ctx_credentials_local);
    INSTALL_TEST(_test_create_datakey_with_retry);
}
libmongocrypt-1.19.0/test/test-mongocrypt-dll.c000066400000000000000000000023451521103432300215420ustar00rootroot00000000000000#include "mongocrypt.h"

#include "mongocrypt-dll-private.h"

#include 

#include "test-mongocrypt.h"

static void _test_load_simple_library(_mongocrypt_tester_t *t) {
    (void)t;
    mstr self_path = mstr_copy_cstr(TEST_MONGOCRYPT_OUTPUT_PATH);

    mstr dll_path =
        mpath_join(mpath_parent(self_path.view, MPATH_NATIVE), mstrv_view_cstr("test-dll.dll"), MPATH_NATIVE);

    mcr_dll lib = mcr_dll_open(dll_path.raw.data);
    BSON_ASSERT(lib.error_string.raw.len == 0);

    MC_BEGIN_CAST_FUNCTION_TYPE_STRICT_IGNORE
    int (*say_hello)(void) = (int (*)(void))mcr_dll_sym(lib, "say_hello");
    MC_END_CAST_FUNCTION_TYPE_STRICT_IGNORE

    BSON_ASSERT(say_hello != NULL);

    int rval = say_hello();
    ASSERT_CMPINT(rval, ==, 42);

    mcr_dll_close(lib);
    mstr_free(dll_path);
    mstr_free(self_path);
}

static void _test_load_nonesuch(_mongocrypt_tester_t *t) {
    (void)t;
    mcr_dll lib = mcr_dll_open("no-such-directory/no-such-lib.dll");
    BSON_ASSERT(lib._native_handle == NULL);
    BSON_ASSERT(lib.error_string.raw.len > 0);
    mcr_dll_close(lib);
}

void _mongocrypt_tester_install_dll(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_load_simple_library);
    INSTALL_TEST(_test_load_nonesuch);
}
libmongocrypt-1.19.0/test/test-mongocrypt-endpoint.c000066400000000000000000000120201521103432300225760ustar00rootroot00000000000000/*
 * Copyright 2020-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 

#include "test-mongocrypt.h"

static void _test_mongocrypt_endpoint(_mongocrypt_tester_t *tester) {
    _mongocrypt_endpoint_t *endpoint;
    mongocrypt_status_t *status;
    _mongocrypt_endpoint_parse_opts_t opts;

    status = mongocrypt_status_new();

    endpoint = _mongocrypt_endpoint_new("https://kevin.keyvault.azure.net:443/some/path/?query=value",
                                        -1,
                                        NULL /* opts */,
                                        status);
    ASSERT_STREQUAL(endpoint->host, "kevin.keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->domain, "keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->subdomain, "kevin");
    ASSERT_STREQUAL(endpoint->protocol, "https");
    ASSERT_STREQUAL(endpoint->port, "443");
    ASSERT_STREQUAL(endpoint->path, "some/path");
    ASSERT_STREQUAL(endpoint->query, "query=value");
    BSON_ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_endpoint_destroy(endpoint);

    endpoint = _mongocrypt_endpoint_new("kevin.keyvault.azure.net:443", -1, NULL /* opts */, status);
    ASSERT_STREQUAL(endpoint->host, "kevin.keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->domain, "keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->subdomain, "kevin");
    BSON_ASSERT(!endpoint->protocol);
    ASSERT_STREQUAL(endpoint->port, "443");
    BSON_ASSERT(!endpoint->path);
    BSON_ASSERT(!endpoint->query);
    BSON_ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_endpoint_destroy(endpoint);

    endpoint = _mongocrypt_endpoint_new("kevin.keyvault.azure.net", -1, NULL /* opts */, status);
    ASSERT_STREQUAL(endpoint->host, "kevin.keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->domain, "keyvault.azure.net");
    ASSERT_STREQUAL(endpoint->subdomain, "kevin");
    BSON_ASSERT(!endpoint->protocol);
    BSON_ASSERT(!endpoint->port);
    BSON_ASSERT(!endpoint->path);
    BSON_ASSERT(!endpoint->query);
    BSON_ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_endpoint_destroy(endpoint);

    endpoint = _mongocrypt_endpoint_new("malformed", -1, NULL /* opts */, status);
    BSON_ASSERT(!endpoint);
    ASSERT_STATUS_CONTAINS(status, "Invalid endpoint, expected dot separator in host, but got: malformed");
    _mongocrypt_endpoint_destroy(endpoint);

    /* A host without a dot separator is valid if the "allow_empty_subdomain"
     * option is true. */
    memset(&opts, 0, sizeof(opts));
    opts.allow_empty_subdomain = true;
    endpoint = _mongocrypt_endpoint_new("localhost", -1, &opts, status);
    ASSERT_STREQUAL(endpoint->host, "localhost");
    ASSERT_STREQUAL(endpoint->domain, "localhost");
    BSON_ASSERT(!endpoint->subdomain);
    BSON_ASSERT(!endpoint->protocol);
    BSON_ASSERT(!endpoint->port);
    BSON_ASSERT(!endpoint->path);
    BSON_ASSERT(!endpoint->query);
    BSON_ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_endpoint_destroy(endpoint);

    memset(&opts, 0, sizeof(opts));
    opts.allow_empty_subdomain = true;
    endpoint = _mongocrypt_endpoint_new("localhost:1234", -1, &opts, status);
    ASSERT_STREQUAL(endpoint->host, "localhost");
    ASSERT_STREQUAL(endpoint->domain, "localhost");
    BSON_ASSERT(!endpoint->subdomain);
    BSON_ASSERT(!endpoint->protocol);
    ASSERT_STREQUAL(endpoint->port, "1234");
    BSON_ASSERT(!endpoint->path);
    BSON_ASSERT(!endpoint->query);
    BSON_ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_endpoint_destroy(endpoint);

    /* A host without a dot separator is invalid if the "allow_empty_subdomain"
     * option is false. */
    memset(&opts, 0, sizeof(opts));
    opts.allow_empty_subdomain = false;
    endpoint = _mongocrypt_endpoint_new("localhost", -1, &opts, status);
    ASSERT_STATUS_CONTAINS(status, "Invalid endpoint, expected dot separator in host, but got: localhost");
    BSON_ASSERT(!endpoint);

    mongocrypt_status_destroy(status);
}

static void _test_mongocrypt_apply_default_port(_mongocrypt_tester_t *tester) {
    char *endpoint;

    endpoint = bson_strdup("example.com");
    _mongocrypt_apply_default_port(&endpoint, "12");
    ASSERT_STREQUAL(endpoint, "example.com:12");
    bson_free(endpoint);

    endpoint = bson_strdup("example.com:34");
    _mongocrypt_apply_default_port(&endpoint, "12");
    ASSERT_STREQUAL(endpoint, "example.com:34");
    bson_free(endpoint);
}

void _mongocrypt_tester_install_endpoint(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_mongocrypt_endpoint);
    INSTALL_TEST(_test_mongocrypt_apply_default_port);
}
libmongocrypt-1.19.0/test/test-mongocrypt-kek.c000066400000000000000000000063321521103432300215410ustar00rootroot00000000000000/*
 * Copyright 2020-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-kek-private.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"
#include 

static void _run_one_test(_mongocrypt_tester_t *tester, bson_t *test) {
    bson_iter_t iter;
    bson_t input;
    char *input_str;
    mongocrypt_status_t *status;
    _mongocrypt_kek_t kek;
    const char *expect;
    bool ret;
    bson_t out;
    const char *expect_append;

    status = mongocrypt_status_new();
    memset(&kek, 0, sizeof(_mongocrypt_kek_t));
    BSON_ASSERT(bson_iter_init_find(&iter, test, "input"));
    bson_iter_bson(&iter, &input);
    BSON_ASSERT(bson_iter_init_find(&iter, test, "expect"));
    expect = bson_iter_utf8(&iter, NULL);

    /* Tests may include an optional different expectation for calling
     * _mongocrypt_kek_append. */
    if (bson_iter_init_find(&iter, test, "expect_append")) {
        expect_append = bson_iter_utf8(&iter, NULL);
    } else {
        expect_append = expect;
    }

    input_str = bson_as_relaxed_extended_json(&input, NULL);
    TEST_PRINTF("- testcase: %s\n", input_str);
    bson_free(input_str);

    ret = _mongocrypt_kek_parse_owned(&input, &kek, status);
    if (0 == strcmp(expect, "ok")) {
        ASSERT_OK_STATUS(ret, status);
        bson_init(&out);
        ret = _mongocrypt_kek_append(&kek, &out, status);
        if (0 == strcmp(expect_append, "ok")) {
            _mongocrypt_kek_t kek_copy;

            ASSERT_OK_STATUS(ret, status);
            /* This should round trip. */
            _assert_match_bson(&out, &input);

            /* Check that copy works as well. */
            bson_reinit(&out);
            _mongocrypt_kek_copy_to(&kek, &kek_copy);
            ret = _mongocrypt_kek_append(&kek_copy, &out, status);
            ASSERT_OK_STATUS(ret, status);
            _assert_match_bson(&out, &input);
            _mongocrypt_kek_cleanup(&kek_copy);
        } else {
            ASSERT_FAILS_STATUS(ret, status, expect_append);
        }

        bson_destroy(&out);
    } else {
        ASSERT_FAILS_STATUS(ret, status, expect);
    }

    _mongocrypt_kek_cleanup(&kek);
    mongocrypt_status_destroy(status);
}

static void test_mongocrypt_kek_parsing(_mongocrypt_tester_t *tester) {
    bson_t test_file;
    bson_iter_t iter;

    _load_json_as_bson("./test/data/kek-tests.json", &test_file);
    for (bson_iter_init(&iter, &test_file); bson_iter_next(&iter);) {
        bson_t test;

        bson_iter_bson(&iter, &test);
        _run_one_test(tester, &test);
        bson_destroy(&test);
    }
    bson_destroy(&test_file);
}

void _mongocrypt_tester_install_kek(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_kek_parsing);
}
libmongocrypt-1.19.0/test/test-mongocrypt-key-broker.c000066400000000000000000001307741521103432300230510ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-key-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt.h"

static void _key_broker_add_name(_mongocrypt_key_broker_t *kb, char *string) {
    bson_value_t key_name;

    _bson_value_from_string(string, &key_name);
    ASSERT_OK(_mongocrypt_key_broker_request_name(kb, (void *)&key_name), kb);
    bson_value_destroy(&key_name);
}

/* Create an example 16 byte UUID. Use first_byte to distinguish. */
static void _gen_uuid(uint8_t first_byte, _mongocrypt_buffer_t *out) {
    _mongocrypt_tester_fill_buffer(out, 16);
    out->subtype = BSON_SUBTYPE_UUID;
    out->data[0] = first_byte;
}

/* Create an example 16 byte UUID and a corresponding key document with the same
 * _id as the UUID. */
static void _gen_uuid_and_key_and_altname(_mongocrypt_tester_t *tester,
                                          char *altname,
                                          uint8_t first_byte,
                                          _mongocrypt_buffer_t *id,
                                          _mongocrypt_buffer_t *doc) {
    bson_t as_bson, copied;

    _gen_uuid(first_byte, id);
    BSON_ASSERT(_mongocrypt_binary_to_bson(TEST_FILE("./test/example/key-document.json"), &as_bson));
    bson_init(&copied);
    bson_copy_to_excluding_noinit(&as_bson, &copied, "_id", "keyAltNames", NULL);
    BSON_ASSERT(_mongocrypt_buffer_append(id, &copied, "_id", 3));
    if (altname) {
        bson_t child;
        bson_append_array_unsafe_begin(&copied, "keyAltNames", -1, &child);
        bson_append_utf8(&child, "0", -1, altname, -1);
        bson_append_array_end(&copied, &child);
    }
    _mongocrypt_buffer_steal_from_bson(doc, &copied);
}

static void _gen_uuid_and_key(_mongocrypt_tester_t *tester,
                              uint8_t first_byte,
                              _mongocrypt_buffer_t *id,
                              _mongocrypt_buffer_t *doc) {
    _gen_uuid_and_key_and_altname(tester, NULL, first_byte, id, doc);
}

static uint32_t _key_broker_num_satisfied(_mongocrypt_key_broker_t *kb) {
    key_request_t *req;
    uint32_t count;

    count = 0;
    for (req = kb->key_requests; NULL != req; req = req->next) {
        if (req->satisfied) {
            count++;
        }
    }
    return count;
}

static void _test_key_broker_get_key_filter(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id1, key_id2;
    mongocrypt_binary_t *filter;
    _mongocrypt_key_broker_t key_broker;
    bson_t as_bson;
    bson_t *expected;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    _gen_uuid(1, &key_id1);
    _gen_uuid(2, &key_id2);

    /* Multiple different key ids. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id2), &key_broker);
    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker);
    BSON_ASSERT(_mongocrypt_binary_to_bson(filter, &as_bson));

    expected = BCON_NEW("$or",
                        "[",
                        "{",
                        "_id",
                        "{",
                        "$in",
                        "[",
                        BCON_BIN(BSON_SUBTYPE_UUID, key_id2.data, key_id2.len),
                        BCON_BIN(BSON_SUBTYPE_UUID, key_id1.data, key_id1.len),
                        "]",
                        "}",
                        "}",
                        "{",
                        "keyAltNames",
                        "{",
                        "$in",
                        "[",
                        "]",
                        "}",
                        "}",
                        "]");

    BSON_ASSERT(0 == bson_compare(expected, &as_bson));
    bson_destroy(expected);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_binary_destroy(filter);

    /* Duplicate key ids. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker);
    BSON_ASSERT(_mongocrypt_binary_to_bson(filter, &as_bson));

    expected = BCON_NEW("$or",
                        "[",
                        "{",
                        "_id",
                        "{",
                        "$in",
                        "[",
                        BCON_BIN(BSON_SUBTYPE_UUID, key_id1.data, key_id1.len),
                        "]",
                        "}",
                        "}",
                        "{",
                        "keyAltNames",
                        "{",
                        "$in",
                        "[",
                        "]",
                        "}",
                        "}",
                        "]");

    BSON_ASSERT(0 == bson_compare(expected, &as_bson));
    bson_destroy(expected);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_binary_destroy(filter);

    /* No key requests made. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    filter = mongocrypt_binary_new();
    ASSERT_FAILS(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker, "in wrong state");
    mongocrypt_binary_destroy(filter);
    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Both key ids and keyAltName */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    _key_broker_add_name(&key_broker, "Miriam");
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker);
    BSON_ASSERT(_mongocrypt_binary_to_bson(filter, &as_bson));

    expected = BCON_NEW("$or",
                        "[",
                        "{",
                        "_id",
                        "{",
                        "$in",
                        "[",
                        BCON_BIN(BSON_SUBTYPE_UUID, key_id1.data, key_id1.len),
                        "]",
                        "}",
                        "}",
                        "{",
                        "keyAltNames",
                        "{",
                        "$in",
                        "[",
                        BCON_UTF8("Miriam"),
                        "]",
                        "}",
                        "}",
                        "]");

    BSON_ASSERT(0 == bson_compare(expected, &as_bson));
    bson_destroy(expected);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_binary_destroy(filter);

    /* Keys with only keyAltName */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    _key_broker_add_name(&key_broker, "Sharlene");
    _key_broker_add_name(&key_broker, "Emily");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker);
    BSON_ASSERT(_mongocrypt_binary_to_bson(filter, &as_bson));

    expected = BCON_NEW("$or",
                        "[",
                        "{",
                        "_id",
                        "{",
                        "$in",
                        "[",
                        "]",
                        "}",
                        "}",
                        "{",
                        "keyAltNames",
                        "{",
                        "$in",
                        "[",
                        BCON_UTF8("Emily"),
                        BCON_UTF8("Sharlene"),
                        "]",
                        "}",
                        "}",
                        "]");

    BSON_ASSERT(0 == bson_compare(expected, &as_bson));
    bson_destroy(expected);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_binary_destroy(filter);

    /* Duplicate alt names */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    _key_broker_add_name(&key_broker, "Jackie");
    _key_broker_add_name(&key_broker, "Jackie");
    _key_broker_add_name(&key_broker, "Jackie");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&key_broker, filter), &key_broker);
    BSON_ASSERT(_mongocrypt_binary_to_bson(filter, &as_bson));

    expected = BCON_NEW("$or",
                        "[",
                        "{",
                        "_id",
                        "{",
                        "$in",
                        "[",
                        "]",
                        "}",
                        "}",
                        "{",
                        "keyAltNames",
                        "{",
                        "$in",
                        "[",
                        BCON_UTF8("Jackie"),
                        "]",
                        "}",
                        "}",
                        "]");

    BSON_ASSERT(0 == bson_compare(expected, &as_bson));
    bson_destroy(expected);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_binary_destroy(filter);

    _mongocrypt_buffer_cleanup(&key_id1);
    _mongocrypt_buffer_cleanup(&key_id2);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_broker_add_key(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id1, key_id2, key_doc1, key_doc2, malformed_buf, key_buf_x, key_buf_y, key_doc_names;
    _mongocrypt_buffer_t *id_x;
    _mongocrypt_key_doc_t *key_x;
    bson_t key_bson_x;
    bson_t *malformed;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    _mongocrypt_key_broker_t key_broker;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _gen_uuid_and_key(tester, 1, &key_id1, &key_doc1);
    _gen_uuid_and_key(tester, 2, &key_id2, &key_doc2);

    /* Valid key documents. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id2), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc2), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&key_broker), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Valid document with a key name. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    _key_broker_add_name(&key_broker, "Kasey");
    _mongocrypt_buffer_from_binary(&key_doc_names, TEST_FILE("./test/data/key-document-with-alt-name.json"));
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc_names), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Malformed key document. */
    malformed = BCON_NEW("abc", BCON_INT32(123));
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    _mongocrypt_buffer_from_bson(&malformed_buf, malformed);
    ASSERT_FAILS(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &malformed_buf),
                 &key_broker,
                 "unrecognized field");
    _mongocrypt_key_broker_cleanup(&key_broker);
    bson_destroy(malformed);

    /* NULL key document. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    BSON_ASSERT(_mongocrypt_key_broker_request_id(&key_broker, &key_id1));
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_FAILS(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, NULL), &key_broker, "invalid key");
    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Unmatched key document. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_FAILS(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc2),
                 &key_broker,
                 "unexpected key returned");
    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Two key documents with the same keyAltName and
       different key ids. In order to test this, we need
       to add a name X and an id Y to the broker, then
       add a document with name X and id Z (succeeds) and
       afterwards add a doc with name X and id Y (fails). */
    key_x = _mongocrypt_key_new();
    _mongocrypt_key_broker_init(&key_broker, crypt);

    _mongocrypt_buffer_from_binary(&key_buf_x, TEST_FILE("./test/data/key-document-with-alt-name.json"));
    _mongocrypt_buffer_from_binary(&key_buf_y, TEST_FILE("./test/data/key-document-with-alt-name-duplicate-id.json"));

    BSON_ASSERT(_mongocrypt_buffer_to_bson(&key_buf_x, &key_bson_x));
    ASSERT_OR_PRINT(_mongocrypt_key_parse_owned(&key_bson_x, key_x, status), status);
    id_x = &key_x->id;

    /* Configure the key broker so it contains:
       - { id : X }
       - { name : "Sharlene" } */
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, id_x), &key_broker);
    _key_broker_add_name(&key_broker, "Sharlene");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);

    /* Add { id : Y, name : "Sharlene" }, should pass. */
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_buf_y), &key_broker);

    /* Add { id : X, name : "Sharlene" }, should fail, it shares an alt name. */
    ASSERT_FAILS(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_buf_x),
                 &key_broker,
                 "duplicate keyAltNames");

    _mongocrypt_key_broker_cleanup(&key_broker);

    /* Calling done before supplying all keys. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);

    bson_destroy(&key_bson_x);
    _mongocrypt_key_destroy(key_x);
    _mongocrypt_buffer_cleanup(&key_doc_names);
    _mongocrypt_buffer_cleanup(&key_id1);
    _mongocrypt_buffer_cleanup(&key_id2);
    _mongocrypt_buffer_cleanup(&key_doc1);
    _mongocrypt_buffer_cleanup(&key_doc2);
    _mongocrypt_buffer_cleanup(&key_buf_x);
    _mongocrypt_buffer_cleanup(&key_buf_y);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_broker_add_decrypted_key(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id1, key_id2, key_doc1, key_doc2, key_doc_names, key_id_names;
    _mongocrypt_key_broker_t key_broker;
    mongocrypt_kms_ctx_t *kms;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    bson_iter_t iter;
    bson_t key_doc_names_bson;

    status = mongocrypt_status_new();
    _gen_uuid_and_key(tester, 1, &key_id1, &key_doc1);
    _gen_uuid_and_key(tester, 2, &key_id2, &key_doc2);

    /* Success. With key ids. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&key_broker, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id2), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc2), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc1), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&key_broker), &key_broker);
    kms = _mongocrypt_key_broker_next_kms(&key_broker);
    BSON_ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    kms = _mongocrypt_key_broker_next_kms(&key_broker);
    BSON_ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    BSON_ASSERT(!_mongocrypt_key_broker_next_kms(&key_broker));
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&key_broker, kms_providers), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_destroy(crypt); /* destroy crypt to reset cache. */

    /* Success. With key alt names. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&key_broker, crypt);
    _key_broker_add_name(&key_broker, "Sharlene");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);

    _mongocrypt_buffer_from_binary(&key_doc_names, TEST_FILE("./test/data/key-document-with-alt-name.json"));
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc_names), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&key_broker), &key_broker);
    kms = _mongocrypt_key_broker_next_kms(&key_broker);
    BSON_ASSERT(kms);

    _mongocrypt_tester_satisfy_kms(tester, kms);
    BSON_ASSERT(!_mongocrypt_key_broker_next_kms(&key_broker));
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&key_broker, kms_providers), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_destroy(crypt); /* destroy crypt to reset cache. */

    /* With both key ids and key alt names, some referring to the same key */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&key_broker, crypt);
    BSON_ASSERT(_mongocrypt_buffer_to_bson(&key_doc_names, &key_doc_names_bson));
    BSON_ASSERT(bson_iter_init_find(&iter, &key_doc_names_bson, "_id"));
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&key_id_names, &iter));
    BSON_ASSERT(key_id_names.subtype == BSON_SUBTYPE_UUID);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&key_broker, &key_id_names), &key_broker);
    _key_broker_add_name(&key_broker, "Sharlene");
    _key_broker_add_name(&key_broker, "Kasey");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc_names), &key_broker);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&key_broker), &key_broker);
    kms = _mongocrypt_key_broker_next_kms(&key_broker);
    BSON_ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    BSON_ASSERT(!_mongocrypt_key_broker_next_kms(&key_broker));
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&key_broker, kms_providers), &key_broker);
    _mongocrypt_key_broker_cleanup(&key_broker);

    bson_destroy(&key_doc_names_bson);
    _mongocrypt_buffer_cleanup(&key_id_names);
    _mongocrypt_buffer_cleanup(&key_id1);
    _mongocrypt_buffer_cleanup(&key_id2);
    _mongocrypt_buffer_cleanup(&key_doc1);
    _mongocrypt_buffer_cleanup(&key_doc2);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_broker_wrong_subtype(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id, key_doc;
    _mongocrypt_key_broker_t key_broker;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    _gen_uuid_and_key(tester, 1, &key_id, &key_doc);

    /* Valid key documents. */
    _mongocrypt_key_broker_init(&key_broker, crypt);
    key_id.subtype = 0;
    ASSERT_FAILS(_mongocrypt_key_broker_request_id(&key_broker, &key_id), &key_broker, "expected UUID");

    _mongocrypt_buffer_cleanup(&key_id);
    _mongocrypt_buffer_cleanup(&key_doc);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_broker_multi_match(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    _mongocrypt_key_broker_t key_broker;
    status = mongocrypt_status_new();
    _mongocrypt_buffer_t key_id1, key_id2, key_doc1, key_doc2;

    _gen_uuid_and_key_and_altname(tester, "alt1", 1, &key_id1, &key_doc1);
    _gen_uuid_and_key_and_altname(tester, "alt2", 2, &key_id2, &key_doc2);

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&key_broker, crypt);

    /* Add two ids and two alt names */
    BSON_ASSERT(_mongocrypt_key_broker_request_id(&key_broker, &key_id1));
    _key_broker_add_name(&key_broker, "alt1");
    BSON_ASSERT(_mongocrypt_key_broker_request_id(&key_broker, &key_id2));
    _key_broker_add_name(&key_broker, "alt2");
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&key_broker), &key_broker);

    /* Should be zero satisfied */
    BSON_ASSERT(0 == _key_broker_num_satisfied(&key_broker));

    /* Add one doc, should satisfy two requests. */
    BSON_ASSERT(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc1));
    BSON_ASSERT(2 == _key_broker_num_satisfied(&key_broker));

    /* Add other doc, should satisfy all. */
    BSON_ASSERT(_mongocrypt_key_broker_add_doc(&key_broker, kms_providers, &key_doc2));
    BSON_ASSERT(4 == _key_broker_num_satisfied(&key_broker));

    _mongocrypt_buffer_cleanup(&key_id1);
    _mongocrypt_buffer_cleanup(&key_doc1);
    _mongocrypt_buffer_cleanup(&key_id2);
    _mongocrypt_buffer_cleanup(&key_doc2);
    _mongocrypt_key_broker_cleanup(&key_broker);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

/*

 
  
   
   
  
  
 
 
  
  
   
  
 

*/
static const uint8_t EXPECTED_GET_REQUEST[] = {
    0x42, 0x00, 0x78, 0x01, 0x00, 0x00, 0x00, 0x88, 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, 0x40, 0x42, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x79, 0x01, 0x00, 0x00, 0x00, 0x28, 0x42, 0x00, 0x94, 0x07,
    0x00, 0x00, 0x00, 0x20, 0x79, 0x77, 0x78, 0x72, 0x53, 0x6a, 0x35, 0x54, 0x4c, 0x6a, 0x73, 0x77, 0x64, 0x31,
    0x47, 0x34, 0x6f, 0x47, 0x46, 0x4a, 0x36, 0x68, 0x77, 0x57, 0x67, 0x74, 0x54, 0x73, 0x51, 0x69, 0x70, 0x30};

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
   
   
    
    
     
     
      
     
    
   
  
 

*/
static const uint8_t SUCCESS_GET_RESPONSE[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x01, 0x58, 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, 0x59, 0xea, 0xe8, 0x42, 0x00, 0x0d, 0x02,
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x01,
    0x00, 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, 0xd8, 0x42, 0x00, 0x57, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x94, 0x07, 0x00, 0x00, 0x00, 0x20, 0x79, 0x77, 0x78, 0x72, 0x53, 0x6a, 0x35, 0x54, 0x4c, 0x6a, 0x73,
    0x77, 0x64, 0x31, 0x47, 0x34, 0x6f, 0x47, 0x46, 0x4a, 0x36, 0x68, 0x77, 0x57, 0x67, 0x74, 0x54, 0x73, 0x51, 0x69,
    0x70, 0x30, 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, 0x0c, 0x2e, 0xa7, 0x29, 0x71, 0x80, 0xf8, 0x2a, 0x98, 0x4b,
    0x2f, 0xd4, 0x7d, 0x63, 0x27, 0xce, 0x22, 0x6f, 0x62, 0xe9, 0x01, 0x7b, 0x91, 0xdc, 0x6e, 0x5d, 0x6d, 0xfd, 0x98,
    0x74, 0x7d, 0x97, 0xe8, 0x9f, 0x17, 0xbf, 0x09, 0x26, 0xcf, 0xcc, 0x0a, 0xfb, 0x24, 0xe6, 0x9b, 0x7c, 0x00, 0x12,
    0x1d, 0xda, 0x12, 0xd0, 0x15, 0x8c, 0x43, 0x75, 0xc3, 0x10, 0x84, 0xab, 0xf7, 0xf2, 0xe6, 0x04, 0x4e, 0xdc, 0x2f,
    0x92, 0x80, 0x2b, 0xa3, 0xf6, 0x76, 0xd4, 0x70, 0xd2, 0xcb, 0xc4, 0xe3, 0x3a, 0x2a, 0x8e, 0x53, 0xdc, 0xed, 0x78,
    0x28, 0xdd, 0x8a, 0x35, 0xf2, 0x68, 0x43, 0x7f, 0xf1, 0x41};

static const uint8_t EXPECTED_SECRETDATA[] = {
    0x94, 0x82, 0x4f, 0x44, 0xbe, 0xb2, 0x20, 0x73, 0x14, 0xad, 0x8b, 0x36, 0x38, 0xaf, 0x01, 0x45,
    0xa5, 0x13, 0x80, 0x84, 0x44, 0x57, 0xdf, 0xde, 0x9f, 0xb6, 0x7b, 0xfb, 0xf9, 0x21, 0xf9, 0x00,
    0xb2, 0x00, 0x9e, 0x07, 0xcf, 0x04, 0xc3, 0x5b, 0x9a, 0x98, 0x3b, 0xa9, 0x22, 0x83, 0x3d, 0x7a,
    0x07, 0xc5, 0x90, 0x84, 0xe7, 0x63, 0xf0, 0x47, 0xf0, 0x1a, 0x4b, 0xfe, 0x03, 0xbc, 0xe3, 0x82,
    0x96, 0x95, 0x88, 0xb8, 0x18, 0x63, 0x33, 0x15, 0x73, 0x95, 0xe2, 0xb1, 0x38, 0xde, 0x6c, 0x13,
    0xf8, 0x98, 0x43, 0xbe, 0x3f, 0x85, 0x83, 0xd0, 0x11, 0x88, 0xb8, 0x0f, 0xb5, 0x8c, 0x2a, 0x1c};

static void _test_key_broker_kmip(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_key_broker_t kb;
    bson_t keydoc_bson;
    bson_iter_t iter;
    _mongocrypt_buffer_t id;
    _mongocrypt_buffer_t keydoc;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_binary_t *msg;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    _mongocrypt_buffer_t secretdata;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    status = mongocrypt_status_new();
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&kb, crypt);
    _load_json_as_bson("./test/data/key-document-kmip.json", &keydoc_bson);

    ASSERT_OR_PRINT_MSG(bson_iter_init_find(&iter, &keydoc_bson, "_id"),
                        "could not find _id in key-document-kmip.json");
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&id, &iter));
    ASSERT_OK(_mongocrypt_key_broker_request_id(&kb, &id), &kb);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&kb), &kb);

    /* Add the key document. */
    _mongocrypt_buffer_from_bson(&keydoc, &keydoc_bson);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &keydoc), &kb);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);

    /* There should be exactly one KMS request for KMIP. */
    kms = _mongocrypt_key_broker_next_kms(&kb);
    ASSERT_OR_PRINT_MSG(kms, "expected KMS context returned, got none");

    msg = mongocrypt_binary_new();
    mongocrypt_kms_ctx_message(kms, msg);
    ASSERT_CMPBYTES(EXPECTED_GET_REQUEST,
                    sizeof(EXPECTED_GET_REQUEST),
                    mongocrypt_binary_data(msg),
                    mongocrypt_binary_len(msg));

    ASSERT_OK(kms_ctx_feed_all(kms, SUCCESS_GET_RESPONSE, sizeof(SUCCESS_GET_RESPONSE)), kms);
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&kb, kms_providers), &kb);

    BSON_ASSERT(_mongocrypt_key_broker_decrypted_key_by_id(&kb, &id, &secretdata));
    ASSERT_CMPBYTES(secretdata.data, secretdata.len, EXPECTED_SECRETDATA, sizeof(EXPECTED_SECRETDATA));

    _mongocrypt_buffer_cleanup(&secretdata);
    mongocrypt_binary_destroy(msg);
    _mongocrypt_buffer_cleanup(&keydoc);
    _mongocrypt_buffer_cleanup(&id);
    bson_destroy(&keydoc_bson);
    _mongocrypt_key_broker_cleanup(&kb);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

/*

 
  
   
   
  
  
  
 
 
  
  
  
  
 

*/
static const uint8_t ERROR_GET_RESPOSE_NOTFOUND[] = {
    0x42, 0x00, 0x7b, 0x01, 0x00, 0x00, 0x00, 0xa8, 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, 0x57, 0x1e, 0x81,
    0x42, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
    0x0f, 0x01, 0x00, 0x00, 0x00, 0x50, 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, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x42, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x42, 0x00, 0x7d, 0x07, 0x00, 0x00, 0x00, 0x18, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, 0x61, 0x73,
    0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x4e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64};

static void _test_key_broker_kmip_notfound(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_key_broker_t kb;
    bson_t keydoc_bson;
    bson_iter_t iter;
    _mongocrypt_buffer_t id;
    _mongocrypt_buffer_t keydoc;
    mongocrypt_kms_ctx_t *kms;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    mongocrypt_binary_t *msg;

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    status = mongocrypt_status_new();
    kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_init(&kb, crypt);
    _load_json_as_bson("./test/data/key-document-kmip.json", &keydoc_bson);

    ASSERT_OR_PRINT_MSG(bson_iter_init_find(&iter, &keydoc_bson, "_id"),
                        "could not find _id in key-document-kmip.json");
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&id, &iter));
    ASSERT_OK(_mongocrypt_key_broker_request_id(&kb, &id), &kb);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&kb), &kb);

    /* Add the key document. */
    _mongocrypt_buffer_from_bson(&keydoc, &keydoc_bson);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &keydoc), &kb);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);

    /* There should be exactly one KMS request for KMIP. */
    kms = _mongocrypt_key_broker_next_kms(&kb);
    ASSERT_OR_PRINT_MSG(kms, "expected KMS context returned, got none");

    msg = mongocrypt_binary_new();
    mongocrypt_kms_ctx_message(kms, msg);
    ASSERT_CMPBYTES(EXPECTED_GET_REQUEST,
                    sizeof(EXPECTED_GET_REQUEST),
                    mongocrypt_binary_data(msg),
                    mongocrypt_binary_len(msg));

    ASSERT_FAILS(kms_ctx_feed_all(kms, ERROR_GET_RESPOSE_NOTFOUND, sizeof(ERROR_GET_RESPOSE_NOTFOUND)),
                 kms,
                 "ResultReasonItemNotFound");

    mongocrypt_binary_destroy(msg);
    _mongocrypt_buffer_cleanup(&keydoc);
    _mongocrypt_buffer_cleanup(&id);
    bson_destroy(&keydoc_bson);
    _mongocrypt_key_broker_cleanup(&kb);
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

static void _test_key_broker_request_any(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    _mongocrypt_key_broker_t kb;

    /* Can switch to any mode before any keys are requested. */
    _mongocrypt_key_broker_init(&kb, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_any(&kb), &kb);
    _mongocrypt_key_broker_cleanup(&kb);

    /* If keys have already been requested, cannot switch to any mode.*/
    _mongocrypt_key_broker_init(&kb, crypt);
    _key_broker_add_name(&kb, "test");
    ASSERT_FAILS(_mongocrypt_key_broker_request_any(&kb),
                 &kb,
                 "attempting to request any keys, but requests already made");
    _mongocrypt_key_broker_cleanup(&kb);

    mongocrypt_destroy(crypt);
}

static void _test_key_broker_add_any(_mongocrypt_tester_t *tester) {
    mongocrypt_t *const crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    _mongocrypt_opts_kms_providers_t *const kms_providers = &crypt->opts.kms_providers;
    _mongocrypt_key_broker_t kb;
    _mongocrypt_buffer_t key_id, key_doc;

    _gen_uuid_and_key(tester, 1, &key_id, &key_doc);

    /* Can add valid documents. */
    _mongocrypt_key_broker_init(&kb, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_any(&kb), &kb);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc), &kb);
    ASSERT(_key_broker_num_satisfied(&kb) == 1);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);
    _mongocrypt_key_broker_cleanup(&kb);

    /* Still validates no duplicate/incompatible keys. */
    _mongocrypt_key_broker_init(&kb, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_any(&kb), &kb);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc), &kb);
    ASSERT(_key_broker_num_satisfied(&kb) == 1);
    ASSERT_FAILS(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc),
                 &kb,
                 "keys returned have duplicate keyAltNames or _id");
    ASSERT(_key_broker_num_satisfied(&kb) == 1);
    _mongocrypt_key_broker_cleanup(&kb);

    /* Still requests KMS as needed. */
    _mongocrypt_key_broker_init(&kb, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_any(&kb), &kb);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc), &kb);
    ASSERT(_key_broker_num_satisfied(&kb) == 1);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);
    {
        mongocrypt_kms_ctx_t *kms;
        kms = _mongocrypt_key_broker_next_kms(&kb);
        ASSERT_OK(kms, &kb);
        /* Key material should should be cached here. */
        _mongocrypt_tester_satisfy_kms(tester, kms);
        ASSERT_OK(!_mongocrypt_key_broker_next_kms(&kb), &kb);
        ASSERT_OK(_mongocrypt_key_broker_kms_done(&kb, kms_providers), &kb);
    }
    _mongocrypt_key_broker_cleanup(&kb);
    _mongocrypt_key_broker_init(&kb, crypt);
    ASSERT_OK(_mongocrypt_key_broker_request_any(&kb), &kb);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc), &kb);
    ASSERT(_key_broker_num_satisfied(&kb) == 1);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);
    /* Cached key material, should skip straight to done. */
    ASSERT(kb.state == KB_DONE);

    _mongocrypt_key_broker_cleanup(&kb);

    _mongocrypt_buffer_cleanup(&key_doc);
    _mongocrypt_buffer_cleanup(&key_id);
    mongocrypt_destroy(crypt);
}

/* assert_filter_requests_id asserts that filter contains exactly one request
 * for a key ID of expect. */
static void assert_filter_requests_id(mongocrypt_binary_t *filter, const _mongocrypt_buffer_t *expect) {
    bson_t filter_bson;
    ASSERT(_mongocrypt_binary_to_bson(filter, &filter_bson));
    bson_iter_t iter;
    ASSERT(bson_iter_init(&iter, &filter_bson));
    ASSERT(bson_iter_find_descendant(&iter, "$or.0._id.$in", &iter));
    ASSERT(bson_iter_recurse(&iter, &iter));
    ASSERT(bson_iter_next(&iter));
    ASSERT(BSON_ITER_HOLDS_BINARY(&iter));

    _mongocrypt_buffer_t actual;
    ASSERT(_mongocrypt_buffer_from_binary_iter(&actual, &iter));
    ASSERT_CMPBUF(*expect, actual);

    /* Check that there are no additional _id's requested. */
    ASSERT(!bson_iter_next(&iter));

    /* Check that there are no keyAltName requests. */
    ASSERT(bson_iter_init(&iter, &filter_bson));
    ASSERT(bson_iter_find_descendant(&iter, "$or.1.keyAltNames.$in", &iter));
    ASSERT(bson_iter_recurse(&iter, &iter));
    ASSERT(!bson_iter_next(&iter));
}

/* Test that key requests can be added again after transitioning to DONE and
 * calling _mongocrypt_key_broker_restart. */
static void _test_key_broker_restart(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id1, key_id2, key_doc1, key_doc2;
    _mongocrypt_key_broker_t kb;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_binary_t *filter;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _gen_uuid_and_key(tester, 1, &key_id1, &key_doc1);
    _gen_uuid_and_key(tester, 2, &key_id2, &key_doc2);
    _mongocrypt_key_broker_init(&kb, crypt);

    ASSERT(kb.state == KB_REQUESTING);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&kb, &key_id1), &kb);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&kb), &kb);

    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&kb, filter), &kb);
    assert_filter_requests_id(filter, &key_id1);
    mongocrypt_binary_destroy(filter);

    ASSERT(kb.state == KB_ADDING_DOCS);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc1), &kb);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);

    ASSERT(kb.state == KB_DECRYPTING_KEY_MATERIAL);
    kms = _mongocrypt_key_broker_next_kms(&kb);
    ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&kb, kms_providers), &kb);

    /* Restart, and add request for key_id2. */
    ASSERT(kb.state == KB_DONE);
    ASSERT_OK(_mongocrypt_key_broker_restart(&kb), &kb);

    ASSERT(kb.state == KB_REQUESTING);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&kb, &key_id2), &kb);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&kb), &kb);

    filter = mongocrypt_binary_new();
    ASSERT_OK(_mongocrypt_key_broker_filter(&kb, filter), &kb);
    assert_filter_requests_id(filter, &key_id2);
    mongocrypt_binary_destroy(filter);

    ASSERT(kb.state == KB_ADDING_DOCS);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc2), &kb);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);

    ASSERT(kb.state == KB_DECRYPTING_KEY_MATERIAL);
    kms = _mongocrypt_key_broker_next_kms(&kb);
    ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&kb, kms_providers), &kb);
    ASSERT(kb.state == KB_DONE);

    _mongocrypt_key_broker_cleanup(&kb);
    _mongocrypt_buffer_cleanup(&key_doc2);
    _mongocrypt_buffer_cleanup(&key_id2);
    _mongocrypt_buffer_cleanup(&key_doc1);
    _mongocrypt_buffer_cleanup(&key_id1);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

/* Test that a decrypted key can be returned while in the KB_REQUESTING state.
 */
static void _test_key_broker_get_decrypted_key_while_requesting(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    _mongocrypt_buffer_t key_id1, key_doc1, key_decrypted1, key_decrypted1_copy;
    _mongocrypt_key_broker_t kb;
    _mongocrypt_opts_kms_providers_t *kms_providers;
    mongocrypt_kms_ctx_t *kms;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    kms_providers = &crypt->opts.kms_providers;
    _gen_uuid_and_key(tester, 1, &key_id1, &key_doc1);
    _mongocrypt_key_broker_init(&kb, crypt);

    ASSERT(kb.state == KB_REQUESTING);
    ASSERT_OK(_mongocrypt_key_broker_request_id(&kb, &key_id1), &kb);
    ASSERT_OK(_mongocrypt_key_broker_requests_done(&kb), &kb);

    ASSERT(kb.state == KB_ADDING_DOCS);
    ASSERT_OK(_mongocrypt_key_broker_add_doc(&kb, kms_providers, &key_doc1), &kb);
    ASSERT_OK(_mongocrypt_key_broker_docs_done(&kb), &kb);

    ASSERT(kb.state == KB_DECRYPTING_KEY_MATERIAL);
    kms = _mongocrypt_key_broker_next_kms(&kb);
    ASSERT(kms);
    _mongocrypt_tester_satisfy_kms(tester, kms);
    ASSERT_OK(_mongocrypt_key_broker_kms_done(&kb, kms_providers), &kb);

    ASSERT(kb.state == KB_DONE);
    ASSERT_OK(_mongocrypt_key_broker_decrypted_key_by_id(&kb, &key_id1, &key_decrypted1), &kb);

    /* Restart. */
    ASSERT_OK(_mongocrypt_key_broker_restart(&kb), &kb);
    ASSERT(kb.state == KB_REQUESTING);
    ASSERT_OK(_mongocrypt_key_broker_decrypted_key_by_id(&kb, &key_id1, &key_decrypted1_copy), &kb);

    ASSERT_CMPBUF(key_decrypted1, key_decrypted1_copy);

    _mongocrypt_buffer_cleanup(&key_decrypted1_copy);
    _mongocrypt_buffer_cleanup(&key_decrypted1);
    _mongocrypt_key_broker_cleanup(&kb);
    _mongocrypt_buffer_cleanup(&key_doc1);
    _mongocrypt_buffer_cleanup(&key_id1);
    mongocrypt_destroy(crypt);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_key_broker(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_key_broker_get_key_filter);
    INSTALL_TEST(_test_key_broker_add_key);
    INSTALL_TEST(_test_key_broker_add_decrypted_key);
    INSTALL_TEST(_test_key_broker_wrong_subtype);
    INSTALL_TEST(_test_key_broker_multi_match);
    INSTALL_TEST(_test_key_broker_kmip);
    INSTALL_TEST(_test_key_broker_kmip_notfound);
    INSTALL_TEST(_test_key_broker_request_any);
    INSTALL_TEST(_test_key_broker_add_any);
    INSTALL_TEST(_test_key_broker_restart);
    INSTALL_TEST(_test_key_broker_get_decrypted_key_while_requesting);
}
libmongocrypt-1.19.0/test/test-mongocrypt-key-cache.c000066400000000000000000000372741521103432300226310ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-cache-key-private.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"

typedef struct {
    _mongocrypt_buffer_t bson;
    _mongocrypt_key_doc_t *parsed;
    _mongocrypt_buffer_t kms_reply;
    _mongocrypt_buffer_t uuid;
    _mongocrypt_buffer_t marking;
} gen_key_t;

/* The JSON spec tests refer to key ids by a shorthand integer.
 * This function maps that integer to a UUID buffer. */
static void lookup_key_id(uint32_t index, _mongocrypt_buffer_t *buf) {
    const char *key_ids[] = {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                             "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
                             "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"};

    BSON_ASSERT(index < 3);
    _mongocrypt_buffer_copy_from_hex(buf, key_ids[index]);
    buf->subtype = BSON_SUBTYPE_UUID;
    BSON_ASSERT(_mongocrypt_buffer_is_uuid(buf));
}

/* Generate a realistic key document given a @key_description, which contains an
 * _id
 * and possible keyAltNames. key_out and key_doc_out are NULLable outputs. */
static void
gen_key(_mongocrypt_tester_t *tester, bson_t *key_description, bson_t *key_out, _mongocrypt_key_doc_t *key_doc_out) {
    bson_iter_t iter;
    _mongocrypt_buffer_t key_material;
    _mongocrypt_buffer_t key_id;
    bson_t key, masterkey;
    bool local_kms;

    bson_init(&key);
    BSON_APPEND_INT32(&key, "status", 1);
    BSON_APPEND_DATE_TIME(&key, "updateDate", 1234567890);
    BSON_APPEND_DATE_TIME(&key, "creationDate", 1234567890);
    BSON_APPEND_DOCUMENT_BEGIN(&key, "masterKey", &masterkey);

    local_kms = bson_iter_init_find(&iter, key_description, "local") && bson_iter_as_bool(&iter);

    if (local_kms) {
        BSON_APPEND_UTF8(&masterkey, "provider", "local");
    } else {
        BSON_APPEND_UTF8(&masterkey, "provider", "aws");
        BSON_APPEND_UTF8(&masterkey, "region", "us-east-1");
        BSON_APPEND_UTF8(&masterkey,
                         "key",
                         "arn:aws:kms:us-east-1:579766882180:key/"
                         "89fcc2c4-08b0-4bd9-9f25-e30687b580d0");
    }
    bson_append_document_end(&key, &masterkey);

    BSON_ASSERT(bson_iter_init_find(&iter, key_description, "_id"));
    lookup_key_id(bson_iter_int32(&iter), &key_id);
    BSON_ASSERT(_mongocrypt_buffer_append(&key_id, &key, "_id", 3));

    if (bson_iter_init_find(&iter, key_description, "keyAltNames")) {
        bson_t key_alt_name_bson;
        int counter = 0;

        BSON_APPEND_ARRAY_UNSAFE_BEGIN(&key, "keyAltNames", &key_alt_name_bson);
        for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) {
            char *field;

            field = bson_strdup_printf("%d", counter);
            BSON_APPEND_UTF8(&key_alt_name_bson, field, bson_iter_utf8(&iter, NULL));
            counter++;
            bson_free(field);
        }
        bson_append_array_end(&key, &key_alt_name_bson);
    }

    /* Append a keyMaterial that is decryptable by the local KMS masterkey. For
     * AWS it gets ignored since it is dictated by KMS response. */
    _mongocrypt_buffer_copy_from_hex(&key_material,
                                     "75bdbbaec862a8ae09aa16f6c67c0ae117dd15bf49b8a7947bac6de5a"
                                     "610178a3adad4bbe5bec1e30c55378f7d80d0fd5152d46e954aa32528"
                                     "69901e03cf7938434fdf7e5bf27f0ec1c85c4c5a92e38b7e3f7ce686d"
                                     "7985102c85905da220a27ee01202de25b6831e64974baffb35b7c30c5"
                                     "941dfb37b04fff6871d7208e4cde8d1bff0cd69a70dcb613dc27cfe84"
                                     "7d7544b6d0d8b4f6c9a5b6fb9c1565c43ef");
    key_material.subtype = BSON_SUBTYPE_BINARY;
    BSON_ASSERT(_mongocrypt_buffer_append(&key_material, &key, "keyMaterial", -1));

    if (key_out) {
        bson_copy_to(&key, key_out);
    }

    if (key_doc_out) {
        mongocrypt_status_t *status;

        status = mongocrypt_status_new();
        ASSERT_OK_STATUS(_mongocrypt_key_parse_owned(&key, key_doc_out, status), status);
        mongocrypt_status_destroy(status);
    }

    bson_destroy(&key);
    _mongocrypt_buffer_cleanup(&key_id);
    _mongocrypt_buffer_cleanup(&key_material);
}

/* Append a realistic subtype 6 marking to a BSON document given a description
 * of the requested key, which may contain either an _id or single keyAltName.
 */
static void _append_marking(bson_t *appendee, const char *key, const bson_t *request) {
    /* Create a marking with a fixed value "v": 1 */
    bson_t marking_bson;
    _mongocrypt_buffer_t marking_buf;
    bson_iter_t iter;

    bson_init(&marking_bson);

    if (bson_iter_init_find(&iter, request, "_id")) {
        _mongocrypt_buffer_t key_id;

        lookup_key_id(bson_iter_int32(&iter), &key_id);
        BSON_ASSERT(_mongocrypt_buffer_append(&key_id, &marking_bson, "ki", 2));
        _mongocrypt_buffer_cleanup(&key_id);
    } else {
        /* If no _id, then keyAltName must be specified. */
        BSON_ASSERT(bson_iter_init_find(&iter, request, "keyAltName"));
        BSON_APPEND_UTF8(&marking_bson, "ka", bson_iter_utf8(&iter, NULL));
    }

    /* Append an arbitrary value and algorithm. It won't be checked in the tests.
     */
    BSON_APPEND_INT32(&marking_bson, "v", 123);
    BSON_APPEND_INT32(&marking_bson, "a", MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC);

    /* Append the prefix 0 byte, per the marking binary format. */
    _mongocrypt_buffer_init(&marking_buf);
    _mongocrypt_buffer_resize(&marking_buf, marking_bson.len + 1);
    marking_buf.data[0] = 0;
    memcpy(marking_buf.data + 1, bson_get_data(&marking_bson), marking_bson.len);
    marking_buf.subtype = 6;
    BSON_ASSERT(_mongocrypt_buffer_append(&marking_buf, appendee, key, -1));

    bson_destroy(&marking_bson);
    _mongocrypt_buffer_cleanup(&marking_buf);
}

/* Manually add a cache entry given a description (_id and possible keyAltNames)
 */
static void _add_to_cache(_mongocrypt_tester_t *tester, mongocrypt_ctx_t *ctx, bson_t *cache_entry) {
    bson_iter_t iter;
    _mongocrypt_buffer_t key_id;
    _mongocrypt_key_alt_name_t *key_alt_names = NULL;
    _mongocrypt_cache_key_attr_t *cache_key_attr;
    _mongocrypt_cache_key_value_t *cache_key_value;
    _mongocrypt_key_doc_t *key_doc;
    _mongocrypt_buffer_t key_material_placeholder;
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    BSON_ASSERT(bson_iter_init_find(&iter, cache_entry, "_id"));
    lookup_key_id(bson_iter_int32(&iter), &key_id);

    if (bson_iter_init_find(&iter, cache_entry, "keyAltNames")) {
        ASSERT_OK_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status);
    }

    cache_key_attr = _mongocrypt_cache_key_attr_new(&key_id, key_alt_names);
    /* TODO: consider improving these tests by identifying the decrypted and
     * encrypted key material. That is a little tricky, since it will require
     * parsing the KMS request to know which decrypted key material to respond
     * with. */
    _mongocrypt_buffer_init(&key_material_placeholder);
    _mongocrypt_buffer_resize(&key_material_placeholder, MONGOCRYPT_KEY_LEN);
    memset(key_material_placeholder.data, 0, MONGOCRYPT_KEY_LEN);

    key_doc = _mongocrypt_key_new();
    gen_key(tester, cache_entry, NULL, key_doc);

    cache_key_value = _mongocrypt_cache_key_value_new(key_doc, &key_material_placeholder);

    ASSERT_OK_STATUS(_mongocrypt_cache_add_copy(&ctx->crypt->cache_key, cache_key_attr, cache_key_value, status),
                     status);

    _mongocrypt_key_destroy(key_doc);
    _mongocrypt_buffer_cleanup(&key_material_placeholder);
    _mongocrypt_cache_key_attr_destroy(cache_key_attr);
    _mongocrypt_cache_key_value_destroy(cache_key_value);
    _mongocrypt_key_alt_name_destroy_all(key_alt_names);
    _mongocrypt_buffer_cleanup(&key_id);
    mongocrypt_status_destroy(status);
}

/* Match a single cache entry against an expectation document (containing _id
 * and
 * possible list of keyAltNames)
 */
static bool _match_one_cache_entry(_mongocrypt_cache_pair_t *pair, bson_t *expected_entry) {
    bson_iter_t iter;
    _mongocrypt_buffer_t key_id;
    _mongocrypt_key_alt_name_t *key_alt_names = NULL;
    _mongocrypt_cache_key_attr_t *attr;
    mongocrypt_status_t *status;
    bool matched = false;

    _mongocrypt_buffer_init(&key_id);
    attr = pair->attr;
    status = mongocrypt_status_new();

    bson_iter_init_find(&iter, expected_entry, "_id");
    lookup_key_id(bson_iter_int32(&iter), &key_id);
    if (0 != _mongocrypt_buffer_cmp(&key_id, &attr->id)) {
        goto done;
    }

    if (bson_iter_init_find(&iter, expected_entry, "keyAltNames")) {
        ASSERT_OK_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status);
    }

    if (!_mongocrypt_key_alt_name_unique_list_equal(key_alt_names, attr->alt_names)) {
        TEST_PRINTF("failed to match key alt names\n");
        goto done;
    }

    matched = true;

done:
    _mongocrypt_buffer_cleanup(&key_id);
    _mongocrypt_key_alt_name_destroy_all(key_alt_names);
    mongocrypt_status_destroy(status);
    return matched;
}

/* Find exactly one cache entry matching expected_entry.
 * TODO: Instead of reaching inside the cache to make these checks, I think a
 * better approach would be to have the cache dump as BSON, then check
 * against that BSON with bson matching functions. Right now, if/when we modify
 * the cache structure, we'll need to update this test logic.
 * Alternatively, another solution would be to move part of this logic inside
 * the key cache and call it from the test.  Maybe then the method would look
 * like a "count" method instead of a "return true iff there is a single match"
 * method.  We could also use a "count" method to assert no matches, etc.
 */
static void _match_cache_entry(_mongocrypt_tester_t *tester, mongocrypt_ctx_t *ctx, bson_t *expected_entry) {
    _mongocrypt_cache_pair_t *pair;
    bool matched = false;

    pair = ctx->crypt->cache_key.pair;

    while (pair) {
        if (_match_one_cache_entry(pair, expected_entry)) {
            if (matched) {
                TEST_PRINTF("double matched entry: %s\n", bson_as_relaxed_extended_json(expected_entry, NULL));
                BSON_ASSERT(false);
            }
            matched = true;
        }

        pair = pair->next;
    }

    if (!matched) {
        TEST_PRINTF("could not match entry: %s\n", bson_as_relaxed_extended_json(expected_entry, NULL));
        BSON_ASSERT(false);
    }
}

static void _run_one_test(_mongocrypt_tester_t *tester, mongocrypt_ctx_t *ctx, bson_t *test) {
    bson_iter_t iter;
    bson_t mongocryptd_reply, tmp;
    _mongocrypt_buffer_t buf;
    int32_t counter;
    mongocrypt_status_t *status;

    if (bson_iter_init_find(&iter, test, "description")) {
        TEST_PRINTF("- %s\n", bson_iter_utf8(&iter, NULL));
    }

    if (bson_iter_init_find(&iter, test, "skipReason")) {
        TEST_PRINTF("  - skipping: %s\n", bson_iter_utf8(&iter, NULL));
        return;
    }

    status = mongocrypt_status_new();

    /* Set up cache */
    if (bson_iter_init_find(&iter, test, "cached")) {
        for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) {
            bson_t cache_entry;

            bson_iter_bson(&iter, &cache_entry);
            _add_to_cache(tester, ctx, &cache_entry);
        }
    }

    /* Supply the requests for keys through the mongocryptd reply */
    BSON_ASSERT(bson_iter_init_find(&iter, test, "requests"));
    counter = 0;
    bson_init(&mongocryptd_reply);
    BSON_APPEND_DOCUMENT_BEGIN(&mongocryptd_reply, "result", &tmp);
    for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) {
        bson_t request;
        char *field;

        bson_iter_bson(&iter, &request);
        field = bson_strdup_printf("%d", counter);
        _append_marking(&tmp, field, &request);
        bson_free(field);
        counter++;
    }
    bson_append_document_end(&mongocryptd_reply, &tmp);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_MARKINGS);
    _mongocrypt_buffer_from_bson(&buf, &mongocryptd_reply);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&buf)), ctx);
    BSON_ASSERT(mongocrypt_ctx_mongo_done(ctx));

    /* If we're expected to supply keys back, do so. */
    if (bson_iter_init_find(&iter, test, "replies")) {
        BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_NEED_MONGO_KEYS);

        for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) {
            bson_t key;
            bson_t key_description;

            bson_iter_bson(&iter, &key_description);
            gen_key(tester, &key_description, &key, NULL);
            _mongocrypt_buffer_from_bson(&buf, &key);
            /* If expectations expect failure, fall through. */
            if (!mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&buf))) {
                bson_destroy(&key);
                break;
            }
            bson_destroy(&key);
        }

        /* We might have failed at this point. If so, do not continue so we keep
         * original error message. */
        if (mongocrypt_ctx_status(ctx, status)) {
            (void)mongocrypt_ctx_mongo_done(ctx);
        }
    }

    /* Check expectations */
    if (bson_iter_init_find(&iter, test, "expect")) {
        bson_t expectations;

        mongocrypt_ctx_status(ctx, status);
        bson_iter_bson(&iter, &expectations);
        if (bson_iter_init_find(&iter, &expectations, "errmsg")) {
            _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_ERROR);
            ASSERT_FAILS_STATUS(mongocrypt_status_ok(status), status, bson_iter_utf8(&iter, NULL));
        } else {
            _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_DONE);
            ASSERT_OK_STATUS(mongocrypt_status_ok(status), status);
        }

        if (bson_iter_init_find(&iter, &expectations, "cached")) {
            uint32_t count = 0;

            for (bson_iter_recurse(&iter, &iter); bson_iter_next(&iter);) {
                bson_t entry_description;

                bson_iter_bson(&iter, &entry_description);
                _match_cache_entry(tester, ctx, &entry_description);

                count++;
            }
            BSON_ASSERT(count == _mongocrypt_cache_num_entries(&ctx->crypt->cache_key));
        }
    }

    bson_destroy(&mongocryptd_reply);
    mongocrypt_status_destroy(status);
}

/* Run declarative JSON tests, like driver spec tests. */
static void _test_key_cache(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    bson_t test_file;
    bson_iter_t iter;

    _load_json_as_bson("./test/data/cache-tests.json", &test_file);
    for (bson_iter_init(&iter, &test_file); bson_iter_next(&iter);) {
        bson_t test;
        crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        ctx = mongocrypt_ctx_new(crypt);

        bson_iter_bson(&iter, &test);

        BSON_ASSERT(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_BSON("{'insert': 'test'}")));
        _run_one_test(tester, ctx, &test);
        bson_destroy(&test);

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
    bson_destroy(&test_file);
}

void _mongocrypt_tester_install_key_cache(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_key_cache);
}
libmongocrypt-1.19.0/test/test-mongocrypt-key.c000066400000000000000000000242051521103432300215560ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* For each field, check a valid value, invalid value, missing value */

#include "bson/bson.h"
#include "test-mongocrypt.h"

/* Create a basis key document, but exclude some fields. */
static void _recreate_excluding(_mongocrypt_tester_t *tester, bson_t *out, va_list args) {
    bson_t tmp;

    BSON_ASSERT(_mongocrypt_binary_to_bson(TEST_FILE("./test/data/key-document-full.json"), &tmp));

    /* copy to out */
    bson_destroy(out);
    bson_init(out);
    bson_copy_to_excluding_noinit_va(&tmp, out, "", args);
}

static void _recreate_and_reset(_mongocrypt_tester_t *tester, bson_t *key_bson, mongocrypt_status_t *status, ...) {
    va_list args;

    va_start(args, status);
    _recreate_excluding(tester, key_bson, args);
    va_end(args);
    _mongocrypt_status_reset(status);
}

static void _parse_ok(bson_t *key_bson, mongocrypt_status_t *status) {
    _mongocrypt_key_doc_t *key_doc = _mongocrypt_key_new();
    ASSERT_OK_STATUS(_mongocrypt_key_parse_owned(key_bson, key_doc, status), status);
    _mongocrypt_key_destroy(key_doc);
}

static void _parse_fails(bson_t *key_bson, mongocrypt_status_t *status, const char *msg) {
    _mongocrypt_key_doc_t *key_doc = _mongocrypt_key_new();

    ASSERT_FAILS_STATUS(_mongocrypt_key_parse_owned(key_bson, key_doc, status), status, msg);
    _mongocrypt_key_destroy(key_doc);
}

static void test_mongocrypt_key_parsing(_mongocrypt_tester_t *tester) {
    bson_t key_bson = BSON_INITIALIZER;
    mongocrypt_status_t *status;
    mongocrypt_binary_t *uuid;

    uuid = TEST_BIN(16);
    status = mongocrypt_status_new();

    /* successful case. */
    _recreate_and_reset(tester, &key_bson, status, NULL);
    _parse_ok(&key_bson, status);

    /* unrecognized fields */
    _recreate_and_reset(tester, &key_bson, status, NULL);
    BSON_APPEND_INT32(&key_bson, "invalid", 123);
    _parse_fails(&key_bson, status, "unrecognized field 'invalid'");

    /* malformed BSON */
    _recreate_and_reset(tester, &key_bson, status, NULL);
    /* mess with the length to corrupt the BSON. */
    ((uint8_t *)bson_get_data(&key_bson))[4] = 0xFF;
    _parse_fails(&key_bson, status, "invalid BSON");

    /* _id: missing. */
    _recreate_and_reset(tester, &key_bson, status, "_id", NULL);
    _parse_fails(&key_bson, status, "invalid key, no '_id'");
    /* _id: wrong type. */
    _recreate_and_reset(tester, &key_bson, status, "_id", NULL);
    BSON_APPEND_INT32(&key_bson, "_id", 123);
    _parse_fails(&key_bson, status, "invalid key, '_id' is not a UUID");
    /* _id: invalid binary subtype. */
    _recreate_and_reset(tester, &key_bson, status, "_id", NULL);
    BSON_APPEND_BINARY(&key_bson, "_id", BSON_SUBTYPE_BINARY, uuid->data, uuid->len);
    _parse_fails(&key_bson, status, "invalid key, '_id' is not a UUID");
    /* _id: invalid UUID length. */
    _recreate_and_reset(tester, &key_bson, status, "_id", NULL);
    BSON_APPEND_BINARY(&key_bson, "_id", BSON_SUBTYPE_UUID, uuid->data, 5);
    _parse_fails(&key_bson, status, "invalid key, '_id' is not a UUID");

    /* version: missing (ok) */
    _recreate_and_reset(tester, &key_bson, status, "version", NULL);
    _parse_ok(&key_bson, status);
    /* version: wrong type */
    _recreate_and_reset(tester, &key_bson, status, "version", NULL);
    BSON_APPEND_UTF8(&key_bson, "version", "abc");
    _parse_fails(&key_bson, status, "invalid 'version'");
    /* version: > 0 */
    _recreate_and_reset(tester, &key_bson, status, "version", NULL);
    BSON_APPEND_INT32(&key_bson, "version", 1);
    _parse_fails(&key_bson, status, "unsupported key document version");

    /* keyMaterial: missing. */
    _recreate_and_reset(tester, &key_bson, status, "keyMaterial", NULL);
    _parse_fails(&key_bson, status, "invalid key, no 'keyMaterial'");
    /* keyMaterial: wrong type. */
    _recreate_and_reset(tester, &key_bson, status, "keyMaterial", NULL);
    BSON_APPEND_INT32(&key_bson, "keyMaterial", 1);
    _parse_fails(&key_bson, status, "invalid 'keyMaterial', expected binary");
    /* keyMaterial: wrong subtype. */
    _recreate_and_reset(tester, &key_bson, status, "keyMaterial", NULL);
    BSON_APPEND_BINARY(&key_bson, "keyMaterial", BSON_SUBTYPE_UUID, uuid->data, uuid->len);
    _parse_fails(&key_bson, status, "invalid 'keyMaterial', expected subtype 0");

    /* masterKey: missing. */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    _parse_fails(&key_bson, status, "invalid key, no 'masterKey'");
    /* masterKey: missing provider. */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson, TMP_BSON("{'masterKey': { }}"));
    _parse_fails(&key_bson, status, "expected UTF-8 provider");
    /* masterKey: wrong provider. */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'bad' }}"));
    _parse_fails(&key_bson, status, "unrecognized KMS provider");
    /* masterKey: provider=aws, missing key */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'aws', 'region': 'us-east-1' }}"));
    _parse_fails(&key_bson, status, "expected UTF-8 key");
    /* masterKey: provider=aws, missing region */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson, TMP_BSON("{'masterKey': { 'provider': 'aws', 'key': 'cmk-string' }}"));
    _parse_fails(&key_bson, status, "expected UTF-8 region");
    /* masterKey: provider=aws, bad region */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson,
                TMP_BSON("{'masterKey': { 'provider': 'aws', "
                         "'key': 'cmk-string', 'region': 1 }}"));
    _parse_fails(&key_bson, status, "expected UTF-8 region");

    /* creationDate: missing */
    _recreate_and_reset(tester, &key_bson, status, "creationDate", NULL);
    _parse_fails(&key_bson, status, "invalid key, no 'creationDate'");
    /* creationDate: wrong type */
    _recreate_and_reset(tester, &key_bson, status, "creationDate", NULL);
    BSON_APPEND_UTF8(&key_bson, "creationDate", "abc");
    _parse_fails(&key_bson, status, "invalid 'creationDate', expect datetime");

    /* updateDate: missing */
    _recreate_and_reset(tester, &key_bson, status, "updateDate", NULL);
    _parse_fails(&key_bson, status, "invalid key, no 'updateDate'");

    /* updateDate: wrong type */
    _recreate_and_reset(tester, &key_bson, status, "updateDate", NULL);
    BSON_APPEND_UTF8(&key_bson, "updateDate", "abc");
    _parse_fails(&key_bson, status, "invalid 'updateDate', expect datetime");

    /* status: missing */
    _recreate_and_reset(tester, &key_bson, status, "status", NULL);
    _parse_fails(&key_bson, status, "invalid key, no 'status'");

    /* masterKey: azure */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson,
                TMP_BSON("{'masterKey': { 'provider': 'azure', 'keyVaultEndpoint': "
                         "'abc.example.com', 'keyName': 'test', 'keyVersion': 'abc' }}"));
    _parse_ok(&key_bson, status);

    /* masterKey: gcp */
    _recreate_and_reset(tester, &key_bson, status, "masterKey", NULL);
    bson_concat(&key_bson,
                TMP_BSON("{'masterKey': { 'provider': 'gcp', 'endpoint': "
                         "'abc.example.com', 'projectId': 'project', 'location': "
                         "'global', 'keyRing': 'ring', 'keyName': 'name' }}"));
    _parse_ok(&key_bson, status);

    mongocrypt_status_destroy(status);
    bson_destroy(&key_bson);
}

static void test_mongocrypt_key_alt_name_from_iter(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    bson_iter_t iter;
    bson_t *test;
    _mongocrypt_key_alt_name_t *key_alt_names;

    status = mongocrypt_status_new();

    /* Empty alt names */
    test = TMP_BSON("{'test': []}");
    bson_iter_init_find(&iter, test, "test");
    ASSERT_OK_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status);
    BSON_ASSERT(NULL == key_alt_names);

    /* One alt name */
    test = TMP_BSON("{'test': ['a']}");
    bson_iter_init_find(&iter, test, "test");
    ASSERT_OK_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status);
    BSON_ASSERT(0 == strcmp("a", _mongocrypt_key_alt_name_get_string(key_alt_names)));
    BSON_ASSERT(NULL == key_alt_names->next);
    _mongocrypt_key_alt_name_destroy_all(key_alt_names);

    /* Two alt names */
    test = TMP_BSON("{'test': ['a', 'b']}");
    bson_iter_init_find(&iter, test, "test");
    ASSERT_OK_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status);
    BSON_ASSERT(0 == strcmp("b", _mongocrypt_key_alt_name_get_string(key_alt_names)));
    BSON_ASSERT(0 == strcmp("a", _mongocrypt_key_alt_name_get_string(key_alt_names->next)));
    BSON_ASSERT(NULL == key_alt_names->next->next);
    _mongocrypt_key_alt_name_destroy_all(key_alt_names);

    /* Invalid alt names */
    test = TMP_BSON("{'test': ['a', 1]}");
    bson_iter_init_find(&iter, test, "test");
    ASSERT_FAILS_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status),
                        status,
                        "unexpected non-UTF8");

    /* Duplicate alt names */
    test = TMP_BSON("{'test': ['b', 'a', 'c', 'a']}");
    bson_iter_init_find(&iter, test, "test");
    ASSERT_FAILS_STATUS(_mongocrypt_key_alt_name_from_iter(&iter, &key_alt_names, status), status, "duplicate");

    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_key(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_key_parsing);
    INSTALL_TEST(test_mongocrypt_key_alt_name_from_iter);
}
libmongocrypt-1.19.0/test/test-mongocrypt-kms-ctx.c000066400000000000000000001432061521103432300223570ustar00rootroot00000000000000/*
 * 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 

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

/*

 
  
   
   
  
  
 
 
  
  
   
   
    
      
    
   
   
    
    
     
     
      
     
    
   
  
 

*/
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};

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
  
 

*/
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);
}

/*

 
  
   
   
  
  
 
 
  
  
   
  
 

*/
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};
/*

 
  
   
   
  
  
  
 
 
  
  
  
   
  
 

*/
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);
}

/*

 
  
   
   
  
  
 
 
  
  
   
  
 

*/
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";

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
   
   
    
    
     
     
      
     
    
   
  
 

*/
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);
}

/*

 
  
   
   
  
  
 
 
  
  
   
   
    
     
     
    
    
     
     
    
    
     
     
    
   
  
 

*/
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};

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
   
  
 

*/
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);
}

/*

 
  
   
   
  
  
 
 
  
  
   
   
    
    
    
    
   
   
  
 

*/
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";

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
   
   
  
 

*/
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);
}

/*

 
  
   
   
  
  
 
 
  
  
   
   
    
    
    
   
   
   
  
 

*/
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

};

/*

 
  
   
   
  
  
  
 
 
  
  
  
   
   
  
 

*/
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);
}

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);
}
libmongocrypt-1.19.0/test/test-mongocrypt-kms-responses.c000066400000000000000000000172131521103432300236000ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 

#include "mongocrypt-private.h"
#include "test-mongocrypt-assert-match-bson.h"
#include "test-mongocrypt.h"

/* Tests have the form
{
   ctx: [],
   http_reply: [
      "HTTP line 1\r\n",
      "HTTP line 2\r\n"
   ],
   expect: "ok" | "error message" | 
}
*/

static void _satisfy_oauth_request(mongocrypt_kms_ctx_t *kms_ctx) {
    const char *valid_reply = "HTTP/1.1 200 OK\r\n"
                              "Content-Length: 85\r\n"
                              "\r\n"
                              "{\"token_type\":\"Bearer\",\"expires_in\":3599,\"ext_expires_"
                              "in\":3599,\"access_token\":\"AAAA\"}";

    mongocrypt_binary_t *bin = mongocrypt_binary_new_from_data((uint8_t *)valid_reply, (uint32_t)strlen(valid_reply));
    ASSERT_OK(mongocrypt_kms_ctx_feed(kms_ctx, bin), kms_ctx);
    mongocrypt_binary_destroy(bin);
}

static void _test_one_kms_response(_mongocrypt_tester_t *tester, bson_t *test) {
    mongocrypt_t *crypt;
    mongocrypt_kms_ctx_t *kms_ctx;
    mongocrypt_ctx_t *ctx;
    bson_iter_t iter;
    bson_iter_t ctx_iter;

    BSON_ASSERT(bson_iter_init_find(&iter, test, "description"));
    TEST_PRINTF("- %s\n", bson_iter_utf8(&iter, NULL));
    BSON_ASSERT(bson_iter_init_find(&ctx_iter, test, "ctx"));
    BSON_ASSERT(bson_iter_recurse(&ctx_iter, &ctx_iter));
    while (bson_iter_next(&ctx_iter)) {
        const char *ctx_type = bson_iter_utf8(&ctx_iter, NULL);
        bool ok = false;
        mongocrypt_status_t *status;
        mongocrypt_binary_t *bin = NULL;
        char *expect;

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

        /* Test both contexts for creating a data key and automatic decryption
         * since they go through different code paths related to KMS. */
        if (0 == strcmp("datakey", ctx_type)) {
            mongocrypt_ctx_setopt_masterkey_aws(ctx, "example", -1, "example", -1);
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        } else if (0 == strcmp("decrypt", ctx_type)) {
            mongocrypt_binary_destroy(bin);
            bin = _mongocrypt_tester_encrypted_doc(tester);
            tester->paths.key_file = "./test/example/key-document.json";
            ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, bin), ctx);
        } else if (0 == strcmp("azure_oauth_datakey", ctx_type) || 0 == strcmp("azure_datakey", ctx_type)) {
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'azure', 'keyVaultEndpoint': "
                                                               "'example.vault.azure.net', 'keyName': 'test'}"));
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        } else if (0 == strcmp("azure_oauth_decrypt", ctx_type) || 0 == strcmp("azure_decrypt", ctx_type)) {
            bin = _mongocrypt_tester_encrypted_doc(tester);
            tester->paths.key_file = "./test/data/key-document-azure.json";
            ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, bin), ctx);
        } else if (0 == strcmp("gcp_oauth_datakey", ctx_type) || 0 == strcmp("gcp_datakey", ctx_type)) {
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON("{'provider': 'gcp', 'projectId': 'test', 'location': "
                                                               "'global', 'keyRing': 'test', 'keyName': 'test'}"));
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);
        } else if (0 == strcmp("gcp_oauth_decrypt", ctx_type) || 0 == strcmp("gcp_decrypt", ctx_type)) {
            bin = _mongocrypt_tester_encrypted_doc(tester);
            tester->paths.key_file = "./test/data/key-document-gcp.json";
            ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, bin), ctx);
        } else {
            TEST_STDERR_PRINTF("unsupported ctx type: %s\n", ctx_type);
            abort();
        }

        _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_KMS);
        kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
        BSON_ASSERT(kms_ctx);
        BSON_ASSERT(mongocrypt_kms_ctx_bytes_needed(kms_ctx) > 0);

        if (0 == strcmp("gcp_datakey", ctx_type) || 0 == strcmp("gcp_decrypt", ctx_type)
            || 0 == strcmp("azure_datakey", ctx_type) || 0 == strcmp("azure_decrypt", ctx_type)) {
            /* The targeted request is after the oauth request.
             * Satisfy the oauth request with a valid response */
            _satisfy_oauth_request(kms_ctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx);
            BSON_ASSERT(kms_ctx);
            BSON_ASSERT(mongocrypt_kms_ctx_bytes_needed(kms_ctx) > 0);
        }

        /* Feed until failure or completion. */
        BSON_ASSERT(bson_iter_init_find(&iter, test, "http_reply"));
        BSON_ASSERT(bson_iter_recurse(&iter, &iter));
        while (bson_iter_next(&iter)) {
            uint32_t len;
            uint8_t *data;

            mongocrypt_binary_destroy(bin);
            data = (uint8_t *)bson_iter_utf8(&iter, &len);
            bin = mongocrypt_binary_new_from_data(data, len);
            if (!mongocrypt_kms_ctx_feed(kms_ctx, bin)) {
                mongocrypt_kms_ctx_status(kms_ctx, status);
                goto failed;
            }
        }

        if (!mongocrypt_ctx_kms_done(ctx)) {
            mongocrypt_ctx_status(ctx, status);
            goto failed;
        }

        ok = true;

    failed:
        BSON_ASSERT(bson_iter_init_find(&iter, test, "expect"));
        if (BSON_ITER_HOLDS_ARRAY(&iter)) {
            // Concatenate array into one string.
            expect = bson_strdup("");
            bson_iter_recurse(&iter, &iter);
            while (bson_iter_next(&iter)) {
                ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
                char *previous = expect;
                expect = bson_strdup_printf("%s%s", expect, bson_iter_utf8(&iter, NULL));
                bson_free(previous);
            }
        } else {
            expect = bson_strdup(bson_iter_utf8(&iter, NULL));
        }

        if (0 == strcmp("ok", expect)) {
            ASSERT_OR_PRINT(ok, status);
        } else {
            ASSERT_STATUS_CONTAINS(status, expect);
        }

        bson_free(expect);
        mongocrypt_binary_destroy(bin);
        mongocrypt_status_destroy(status);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

static void _test_kms_responses(_mongocrypt_tester_t *tester) {
    bson_t test_file;
    bson_iter_t iter;

    _load_json_as_bson("./test/data/kms-tests.json", &test_file);
    for (bson_iter_init(&iter, &test_file); bson_iter_next(&iter);) {
        bson_t test;

        bson_iter_bson(&iter, &test);
        _test_one_kms_response(tester, &test);
        bson_destroy(&test);
    }
    bson_destroy(&test_file);
}

void _mongocrypt_tester_install_kms_responses(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_kms_responses);
}
libmongocrypt-1.19.0/test/test-mongocrypt-local-kms.c000066400000000000000000000064521521103432300226540ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "test-mongocrypt.h"

static void _test_local_roundtrip(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    _mongocrypt_buffer_t encrypted_cmd;
    bson_t as_bson;
    bson_iter_t iter;

    bin = mongocrypt_binary_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    /* Encrypt a document. */
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    /* Because this is local, we skip NEED_KMS and go right to READY. */
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);

    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &as_bson));
    /* Keep a copy to decrypt later. */
    _mongocrypt_buffer_copy_from_binary(&encrypted_cmd, bin);
    CRYPT_TRACEF(&crypt->log, "encrypted doc: %s", tmp_json(&as_bson));
    bson_iter_init(&iter, &as_bson);
    bson_iter_find_descendant(&iter, "filter.ssn", &iter);
    BSON_ASSERT(BSON_ITER_HOLDS_BINARY(&iter));
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt); /* destroy because of caching. */

    /* Decrypt it back. */
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    ctx = mongocrypt_ctx_new(crypt);
    _mongocrypt_buffer_to_binary(&encrypted_cmd, bin);
    ASSERT_OK(mongocrypt_ctx_decrypt_init(ctx, bin), ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_NEED_MONGO_KEYS);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, TEST_FILE("./test/data/key-document-local.json")), ctx);
    ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

    /* Because this is local, we skip NEED_KMS and go right to READY. */
    BSON_ASSERT(mongocrypt_ctx_state(ctx) == MONGOCRYPT_CTX_READY);
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);

    BSON_ASSERT(_mongocrypt_binary_to_bson(bin, &as_bson));
    CRYPT_TRACEF(&crypt->log, "decrypted doc: %s", tmp_json(&as_bson));
    bson_iter_init(&iter, &as_bson);
    bson_iter_find_descendant(&iter, "filter.ssn", &iter);
    BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&iter));
    BSON_ASSERT(0 == strcmp(bson_iter_utf8(&iter, NULL), _mongocrypt_tester_plaintext(tester)));

    mongocrypt_binary_destroy(bin);
    _mongocrypt_buffer_cleanup(&encrypted_cmd);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_local_kms(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_local_roundtrip);
}
libmongocrypt-1.19.0/test/test-mongocrypt-log.c000066400000000000000000000063421521103432300215510ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-log-private.h"
#include "mongocrypt-opts-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt.h"

typedef struct {
    char *message;
    mongocrypt_log_level_t expected_level;
} log_test_ctx_t;

static void _test_log_fn(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx_void) {
    log_test_ctx_t *ctx = (log_test_ctx_t *)ctx_void;
    BSON_ASSERT(level == ctx->expected_level);
    BSON_ASSERT(0 == strcmp(message, ctx->message));
}

/* Test a custom log handler on all log levels except for trace. */
static void _test_log(_mongocrypt_tester_t *tester) {
    log_test_ctx_t log_ctx = {0};
    mongocrypt_log_level_t levels[] = {MONGOCRYPT_LOG_LEVEL_FATAL,
                                       MONGOCRYPT_LOG_LEVEL_ERROR,
                                       MONGOCRYPT_LOG_LEVEL_WARNING,
                                       MONGOCRYPT_LOG_LEVEL_INFO};
    size_t i;
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
    /* Test logging with a custom handler messages. */
    _mongocrypt_log_set_fn(&crypt->log, _test_log_fn, &log_ctx);
    log_ctx.message = "test";
    for (i = 0; i < sizeof(levels) / sizeof(*levels); i++) {
        log_ctx.expected_level = levels[i];
        _mongocrypt_log(&crypt->log, levels[i], "test");
    }

    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
}

#if defined(__GLIBC__) || defined(__APPLE__)
static void _test_no_log(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_status_t *status;
    char captured_logs[BUFSIZ];
    const int buffer_size = sizeof(captured_logs);
    int saved_stdout = dup(1);

    /* Redirect stdout to /dev/null and capture output in a buffer
     * so we can check if anything was logged to stdout.
     */
    memset(captured_logs, 0, buffer_size);
    stdout = freopen("/dev/null", "a", stdout);
    setbuf(stdout, captured_logs);

    status = mongocrypt_status_new();
    crypt = mongocrypt_new();
    mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    _mongocrypt_log(&crypt->log, MONGOCRYPT_LOG_LEVEL_FATAL, "Please don't log");
    mongocrypt_status_destroy(status);
    mongocrypt_destroy(crypt);
    BSON_ASSERT(strlen(captured_logs) == 0);
    stdout = fdopen(saved_stdout, "w");
}
#endif

void _mongocrypt_tester_install_log(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_log);
#if defined(__GLIBC__) || defined(__APPLE__)
    INSTALL_TEST(_test_no_log);
#endif
}
libmongocrypt-1.19.0/test/test-mongocrypt-marking.c000066400000000000000000003121211521103432300224130ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* For each field, check a valid value, invalid value, missing value */

#include "bson/bson.h"
#include "mc-fle-blob-subtype-private.h"
#include "mc-fle2-find-text-payload-private.h"
#include "mc-tokens-private.h"
#include "mongocrypt-buffer-private.h"
#include "mongocrypt-ciphertext-private.h"
#include "mongocrypt-marking-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"
#include 
#include 

/* Create a basis marking buffer with valid values for the given fields. */
static void _make_marking(bson_t *bson, _mongocrypt_buffer_t *buf) {
    buf->len = bson->len + 1;
    buf->data = bson_malloc(buf->len);
    BSON_ASSERT(buf->data);

    buf->data[0] = 0;
    buf->owned = true;
    memcpy(buf->data + 1, bson_get_data(bson), bson->len);
}

static void _parse_ok(_mongocrypt_buffer_t *marking_buf, _mongocrypt_marking_t *out) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    memset(out, 0, sizeof(*out));
    ASSERT_OK_STATUS(_mongocrypt_marking_parse_unowned(marking_buf, out, status), status);

    mongocrypt_status_destroy(status);
}

static void _parse_fails(_mongocrypt_buffer_t *marking_buf, const char *msg, _mongocrypt_marking_t *out) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    memset(out, 0, sizeof(*out));
    ASSERT_FAILS_STATUS(_mongocrypt_marking_parse_unowned(marking_buf, out, status), status, msg);

    mongocrypt_status_destroy(status);
}

static void test_mongocrypt_marking_parse(_mongocrypt_tester_t *tester) {
    bson_t *marking_bson;
    _mongocrypt_buffer_t marking_buf;
    _mongocrypt_marking_t marking;

    /* successful case. */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc', 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_ok(&marking_buf, &marking);
    BSON_ASSERT(marking.u.fle1.algorithm == MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM);
    BSON_ASSERT(0 == strcmp("abc", bson_iter_utf8(&marking.u.fle1.v_iter, NULL)));
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* buffer < 6 bytes */
    marking_buf.data = (uint8_t *)"abc";
    marking_buf.len = 3;
    marking_buf.owned = false;
    _parse_fails(&marking_buf, "invalid marking, length < 6", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* bad first byte */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc', 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _mongocrypt_marking_cleanup(&marking);
    marking_buf.data[0] = 1;
    _parse_fails(&marking_buf, "invalid marking", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* unrecognized fields. */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc', 'ka': 'alt', 'extra': 1}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "unrecognized field 'extra'", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* malformed BSON. */
    marking_bson = TMP_BSON("{}");
    ((uint8_t *)bson_get_data(marking_bson))[4] = 0xFF;
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "invalid BSON", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* a: missing */
    marking_bson = TMP_BSON("{'v': 'abc', 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "no 'a' specified", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);
    /* a: wrong type */
    marking_bson = TMP_BSON("{'a': 'abc', 'v': 'abc', 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "invalid marking, 'a' must be an int32", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);
    /* a: wrong integer */
    marking_bson = TMP_BSON("{'a': -1, 'v': 'abc', 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "invalid algorithm value: -1", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* v: missing */
    marking_bson = TMP_BSON("{'a': 2, 'ka': 'alt'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "no 'v' specified", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* Not testing IV per CDRIVER-3127. TODO: remove this comment. */

    /* ki+ka: missing */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc'}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "neither 'ki' nor 'ka' specified", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);
    /* ki+ka: both present */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc', 'ka': 'alt' }");
    BSON_APPEND_BINARY(marking_bson, "ki", BSON_SUBTYPE_UUID, (TEST_BIN(16))->data, 16);
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "both 'ki' and 'ka' specified", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* ki: wrong type */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc', 'ki': 'abc' }");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "key id must be a UUID", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* ki: wrong subtype */
    marking_bson = TMP_BSON("{'a': 2, 'v': 'abc' }");
    BSON_APPEND_BINARY(marking_bson, "ki", BSON_SUBTYPE_BINARY, (TEST_BIN(16))->data, 16);
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "key id must be a UUID", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);

    /* ka: wrong type */
    marking_bson = TMP_BSON("{'v': 'abc', 'ka': 1}");
    _make_marking(marking_bson, &marking_buf);
    _parse_fails(&marking_buf, "key alt name must be a UTF8", &marking);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);
}

#define RAW_STRING(...) #__VA_ARGS__

#define ASSERT_MINCOVER_EQ(got, expectString)                                                                          \
    if (1) {                                                                                                           \
        char *gotStr = bson_strdup("");                                                                                \
        for (size_t i = 0; i < mc_mincover_len(got); i++) {                                                            \
            char *previous = gotStr;                                                                                   \
            gotStr = bson_strdup_printf("%s%s\n", gotStr, mc_mincover_get(got, i));                                    \
            bson_free(previous);                                                                                       \
        }                                                                                                              \
        ASSERT_STREQUAL(gotStr, expectString);                                                                         \
        bson_free(gotStr);                                                                                             \
    } else                                                                                                             \
        ((void)0)

/* test_mc_get_mincover_from_FLE2RangeFindSpec tests processing an
 * FLE2RangeFindSpec into a mincover. It is is analogous to the
 * MinCoverInterfaceTest in the server code:
 * https://github.com/mongodb/mongo/blob/a4a3eba2a0e0a839ca6213491361269b42e12761/src/mongo/crypto/fle_crypto_test.cpp#L2585
 */
static void test_mc_get_mincover_from_FLE2RangeFindSpec(_mongocrypt_tester_t *tester) {
    typedef struct {
        const char *description; // May be NULL.
        const char *findSpecJSON;
        const char *expectedMinCover;
        mc_optional_int64_t sparsity;
        const char *expectedError;
        const char *expectedErrorAtParseTime;
    } testcase_t;

    testcase_t tests[] = {
        {.description = "Int32 Bounds included",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : true,
             "upperBound" : {"$numberInt" : "32"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000111\n"
                             "001\n"
                             "01\n"
                             "100000\n"},

        {.description = "Int32 Bounds excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberInt" : "32"},
             "ubIncluded" : false,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "001\n"
                             "01\n"},
        {.description = "Int32 Upper bound excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : true,
             "upperBound" : {"$numberInt" : "32"},
             "ubIncluded" : false,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000111\n"
                             "001\n"
                             "01\n"},
        {.description = "Int32 Lower bound excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberInt" : "32"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "001\n"
                             "01\n"
                             "100000\n"},
        {.description = "Int32 Infinite upper bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000111\n"
                             "001\n"
                             "01\n"
                             "100000\n"},
        {.description = "Int32 Infinite lower bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberInt" : "8"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000\n"
                             "001000\n"},
        {.description = "Int32 Infinite both bounds",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "0\n"
                             "100000\n"},
        {.description = "Int32 mincover=root no trimming",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "31"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "root\n"},
        {.description = "Int32 mincover=root TF=1",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "31"},
             "trimFactor" : 1
         }),
         .expectedMinCover = "0\n"
                             "1\n"},
        {.description = "Int32 mincover=root TF=3",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "31"},
             "trimFactor" : 3
         }),
         .expectedMinCover = "000\n"
                             "001\n"
                             "010\n"
                             "011\n"
                             "100\n"
                             "101\n"
                             "110\n"
                             "111\n"},
        {.description = "Int32 infinite both bounds SP=2",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .sparsity = OPT_I64(2),
         .expectedMinCover = "00\n"
                             "01\n"
                             "100000\n"},
        {.description = "Int32 infinite both bounds TF=1",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 1
         }),
         .expectedMinCover = "0\n"
                             "100000\n"},
        {.description = "Int32 infinite both bounds TF=2",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 2
         }),
         .expectedMinCover = "00\n"
                             "01\n"
                             "100000\n"},

        {.description = "Int32 infinite both bounds TF=3",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 3
         }),
         .expectedMinCover = "000\n"
                             "001\n"
                             "010\n"
                             "011\n"
                             "100000\n"},
        {.description = "Int32 infinite both bounds SP=2 TF=3",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 3
         }),
         .sparsity = OPT_I64(2),
         .expectedMinCover = "0000\n"
                             "0001\n"
                             "0010\n"
                             "0011\n"
                             "0100\n"
                             "0101\n"
                             "0110\n"
                             "0111\n"
                             "100000\n"},
        {.description = "Too large trim factor fails",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 6
         }),
         .expectedError =
             "Trim factor must be less than the number of bits (6) used to represent an element of the domain"},
        {.description = "Negative trim factor fails",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : -1
         }),
         .expectedErrorAtParseTime = "'trimFactor' must be non-negative"},
        {.description = "Int64 Bounds included",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "0"},
             "lbIncluded" : true,
             "upperBound" : {"$numberLong" : "823"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "-1000000000000000"},
             "indexMax" : {"$numberLong" : "8070450532247928832"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000000000000011100011010111111010100100110001101000000\n"
                             "00000000000001110001101011111101010010011000110100000100\n"
                             "00000000000001110001101011111101010010011000110100000101\n"
                             "0000000000000111000110101111110101001001100011010000011000\n"
                             "000000000000011100011010111111010100100110001101000001100100\n"
                             "000000000000011100011010111111010100100110001101000001100101\n"
                             "000000000000011100011010111111010100100110001101000001100110\n",
         .sparsity = OPT_I64(2)},

        {.description = "Int64 Bounds excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "0"},
             "lbIncluded" : false,
             "upperBound" : {"$numberLong" : "823"},
             "ubIncluded" : false,
             "indexMin" : {"$numberLong" : "-1000000000000000"},
             "indexMax" : {"$numberLong" : "8070450532247928832"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000000000000011100011010111111010100100110001101000000000000001\n"
                             "00000000000001110001101011111101010010011000110100000000000001\n"
                             "00000000000001110001101011111101010010011000110100000000000010\n"
                             "00000000000001110001101011111101010010011000110100000000000011\n"
                             "000000000000011100011010111111010100100110001101000000000001\n"
                             "000000000000011100011010111111010100100110001101000000000010\n"
                             "000000000000011100011010111111010100100110001101000000000011\n"
                             "0000000000000111000110101111110101001001100011010000000001\n"
                             "0000000000000111000110101111110101001001100011010000000010\n"
                             "0000000000000111000110101111110101001001100011010000000011\n"
                             "00000000000001110001101011111101010010011000110100000001\n"
                             "00000000000001110001101011111101010010011000110100000010\n"
                             "00000000000001110001101011111101010010011000110100000011\n"
                             "00000000000001110001101011111101010010011000110100000100\n"
                             "00000000000001110001101011111101010010011000110100000101\n"
                             "0000000000000111000110101111110101001001100011010000011000\n"
                             "000000000000011100011010111111010100100110001101000001100100\n"
                             "000000000000011100011010111111010100100110001101000001100101\n"
                             "00000000000001110001101011111101010010011000110100000110011000\n"
                             "00000000000001110001101011111101010010011000110100000110011001\n"
                             "00000000000001110001101011111101010010011000110100000110011010\n"
                             "000000000000011100011010111111010100100110001101000001100110110\n",
         .sparsity = OPT_I64(2)},

        {.description = "Int64 Upper bound excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "0"},
             "lbIncluded" : true,
             "upperBound" : {"$numberLong" : "823"},
             "ubIncluded" : false,
             "indexMin" : {"$numberLong" : "-1000000000000000"},
             "indexMax" : {"$numberLong" : "8070450532247928832"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000000000000011100011010111111010100100110001101000000\n"
                             "00000000000001110001101011111101010010011000110100000100\n"
                             "00000000000001110001101011111101010010011000110100000101\n"
                             "0000000000000111000110101111110101001001100011010000011000\n"
                             "000000000000011100011010111111010100100110001101000001100100\n"
                             "000000000000011100011010111111010100100110001101000001100101\n"
                             "00000000000001110001101011111101010010011000110100000110011000\n"
                             "00000000000001110001101011111101010010011000110100000110011001\n"
                             "00000000000001110001101011111101010010011000110100000110011010\n"
                             "000000000000011100011010111111010100100110001101000001100110110\n",
         .sparsity = OPT_I64(2)},

        {.description = "Int64 Lower bound excluded",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "0"},
             "lbIncluded" : false,
             "upperBound" : {"$numberLong" : "823"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "-1000000000000000"},
             "indexMax" : {"$numberLong" : "8070450532247928832"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "000000000000011100011010111111010100100110001101000000000000001\n"
                             "00000000000001110001101011111101010010011000110100000000000001\n"
                             "00000000000001110001101011111101010010011000110100000000000010\n"
                             "00000000000001110001101011111101010010011000110100000000000011\n"
                             "000000000000011100011010111111010100100110001101000000000001\n"
                             "000000000000011100011010111111010100100110001101000000000010\n"
                             "000000000000011100011010111111010100100110001101000000000011\n"
                             "0000000000000111000110101111110101001001100011010000000001\n"
                             "0000000000000111000110101111110101001001100011010000000010\n"
                             "0000000000000111000110101111110101001001100011010000000011\n"
                             "00000000000001110001101011111101010010011000110100000001\n"
                             "00000000000001110001101011111101010010011000110100000010\n"
                             "00000000000001110001101011111101010010011000110100000011\n"
                             "00000000000001110001101011111101010010011000110100000100\n"
                             "00000000000001110001101011111101010010011000110100000101\n"
                             "0000000000000111000110101111110101001001100011010000011000\n"
                             "000000000000011100011010111111010100100110001101000001100100\n"
                             "000000000000011100011010111111010100100110001101000001100101\n"
                             "000000000000011100011010111111010100100110001101000001100110\n",
         .sparsity = OPT_I64(2)},
        {.description = "Int64 Infinite upper bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "1"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "7"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "001\n"
                             "01\n"
                             "1\n"},
        {.description = "Int64 Infinite lower bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberLong" : "5"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "7"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "0\n"
                             "10\n"},
        {.description = "Int64 Infinite both bounds",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "-Infinity"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "Infinity"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "7"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "root\n"},
        {.description = "Mismatched types",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "1"},
             "lbIncluded" : true,
             "upperBound" : {"$numberLong" : "2"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "7"},
             "trimFactor" : 0
         }),
         .expectedError = "expected lowerBound to match index type"},
        {.description = "Int32 exclusive lower bound > upper bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberInt" : "7"},
             "ubIncluded" : true,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int64 exclusive lower bound > upper bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberLong" : "7"},
             "ubIncluded" : true,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int32 exclusive upper bound < lower bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : true,
             "upperBound" : {"$numberInt" : "7"},
             "ubIncluded" : false,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int64 exclusive upper bound < lower bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "7"},
             "lbIncluded" : true,
             "upperBound" : {"$numberLong" : "7"},
             "ubIncluded" : false,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int32 exclusive bounds cross",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberInt" : "7"},
             "ubIncluded" : false,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int64 exclusive bounds cross",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberLong" : "7"},
             "lbIncluded" : false,
             "upperBound" : {"$numberLong" : "7"},
             "ubIncluded" : false,
             "indexMin" : {"$numberLong" : "0"},
             "indexMax" : {"$numberLong" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be less than or equal to range max"},
        {.description = "Int32 exclusive upper bound is 0",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberInt" : "0"},
             "lbIncluded" : true,
             "upperBound" : {"$numberInt" : "0"},
             "ubIncluded" : false,
             "indexMin" : {"$numberInt" : "0"},
             "indexMax" : {"$numberInt" : "32"},
             "trimFactor" : 0
         }),
         .expectedError = "must be greater than the range minimum"},
        {.description = "Double inclusive bounds",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "23.5"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "35.25"},
             "ubIncluded" : true,
             "indexMin" : {"$numberDouble" : "0"},
             "indexMax" : {"$numberDouble" : "1000"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "11000000001101111\n"
                             "1100000000111\n"
                             "1100000001000000\n"
                             "11000000010000010\n"
                             "1100000001000001100\n"
                             "110000000100000110100000000000000000000000000000000"
                             "0000000000000\n"},
        {.description = "Double exclusive bounds",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "23.5"},
             "lbIncluded" : false,
             "upperBound" : {"$numberDouble" : "35.25"},
             "ubIncluded" : false,
             "indexMin" : {"$numberDouble" : "0"},
             "indexMax" : {"$numberDouble" : "1000"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "1100000000110111100000000000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000001\n"
                             "110000000011011110000000000000000000000001\n"
                             "11000000001101111000000000000000000000001\n"
                             "1100000000110111100000000000000000000001\n"
                             "110000000011011110000000000000000000001\n"
                             "11000000001101111000000000000000000001\n"
                             "1100000000110111100000000000000000001\n"
                             "110000000011011110000000000000000001\n"
                             "11000000001101111000000000000000001\n"
                             "1100000000110111100000000000000001\n"
                             "110000000011011110000000000000001\n"
                             "11000000001101111000000000000001\n"
                             "1100000000110111100000000000001\n"
                             "110000000011011110000000000001\n"
                             "11000000001101111000000000001\n"
                             "1100000000110111100000000001\n"
                             "110000000011011110000000001\n"
                             "11000000001101111000000001\n"
                             "1100000000110111100000001\n"
                             "110000000011011110000001\n"
                             "11000000001101111000001\n"
                             "1100000000110111100001\n"
                             "110000000011011110001\n"
                             "11000000001101111001\n"
                             "1100000000110111101\n"
                             "110000000011011111\n"
                             "1100000000111\n"
                             "1100000001000000\n"
                             "11000000010000010\n"
                             "1100000001000001100\n"},
        {.description = "Double exclusive upper bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "23.5"},
             "lbIncluded" : true,
             "upperBound" : {"$numberDouble" : "35.25"},
             "ubIncluded" : false,
             "indexMin" : {"$numberDouble" : "0"},
             "indexMax" : {"$numberDouble" : "1000"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "11000000001101111\n"
                             "1100000000111\n"
                             "1100000001000000\n"
                             "11000000010000010\n"
                             "1100000001000001100\n"},
        {.description = "Double exclusive lower bound",
         .findSpecJSON = RAW_STRING({
             "lowerBound" : {"$numberDouble" : "23.5"},
             "lbIncluded" : false,
             "upperBound" : {"$numberDouble" : "35.25"},
             "ubIncluded" : true,
             "indexMin" : {"$numberDouble" : "0"},
             "indexMax" : {"$numberDouble" : "1000"},
             "trimFactor" : 0
         }),
         .expectedMinCover = "1100000000110111100000000000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000000001\n"
                             "110000000011011110000000000000000000000000001\n"
                             "11000000001101111000000000000000000000000001\n"
                             "1100000000110111100000000000000000000000001\n"
                             "110000000011011110000000000000000000000001\n"
                             "11000000001101111000000000000000000000001\n"
                             "1100000000110111100000000000000000000001\n"
                             "110000000011011110000000000000000000001\n"
                             "11000000001101111000000000000000000001\n"
                             "1100000000110111100000000000000000001\n"
                             "110000000011011110000000000000000001\n"
                             "11000000001101111000000000000000001\n"
                             "1100000000110111100000000000000001\n"
                             "110000000011011110000000000000001\n"
                             "11000000001101111000000000000001\n"
                             "1100000000110111100000000000001\n"
                             "110000000011011110000000000001\n"
                             "11000000001101111000000000001\n"
                             "1100000000110111100000000001\n"
                             "110000000011011110000000001\n"
                             "11000000001101111000000001\n"
                             "1100000000110111100000001\n"
                             "110000000011011110000001\n"
                             "11000000001101111000001\n"
                             "1100000000110111100001\n"
                             "110000000011011110001\n"
                             "11000000001101111001\n"
                             "1100000000110111101\n"
                             "110000000011011111\n"
                             "1100000000111\n"
                             "1100000001000000\n"
                             "11000000010000010\n"
                             "1100000001000001100\n"
                             "1100000001000001101000000000000000000000000000000000000000000000\n"},
    };

    for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        bson_error_t error = {0};
        testcase_t *test = tests + i;
        mongocrypt_status_t *status = mongocrypt_status_new();

        if (test->description) {
            TEST_PRINTF("  %zu: %s\n", i, test->description);
        } else {
            TEST_PRINTF("  %zu\n", i);
        }

        bson_t *findSpecVal = bson_new_from_json((const uint8_t *)test->findSpecJSON, -1, &error);
        if (!findSpecVal) {
            TEST_ERROR("failed to parse JSON: %s", error.message);
        }

        bson_t *findSpecDoc = BCON_NEW("findSpec",
                                       "{",
                                       "edgesInfo",
                                       BCON_DOCUMENT(findSpecVal),
                                       "firstOperator", // Use a dummy firstOperator. It is not used for
                                                        // minCover.
                                       BCON_INT32(1),
                                       "payloadId", // Use a dummy payloadId. It is not used for minCover.
                                       BCON_INT32(1234),
                                       "}");

        bson_iter_t findSpecIter;
        ASSERT(bson_iter_init_find(&findSpecIter, findSpecDoc, "findSpec"));

        mc_FLE2RangeFindSpec_t findSpec;
        bool res = mc_FLE2RangeFindSpec_parse(&findSpec, &findSpecIter, status);
        if (test->expectedErrorAtParseTime) {
            ASSERT(!res);
            ASSERT_STATUS_CONTAINS(status, test->expectedErrorAtParseTime);
            goto cleanup;
        } else {
            ASSERT_OK_STATUS(res, status);
        }

        size_t sparsity = 1;
        if (test->sparsity.set) {
            sparsity = (size_t)test->sparsity.value;
        }

        mc_mincover_t *mc = mc_get_mincover_from_FLE2RangeFindSpec(&findSpec, sparsity, status);

        if (test->expectedError) {
            ASSERT(NULL == mc);
            ASSERT_STATUS_CONTAINS(status, test->expectedError);
        } else {
            ASSERT_OK_STATUS(mc, status);
            ASSERT_MINCOVER_EQ(mc, test->expectedMinCover);
        }
        mc_mincover_destroy(mc);

    cleanup:
        bson_destroy(findSpecDoc);
        bson_destroy(findSpecVal);
        mongocrypt_status_destroy(status);
    }
}

// Helper for get_ciphertext_from_marking_json when we don't want to use extra test buffer space.
static void get_ciphertext_from_marking_json_with_bufs(mongocrypt_t *crypt,
                                                       bson_t *marking_bson,
                                                       _mongocrypt_ciphertext_t *out,
                                                       mongocrypt_binary_t *cmd,
                                                       mongocrypt_binary_t *keyIdSpace,
                                                       mongocrypt_binary_t *kiSpace,
                                                       mongocrypt_binary_t *kuSpace) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    // Set up encryption environment
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, cmd), ctx);
    // Add a test key
    _mongocrypt_buffer_t keyId;
    _mongocrypt_buffer_from_binary(&keyId, keyIdSpace);
    keyId.subtype = BSON_SUBTYPE_UUID;
    _mongocrypt_key_broker_add_test_key(&ctx->kb, &keyId);

    _mongocrypt_buffer_t marking_buf;
    _mongocrypt_marking_t marking;
    // Add key identifier info to the marking
    BSON_APPEND_BINARY(marking_bson, "ki", BSON_SUBTYPE_UUID, (kiSpace)->data, 16);
    BSON_APPEND_BINARY(marking_bson, "ku", BSON_SUBTYPE_UUID, (kuSpace)->data, 16);
    _make_marking(marking_bson, &marking_buf);
    // Use FLE2 as the subtype (default is FLE1)
    marking_buf.data[0] = MC_SUBTYPE_FLE2EncryptionPlaceholder;
    _parse_ok(&marking_buf, &marking);

    ASSERT_OK_STATUS(_mongocrypt_marking_to_ciphertext((void *)&ctx->kb, &marking, out, status), status);

    mongocrypt_status_destroy(status);
    _mongocrypt_buffer_cleanup(&marking_buf);
    _mongocrypt_marking_cleanup(&marking);
    mongocrypt_ctx_destroy(ctx);
}

// Runs _mongocrypt_marking_to_ciphertext to compute the ciphertext for the given marking.
static void get_ciphertext_from_marking_json(_mongocrypt_tester_t *tester,
                                             mongocrypt_t *crypt,
                                             const char *markingJSON,
                                             _mongocrypt_ciphertext_t *out) {
    get_ciphertext_from_marking_json_with_bufs(crypt,
                                               TMP_BSON_STR(markingJSON),
                                               out,
                                               TEST_FILE("./test/example/cmd.json"),
                                               TEST_BIN(16),
                                               TEST_BIN(16),
                                               TEST_BIN(16));
}

// Get the ECOC token to use in decryption.
static mc_ECOCToken_t *getECOCToken(mongocrypt_t *crypt) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    // Test token key that we added earlier is all zeros.
    _mongocrypt_buffer_t tokenKey;
    _mongocrypt_buffer_init_size(&tokenKey, MONGOCRYPT_TOKEN_KEY_LEN);
    memset(tokenKey.data, 0, MONGOCRYPT_TOKEN_KEY_LEN);

    mc_CollectionsLevel1Token_t *collectionsLevel1Token =
        mc_CollectionsLevel1Token_new(crypt->crypto, &tokenKey, status);
    mc_ECOCToken_t *ecocToken = mc_ECOCToken_new(crypt->crypto, collectionsLevel1Token, status);
    ASSERT(mongocrypt_status_ok(status));

    mc_CollectionsLevel1Token_destroy(collectionsLevel1Token);
    _mongocrypt_buffer_cleanup(&tokenKey);
    mongocrypt_status_destroy(status);
    return ecocToken;
}

static mc_ServerDataEncryptionLevel1Token_t *getSDEL1Token(mongocrypt_t *crypt) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    // Test token key that we added earlier is all zeros.
    _mongocrypt_buffer_t tokenKey;
    _mongocrypt_buffer_init_size(&tokenKey, MONGOCRYPT_TOKEN_KEY_LEN);
    memset(tokenKey.data, 0, MONGOCRYPT_TOKEN_KEY_LEN);

    mc_ServerDataEncryptionLevel1Token_t *token =
        mc_ServerDataEncryptionLevel1Token_new(crypt->crypto, &tokenKey, status);
    ASSERT(mongocrypt_status_ok(status));
    _mongocrypt_buffer_cleanup(&tokenKey);
    mongocrypt_status_destroy(status);
    return token;
}

static void
validate_and_get_bindata(bson_t *obj, const char *field, bson_subtype_t expected_type, mongocrypt_binary_t *bin_out) {
    bson_iter_t iter;
    ASSERT(bson_iter_init_find(&iter, obj, field));
    ASSERT(BSON_ITER_HOLDS_BINARY(&iter));

    uint32_t bin_len;
    const uint8_t *bin = NULL;
    bson_subtype_t bin_subtype;
    bson_iter_binary(&iter, &bin_subtype, &bin_len, &bin);
    ASSERT(bin_subtype == expected_type);
    bin_out->data = (void *)bin;
    bin_out->len = bin_len;
}

static void validate_encrypted_token(mongocrypt_t *crypt,
                                     mongocrypt_binary_t *encrypted_token_bin,
                                     mongocrypt_binary_t *expected_esc_token,
                                     bool expect_is_leaf,
                                     uint8_t *is_leaf_out,
                                     bool expect_msize,
                                     uint32_t *msize_out) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    mc_ECOCToken_t *ecocToken = getECOCToken(crypt);
    const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2Algorithm();

    _mongocrypt_buffer_t p_buf, decrypt_buf;
    uint32_t expect_decrypt_size = expected_esc_token->len + (expect_is_leaf ? 1 : 0) + (expect_msize ? 3 : 0);
    ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&p_buf, encrypted_token_bin->data, encrypted_token_bin->len));

    _mongocrypt_buffer_init_size(&decrypt_buf, expect_decrypt_size);

    uint32_t decrypt_size;
    ASSERT_OK_STATUS(
        fle2alg
            ->do_decrypt(crypt->crypto, NULL, mc_ECOCToken_get(ecocToken), &p_buf, &decrypt_buf, &decrypt_size, status),
        status);
    ASSERT_CMPUINT32(decrypt_size, ==, decrypt_buf.len);
    ASSERT_CMPUINT32(decrypt_size, ==, expect_decrypt_size);

    ASSERT(0 == memcmp(decrypt_buf.data, expected_esc_token->data, expected_esc_token->len));

    if (expect_is_leaf && is_leaf_out) {
        *is_leaf_out = decrypt_buf.data[decrypt_buf.len - 1];
    }

    if (expect_msize && msize_out) {
        *msize_out = decrypt_buf.data[decrypt_buf.len - 3] | decrypt_buf.data[decrypt_buf.len - 2] << 8
                   | decrypt_buf.data[decrypt_buf.len - 1] << 16;
    }

    _mongocrypt_buffer_cleanup(&decrypt_buf);
    _mongocrypt_buffer_cleanup(&p_buf);
    mc_ECOCToken_destroy(ecocToken);
    mongocrypt_status_destroy(status);
}

typedef struct {
    mongocrypt_binary_t d;
    mongocrypt_binary_t s;
    mongocrypt_binary_t p;
    mongocrypt_binary_t u;
    mongocrypt_binary_t v;
    mongocrypt_binary_t e;
    mongocrypt_binary_t l;
    uint32_t t;
    uint64_t k;
} iupv2_fields_common;

static iupv2_fields_common validate_iupv2_common(bson_t *iup_bson) {
    iupv2_fields_common res;
    memset(&res, 0, sizeof(res));

    bson_iter_t iter;
#define ASSERT_EXISTS_BINDATA_OF_SUBTYPE(Field, Subtype) validate_and_get_bindata(iup_bson, #Field, Subtype, &res.Field)

#define ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(Field, Subtype, Len)                                                  \
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE(Field, Subtype);                                                                  \
    ASSERT(res.Field.len == Len)

    ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(d, BSON_SUBTYPE_BINARY, MONGOCRYPT_HMAC_SHA256_LEN);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(s, BSON_SUBTYPE_BINARY, MONGOCRYPT_HMAC_SHA256_LEN);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE(p, BSON_SUBTYPE_BINARY);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(u, BSON_SUBTYPE_UUID, 16);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE(v, BSON_SUBTYPE_BINARY);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(e, BSON_SUBTYPE_BINARY, MONGOCRYPT_HMAC_SHA256_LEN);
    ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN(l, BSON_SUBTYPE_BINARY, MONGOCRYPT_HMAC_SHA256_LEN);

#undef ASSERT_EXISTS_BINDATA_OF_SUBTYPE_AND_LEN
#undef ASSERT_EXISTS_AND_BINDATA_OF_LEN

    ASSERT(bson_iter_init_find(&iter, iup_bson, "t"));
    ASSERT(BSON_ITER_HOLDS_INT32(&iter));
    res.t = bson_iter_int32(&iter);

    ASSERT(bson_iter_init_find(&iter, iup_bson, "k"));
    ASSERT(BSON_ITER_HOLDS_INT64(&iter));
    res.k = bson_iter_int64(&iter);

    return res;
}

// Assert that the encryptedTokens fields in V2 insert/update ciphertext matches our expectations. Specifically, checks
// that the length of these fields are what we expect, and that the "isLeaf" token is appended when using range V2.
static void
validate_range_ciphertext(_mongocrypt_ciphertext_t *ciphertext, mongocrypt_t *crypt, uint32_t expectedEdges) {
    uint32_t expectedPLength = (MONGOCRYPT_HMAC_SHA256_LEN + 1);

    bson_t ciphertextBSON;
    bson_iter_t iter;
    ASSERT(_mongocrypt_buffer_to_bson(&ciphertext->data, &ciphertextBSON));

    ASSERT(ciphertext->blob_subtype == MC_SUBTYPE_FLE2InsertUpdatePayloadV2);
    ASSERT(ciphertext->original_bson_type == 0); // unset
    ASSERT(ciphertext->key_id.len == 0);         // unset

    iupv2_fields_common res = validate_iupv2_common(&ciphertextBSON);

    // 'p' field should be available, length should be 16 bytes of IV + expected bytes
    ASSERT(res.p.len == 16 + expectedPLength);

    // validate crypto of 'p'
    uint8_t is_leaf = 255;
    validate_encrypted_token(crypt, &res.p, &res.s, true, &is_leaf, false, NULL);
    // isLeaf byte should be 0.
    ASSERT(is_leaf == 0);

    // 'g' field should be available
    ASSERT(bson_iter_init_find(&iter, &ciphertextBSON, "g"));
    ASSERT(BSON_ITER_HOLDS_ARRAY(&iter));
    bson_t g_arr;
    {
        uint32_t g_buf_len;
        const uint8_t *g_buf;
        bson_iter_array(&iter, &g_buf_len, &g_buf);
        ASSERT(bson_init_static(&g_arr, g_buf, g_buf_len));
    }

    bson_iter_t g_iter;
    bson_iter_init(&g_iter, &g_arr);
    size_t g_count = 0, leaf_count = 0;
    // Iterate through each edge token set and check p for each
    while (bson_iter_next(&g_iter)) {
        g_count++;
        ASSERT(BSON_ITER_HOLDS_DOCUMENT(&g_iter));
        bson_t subdoc;
        {
            uint32_t subdoc_len;
            const uint8_t *subdoc_buf;
            bson_iter_document(&g_iter, &subdoc_len, &subdoc_buf);
            ASSERT(bson_init_static(&subdoc, subdoc_buf, subdoc_len));
        }

        mongocrypt_binary_t encrypted_token_bin, esc_token_bin;
        validate_and_get_bindata(&subdoc, "p", BSON_SUBTYPE_BINARY, &encrypted_token_bin);
        validate_and_get_bindata(&subdoc, "s", BSON_SUBTYPE_BINARY, &esc_token_bin);
        ASSERT_CMPUINT32(encrypted_token_bin.len, ==, 16 + expectedPLength);
        ASSERT_CMPUINT32(esc_token_bin.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);

        uint8_t is_leaf = 255;
        validate_encrypted_token(crypt, &encrypted_token_bin, &esc_token_bin, true, &is_leaf, false, NULL);
        // isLeaf byte should be either 0 or 1.
        if (is_leaf == 1) {
            leaf_count++;
        } else {
            ASSERT_CMPUINT8(is_leaf, ==, 0);
        }
    }
    ASSERT_CMPSIZE_T(g_count, ==, expectedEdges);
    // There should be exactly one leaf in any insert call.
    ASSERT_CMPSIZE_T(leaf_count, ==, 1);
    bson_destroy(&ciphertextBSON);
}

static void test_mc_marking_to_ciphertext_fle2_range(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    // Test that ciphertext matches our expectations.
    {
        const char markingJSON[] = RAW_STRING({
            't' : 1,
            'a' : 3,
            'v' : {'min' : 0, 'max' : 7, 'v' : 5, 'trimFactor' : 0},
            's' : {'$numberLong' : '1'},
            'cm' : {'$numberLong' : '1'}
        });
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

        get_ciphertext_from_marking_json(tester, crypt, markingJSON, &ciphertext);

        validate_range_ciphertext(&ciphertext, crypt, 4);
        _mongocrypt_ciphertext_cleanup(&ciphertext);

        mongocrypt_destroy(crypt);
    }
}

static void validate_text_search_token_set_common(bson_iter_t *iter_at_token_set_obj,
                                                  mongocrypt_t *crypt,
                                                  uint32_t expected_msize) {
    ASSERT(BSON_ITER_HOLDS_DOCUMENT(iter_at_token_set_obj));
    bson_t ts_bson;
    {
        uint32_t len;
        const uint8_t *buf;
        bson_iter_document(iter_at_token_set_obj, &len, &buf);
        ASSERT(bson_init_static(&ts_bson, buf, len));
    }

    mongocrypt_binary_t token_bin;
    mongocrypt_binary_t esc_token_bin;
    mongocrypt_binary_t encrypted_token_bin;

    validate_and_get_bindata(&ts_bson, "d", BSON_SUBTYPE_BINARY, &token_bin);
    ASSERT_CMPUINT32(token_bin.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);

    validate_and_get_bindata(&ts_bson, "l", BSON_SUBTYPE_BINARY, &token_bin);
    ASSERT_CMPUINT32(token_bin.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);

    validate_and_get_bindata(&ts_bson, "s", BSON_SUBTYPE_BINARY, &esc_token_bin);
    ASSERT_CMPUINT32(esc_token_bin.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);

    validate_and_get_bindata(&ts_bson, "p", BSON_SUBTYPE_BINARY, &encrypted_token_bin);
    ASSERT_CMPUINT32(encrypted_token_bin.len, ==, (16 + MONGOCRYPT_HMAC_SHA256_LEN + 3 /* msize */));

    // validate crypto of p
    uint32_t out_msize;
    validate_encrypted_token(crypt, &encrypted_token_bin, &esc_token_bin, false, NULL, true, &out_msize);
    ASSERT_CMPUINT32(out_msize, ==, expected_msize);
}

static size_t
validate_text_search_token_set_array_common(bson_iter_t *iter_at_array, mongocrypt_t *crypt, uint32_t expected_msize) {
    ASSERT(BSON_ITER_HOLDS_ARRAY(iter_at_array));
    bson_t arr_bson;
    {
        uint32_t subdoc_len;
        const uint8_t *subdoc_buf;
        bson_iter_array(iter_at_array, &subdoc_len, &subdoc_buf);
        ASSERT(bson_init_static(&arr_bson, subdoc_buf, subdoc_len));
    }

    bson_iter_t iter;
    bson_iter_init(&iter, &arr_bson);

    size_t count = 0;
    while (bson_iter_next(&iter)) {
        count++;
        validate_text_search_token_set_common(&iter, crypt, expected_msize);
    }
    return count;
}

typedef struct {
    size_t substrings;
    size_t suffixes;
    size_t prefixes;
} text_search_expected_token_counts;

// Assert that the fields in a insert/update payload V2 for text search match our expectations.
// Specifically, checks that the length of these fields, and the values of deterministic fields,
// are what we expect.
static void validate_text_search_ciphertext(_mongocrypt_tester_t *tester,
                                            _mongocrypt_ciphertext_t *ciphertext,
                                            mongocrypt_t *crypt,
                                            mc_FLE2TextSearchInsertSpec_t *spec,
                                            mongocrypt_fle2_placeholder_type_t type,
                                            uint64_t contention_max,
                                            text_search_expected_token_counts *expected_tag_counts) {
    bson_t payload_bson;
    bson_iter_t iter;
    ASSERT(_mongocrypt_buffer_to_bson(&ciphertext->data, &payload_bson));

    mc_ServerDataEncryptionLevel1Token_t *sdel1Token = getSDEL1Token(crypt);
    const mongocrypt_binary_t *keyId = TEST_BIN(16); // don't free!

    if (type == MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT) {
        ASSERT_CMPUINT8(ciphertext->blob_subtype, ==, MC_SUBTYPE_FLE2InsertUpdatePayloadV2);
        ASSERT_CMPUINT8(ciphertext->original_bson_type, ==, 0); // unset
        ASSERT_CMPUINT32(ciphertext->key_id.len, ==, 0);        // unset

        iupv2_fields_common res = validate_iupv2_common(&payload_bson);

        // validate u, t, k have correct values
        ASSERT_CMPBYTES(keyId->data, keyId->len, res.u.data, res.u.len);
        ASSERT_CMPUINT32(res.t, ==, BSON_TYPE_UTF8);
        ASSERT_CMPUINT64(res.k, <=, contention_max);

        // validate e is ServerDataEncryptionLevel1Token = HMAC(RootKey, 3)
        ASSERT_CMPUINT32(res.e.len, ==, mc_ServerDataEncryptionLevel1Token_get(sdel1Token)->len);
        ASSERT(memcmp(res.e.data, mc_ServerDataEncryptionLevel1Token_get(sdel1Token)->data, res.e.len) == 0);

        // validate crypto of p
        ASSERT(res.p.len == 16 + MONGOCRYPT_HMAC_SHA256_LEN + 3 /* msize */);
        // msize should be 0 for p
        uint32_t out_msize;
        validate_encrypted_token(crypt, &res.p, &res.s, false, NULL, true, &out_msize);
        ASSERT_CMPUINT32(0, ==, out_msize);

        // validate v decrypts cleanly
        {
            mongocrypt_status_t *status = mongocrypt_status_new();

            const _mongocrypt_value_encryption_algorithm_t *fle2alg = _mcFLE2v2AEADAlgorithm();
            // assert first 16 bytes == userKeyId == indexKeyId
            ASSERT_CMPUINT32(res.v.len, >, 16);
            ASSERT_CMPBYTES(keyId->data, keyId->len, res.v.data, 16);

            _mongocrypt_buffer_t key, aad, ctext, ptext;
            _mongocrypt_buffer_init_size(&key, MONGOCRYPT_KEY_LEN);
            memset(key.data, 0, key.len);
            ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&aad, res.v.data, 16));
            ASSERT(_mongocrypt_buffer_copy_from_data_and_size(&ctext, ((uint8_t *)res.v.data) + 16, res.v.len - 16));
            uint32_t plen = fle2alg->get_plaintext_len(res.v.len - 16, status);
            _mongocrypt_buffer_init_size(&ptext, plen);

            uint32_t pbytes;
            ASSERT_OK_STATUS(fle2alg->do_decrypt(crypt->crypto, &aad, &key, &ctext, &ptext, &pbytes, status), status);

            // BSON strings have 5 (4 for size + 1 null terminator) bytes of overhead
            ASSERT_CMPUINT32(pbytes, >=, 5);
            ASSERT_CMPSIZE_T(spec->len, ==, (pbytes - 5));
            ASSERT_STREQUAL(spec->v, ((char *)(ptext.data + 4)));

            _mongocrypt_buffer_cleanup(&ptext);
            _mongocrypt_buffer_cleanup(&ctext);
            _mongocrypt_buffer_cleanup(&aad);
            _mongocrypt_buffer_cleanup(&key);
            mongocrypt_status_destroy(status);
        }

        // assert b exists with correct fields
        ASSERT(bson_iter_init_find(&iter, &payload_bson, "b"));
        ASSERT(BSON_ITER_HOLDS_DOCUMENT(&iter));

        bson_t b_bson;
        bson_iter_t b_iter;
        {
            uint32_t subdoc_len;
            const uint8_t *subdoc_buf;
            bson_iter_document(&iter, &subdoc_len, &subdoc_buf);
            ASSERT(bson_init_static(&b_bson, subdoc_buf, subdoc_len));
        }

        // msize on equality token should be the sum of all tag counts, and for other tokens, 0.
        ASSERT(bson_iter_init_find(&b_iter, &b_bson, "e"));
        validate_text_search_token_set_common(&b_iter,
                                              crypt,
                                              (uint32_t)(1 + expected_tag_counts->substrings
                                                         + expected_tag_counts->suffixes
                                                         + expected_tag_counts->prefixes));

        size_t tscount = 0;
        ASSERT(bson_iter_init_find(&b_iter, &b_bson, "s"));
        tscount = validate_text_search_token_set_array_common(&b_iter, crypt, 0 /* msize */);
        ASSERT_CMPSIZE_T(expected_tag_counts->substrings, ==, tscount);

        ASSERT(bson_iter_init_find(&b_iter, &b_bson, "u"));
        tscount = validate_text_search_token_set_array_common(&b_iter, crypt, 0 /* msize */);
        ASSERT_CMPSIZE_T(expected_tag_counts->suffixes, ==, tscount);

        ASSERT(bson_iter_init_find(&b_iter, &b_bson, "p"));
        tscount = validate_text_search_token_set_array_common(&b_iter, crypt, 0 /* msize */);
        ASSERT_CMPSIZE_T(expected_tag_counts->prefixes, ==, tscount);
    } else {
        ASSERT_CMPUINT8(ciphertext->blob_subtype, ==, MC_SUBTYPE_FLE2FindTextPayload);
        ASSERT_CMPUINT8(ciphertext->original_bson_type, ==, 0); // unset
        ASSERT_CMPUINT32(ciphertext->key_id.len, ==, 0);        // unset
        mc_FLE2FindTextPayload_t parsed;
        mongocrypt_status_t *status = mongocrypt_status_new();

        ASSERT_OK_STATUS(mc_FLE2FindTextPayload_parse(&parsed, &payload_bson, status), status);
        ASSERT_CMPUINT64(parsed.maxContentionFactor, ==, contention_max);
        ASSERT(parsed.caseFold == spec->casef);
        ASSERT(parsed.diacriticFold == spec->diacf);

        bool exact = !(spec->prefix.set || spec->suffix.set || spec->substr.set);
        ASSERT(parsed.tokenSets.prefix.set == spec->prefix.set);
        ASSERT(parsed.tokenSets.substring.set == spec->substr.set);
        ASSERT(parsed.tokenSets.suffix.set == spec->suffix.set);
        ASSERT(parsed.tokenSets.exact.set == exact);
        ASSERT(parsed.prefixSpec.set == spec->prefix.set);
        ASSERT(parsed.substringSpec.set == spec->substr.set);
        ASSERT(parsed.suffixSpec.set == spec->suffix.set);

#define CHECK_TOKENS(Type)                                                                                             \
    if (parsed.tokenSets.Type.set) {                                                                                   \
        ASSERT_CMPUINT32(parsed.tokenSets.Type.value.edcDerivedToken.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);             \
        ASSERT_CMPUINT32(parsed.tokenSets.Type.value.escDerivedToken.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);             \
        ASSERT_CMPUINT32(parsed.tokenSets.Type.value.serverDerivedFromDataToken.len, ==, MONGOCRYPT_HMAC_SHA256_LEN);  \
    }
        CHECK_TOKENS(prefix);
        CHECK_TOKENS(suffix);
        CHECK_TOKENS(substring);
        CHECK_TOKENS(exact);
#undef CHECK_TOKENS
        if (parsed.prefixSpec.set) {
            ASSERT_CMPUINT32(parsed.prefixSpec.value.lb, ==, spec->prefix.value.lb);
            ASSERT_CMPUINT32(parsed.prefixSpec.value.ub, ==, spec->prefix.value.ub);
        }
        if (parsed.suffixSpec.set) {
            ASSERT_CMPUINT32(parsed.suffixSpec.value.lb, ==, spec->suffix.value.lb);
            ASSERT_CMPUINT32(parsed.suffixSpec.value.ub, ==, spec->suffix.value.ub);
        }
        if (parsed.substringSpec.set) {
            ASSERT_CMPUINT32(parsed.substringSpec.value.mlen, ==, spec->substr.value.mlen);
            ASSERT_CMPUINT32(parsed.substringSpec.value.lb, ==, spec->substr.value.lb);
            ASSERT_CMPUINT32(parsed.substringSpec.value.ub, ==, spec->substr.value.ub);
        }
        mongocrypt_status_destroy(status);
        mc_FLE2FindTextPayload_cleanup(&parsed);
    }

    mc_ServerDataEncryptionLevel1Token_destroy(sdel1Token);
    bson_destroy(&payload_bson);
}

static size_t calculate_expected_substring_tag_count(size_t beta, size_t mlen, size_t ub, size_t lb) {
    ASSERT_CMPSIZE_T(beta, <=, (SIZE_MAX - 15));
    ASSERT_CMPSIZE_T(lb, <=, ub);
    ASSERT_CMPSIZE_T(mlen, >=, ub);

    size_t padded_len = 16 * ((beta + 5 + 15) / 16) - 5;
    if (beta > mlen || lb > padded_len) {
        return 0;
    }
    size_t maxkgram1 = 0;
    size_t maxkgram2 = 0;
    for (size_t j = lb; j <= ub; j++) {
        maxkgram1 += (mlen - j + 1);
    }
    for (size_t j = lb; j <= BSON_MIN(ub, padded_len); j++) {
        maxkgram2 += (padded_len - j + 1);
    }
    return BSON_MIN(maxkgram1, maxkgram2); // msize
}

static size_t calculate_expected_nfix_tag_count(size_t beta, size_t ub, size_t lb) {
    ASSERT_CMPSIZE_T(beta, <=, (SIZE_MAX - 15));
    ASSERT_CMPSIZE_T(lb, <=, ub);
    size_t padded_len = 16 * ((beta + 5 + 15) / 16) - 5;
    if (lb > padded_len) {
        return 0;
    }
    return BSON_MIN(ub, padded_len) - lb + 1;
}

// Runs _mongocrypt_marking_to_ciphertext to compute the ciphertext for the given marking.
static bool test_text_search_marking_to_ciphertext(_mongocrypt_tester_t *tester,
                                                   mongocrypt_t *crypt,
                                                   _mongocrypt_ciphertext_t *out,
                                                   mongocrypt_fle2_placeholder_type_t type,
                                                   int64_t contention_max,
                                                   mc_FLE2TextSearchInsertSpec_t *test_spec,
                                                   mongocrypt_status_t *status) {
    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);

    // Set up encryption environment
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);
    // Add a test key
    _mongocrypt_buffer_t keyId;
    _mongocrypt_buffer_from_binary(&keyId, TEST_BIN(16));
    keyId.subtype = BSON_SUBTYPE_UUID;
    _mongocrypt_key_broker_add_test_key(&ctx->kb, &keyId);

    _mongocrypt_buffer_t marking_buf;
    _mongocrypt_marking_t marking;

    bson_t *marking_bson = bson_new();
    BSON_APPEND_INT32(marking_bson, "t", type);
    BSON_APPEND_INT32(marking_bson, "a", MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH);
    BSON_APPEND_INT64(marking_bson, "cm", contention_max);
    bson_t text_spec;
    BSON_APPEND_DOCUMENT_BEGIN(marking_bson, "v", &text_spec);
    bson_append_utf8(&text_spec, "v", 1, test_spec->v, test_spec->len);
    BSON_APPEND_BOOL(&text_spec, "casef", test_spec->casef);
    BSON_APPEND_BOOL(&text_spec, "diacf", test_spec->diacf);
    if (test_spec->prefix.set) {
        bson_t subspec;
        BSON_APPEND_DOCUMENT_BEGIN(&text_spec, "prefix", &subspec);
        BSON_APPEND_INT32(&subspec, "ub", test_spec->prefix.value.ub);
        BSON_APPEND_INT32(&subspec, "lb", test_spec->prefix.value.lb);
        ASSERT(bson_append_document_end(&text_spec, &subspec));
    }
    if (test_spec->substr.set) {
        bson_t subspec;
        BSON_APPEND_DOCUMENT_BEGIN(&text_spec, "substr", &subspec);
        BSON_APPEND_INT32(&subspec, "mlen", test_spec->substr.value.mlen);
        BSON_APPEND_INT32(&subspec, "ub", test_spec->substr.value.ub);
        BSON_APPEND_INT32(&subspec, "lb", test_spec->substr.value.lb);
        ASSERT(bson_append_document_end(&text_spec, &subspec));
    }
    if (test_spec->suffix.set) {
        bson_t subspec;
        BSON_APPEND_DOCUMENT_BEGIN(&text_spec, "suffix", &subspec);
        BSON_APPEND_INT32(&subspec, "ub", test_spec->suffix.value.ub);
        BSON_APPEND_INT32(&subspec, "lb", test_spec->suffix.value.lb);
        ASSERT(bson_append_document_end(&text_spec, &subspec));
    }
    ASSERT(bson_append_document_end(marking_bson, &text_spec));

    // Add key identifier info to the marking
    BSON_APPEND_BINARY(marking_bson, "ki", BSON_SUBTYPE_UUID, (TEST_BIN(16))->data, 16);
    BSON_APPEND_BINARY(marking_bson, "ku", BSON_SUBTYPE_UUID, (TEST_BIN(16))->data, 16);
    _make_marking(marking_bson, &marking_buf);
    // Use FLE2 as the subtype (default is FLE1)
    marking_buf.data[0] = MC_SUBTYPE_FLE2EncryptionPlaceholder;
    _parse_ok(&marking_buf, &marking);

    bool result = _mongocrypt_marking_to_ciphertext((void *)&ctx->kb, &marking, out, status);

    _mongocrypt_buffer_cleanup(&marking_buf);
    bson_destroy(marking_bson);
    _mongocrypt_marking_cleanup(&marking);
    mongocrypt_ctx_destroy(ctx);
    return result;
}

static void test_mc_marking_to_ciphertext_fle2_text_search(_mongocrypt_tester_t *tester) {
    if (!_aes_ctr_is_supported_by_os) {
        TEST_PRINTF("Common Crypto with no CTR support detected. Skipping.");
        return;
    }

    // Test substring
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foobar",
                                              .len = 6,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 1000, .ub = 100, .lb = 10}};
        text_search_expected_token_counts counts = {0};
        counts.substrings = calculate_expected_substring_tag_count(6, 1000, 100, 10);

        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                2,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                        2,
                                        &counts);

        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test suffix + prefix
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foobar",
                                              .len = 6,
                                              .suffix.set = true,
                                              .suffix.value = {.ub = 100, .lb = 10},
                                              .prefix.set = true,
                                              .prefix.value = {.ub = 100, .lb = 10}};
        text_search_expected_token_counts counts = {0};
        counts.suffixes = counts.prefixes = calculate_expected_nfix_tag_count(6, 100, 10);

        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                2,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                        2,
                                        &counts);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test empty string
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "",
                                              .len = 0,
                                              .prefix.set = true,
                                              .prefix.value = {.ub = 100, .lb = 10}};
        text_search_expected_token_counts counts = {0};

        // beta is 1 for empty strings
        counts.prefixes = calculate_expected_nfix_tag_count(1, 100, 10);

        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                2,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                        2,
                                        &counts);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test string cbc-padded length is less than lb (ie. substring/suffix/prefix tag sets will be
    // empty)
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foobar",
                                              .len = 6,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 1000, .ub = 100, .lb = 20},
                                              .prefix.set = true,
                                              .prefix.value = {.ub = 100, .lb = 20},
                                              .suffix.set = true,
                                              .suffix.value = {.ub = 100, .lb = 20}};
        text_search_expected_token_counts counts = {0};

        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                2,
                                                                &spec,
                                                                status),
                         status);

        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                        2,
                                        &counts);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test string exceeds mlen
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foobar",
                                              .len = 6,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 3, .ub = 1, .lb = 1}};
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            "longer than the maximum length for substring indexing");

        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test string is not valid utf-8
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        const char *expected_msg = "String passed in was not valid UTF-8";
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foob\xffr",
                                              .len = 6,
                                              .substr.set = true,
                                              .substr.value = {.mlen = INT32_MAX, .ub = 1, .lb = 1}};

        // invalid utf-8 byte 0xff
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        _mongocrypt_status_reset(status);

        // embedded null byte
        spec.v = "foob\x00r";
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        _mongocrypt_status_reset(status);

        // overlong encoding of 'a' (\x61)
        spec.v = "foob\xE0\x81\xA1r";
        spec.len = 8;
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);

        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test string is too large
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        const char *expected_msg = "String passed in was too long";
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.substr.set = true,
                                              .substr.value = {.mlen = INT32_MAX, .ub = 1, .lb = 1}};

        int len = (16 * 1024 * 1024) + 2;
        char *large_str = bson_malloc(len);
        memset(large_str, 'a', len);
        large_str[len - 1] = '\0';
        spec.v = large_str;
        spec.len = len - 1;

        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        bson_free(large_str);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test insert placeholder missing substring/suffix/prefix spec
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        const char *expected_msg = "missing a substring, suffix, or prefix index specification";
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo", .len = 3};
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_INSERT,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test find placeholder has multiple query specs
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        const char *expected_msg = "cannot contain multiple query type specifications";
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo",
                                              .len = 3,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 3, .ub = 1, .lb = 1},
                                              .prefix.set = true,
                                              .prefix.value = {.ub = 1, .lb = 1}};

        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test find placeholder has invalid UTF-8 string
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        const char *expected_msg = "String passed in was not valid UTF-8";
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foob\xffr",
                                              .len = 6,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 50, .ub = 30, .lb = 1}};

        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                   2,
                                                                   &spec,
                                                                   status),
                            status,
                            expected_msg);
        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test exact match find
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo", .len = 3, .diacf = true, .casef = true};
        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                7,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                        7,
                                        NULL);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        _mongocrypt_ciphertext_init(&ciphertext);

        // Test empty string case
        spec.v = "";
        spec.len = 0;
        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                7,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                        7,
                                        NULL);

        mongocrypt_status_destroy(status);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
    }

    // Test substring find
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo",
                                              .len = 3,
                                              .diacf = true,
                                              .substr.set = true,
                                              .substr.value = {.mlen = 300, .ub = 200, .lb = 2}};
        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                8,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                        8,
                                        NULL);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        _mongocrypt_ciphertext_init(&ciphertext);

        // Test empty string case
        spec.v = "";
        spec.len = 0;
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                   8,
                                                                   &spec,
                                                                   status),
                            status,
                            "string value cannot be empty");
        mongocrypt_status_destroy(status);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        mongocrypt_destroy(crypt);
    }

    // Test suffix find
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo",
                                              .len = 3,
                                              .casef = true,
                                              .suffix.set = true,
                                              .suffix.value = {.ub = 100, .lb = 1}};
        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                9,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                        9,
                                        NULL);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        _mongocrypt_ciphertext_init(&ciphertext);

        // Test empty string case
        spec.v = "";
        spec.len = 0;
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                   8,
                                                                   &spec,
                                                                   status),
                            status,
                            "string value cannot be empty");
        mongocrypt_status_destroy(status);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        mongocrypt_destroy(crypt);
    }

    // Test prefix find
    {
        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);
        mongocrypt_status_t *status = mongocrypt_status_new();
        mc_FLE2TextSearchInsertSpec_t spec = {.v = "foo",
                                              .len = 3,
                                              .prefix.set = true,
                                              .prefix.value = {.ub = 300, .lb = 3}};
        ASSERT_OK_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                crypt,
                                                                &ciphertext,
                                                                MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                10,
                                                                &spec,
                                                                status),
                         status);
        validate_text_search_ciphertext(tester,
                                        &ciphertext,
                                        crypt,
                                        &spec,
                                        MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                        10,
                                        NULL);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        _mongocrypt_ciphertext_init(&ciphertext);

        // Test empty string case
        spec.v = "";
        spec.len = 0;
        ASSERT_FAILS_STATUS(test_text_search_marking_to_ciphertext(tester,
                                                                   crypt,
                                                                   &ciphertext,
                                                                   MONGOCRYPT_FLE2_PLACEHOLDER_TYPE_FIND,
                                                                   8,
                                                                   &spec,
                                                                   status),
                            status,
                            "string value cannot be empty");
        mongocrypt_status_destroy(status);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        mongocrypt_destroy(crypt);
    }
}

static size_t count_suffixes(bson_t *ciphertext_bson) {
    bson_iter_t iter;
    // Find TextSearchTokenSets ("b").
    ASSERT(bson_iter_init_find(&iter, ciphertext_bson, "b"));
    ASSERT(bson_iter_recurse(&iter, &iter));
    // Find suffix tokens ("u"):
    ASSERT(bson_iter_find(&iter, "u"));
    ASSERT(bson_iter_recurse(&iter, &iter));
    size_t suffix_count = 0;
    while (bson_iter_next(&iter)) {
        suffix_count++;
    }
    return suffix_count;
}

static void test_ciphertext_len_steps_fle2_text_search(_mongocrypt_tester_t *tester) {
#define MARKING_JSON_FORMAT                                                                                            \
    RAW_STRING({                                                                                                       \
        't' : 1,                                                                                                       \
        'a' : 4,                                                                                                       \
        'v' : {                                                                                                        \
            'v' : "%s",                                                                                                \
            'casef' : false,                                                                                           \
            'diacf' : false,                                                                                           \
            'suffix' : {'ub' : {'$numberInt' : '100'}, 'lb' : {'$numberInt' : '1'}}                                    \
        },                                                                                                             \
        'cm' : {'$numberLong' : '2'}                                                                                   \
    })

    size_t last_len = 0;
    size_t last_suffix_count = 0;
    mongocrypt_binary_t *cmd = TEST_FILE("./test/example/cmd.json");
    mongocrypt_binary_t *key_file = TEST_BIN(16);
    mongocrypt_binary_t *ki = TEST_BIN(16);
    mongocrypt_binary_t *ku = TEST_BIN(16);

    for (size_t str_len = 0; str_len < 256; str_len++) {
        char *v = bson_malloc0(str_len + 1);
        memset(v, 'a', str_len);
        size_t bufsize = snprintf(NULL, 0, MARKING_JSON_FORMAT, v) + 1;
        ASSERT(bufsize > 0);
        char *markingJSON = bson_malloc(bufsize);
        int ret = bson_snprintf(markingJSON, bufsize, MARKING_JSON_FORMAT, v);
        BSON_ASSERT(0 < ret && ret < (int)bufsize);
        bson_t *marking_bson = TMP_BSON_STR(markingJSON);

        _mongocrypt_ciphertext_t ciphertext;
        _mongocrypt_ciphertext_init(&ciphertext);
        mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

        get_ciphertext_from_marking_json_with_bufs(crypt, marking_bson, &ciphertext, cmd, key_file, ki, ku);

        // Get res.v, and make sure its size steps when we expect.
        bson_t ciphertext_bson;
        ASSERT(_mongocrypt_buffer_to_bson(&ciphertext.data, &ciphertext_bson));
        iupv2_fields_common res = validate_iupv2_common(&ciphertext_bson);
        size_t suffix_count = count_suffixes(&ciphertext_bson);
        if (str_len != 0) {
            // We expect a step in ciphertext len iff str_len + 5 goes from 16k-1 to 16k. 5 is the number of overhead
            // bytes from the BSON header + null byte.
            if ((str_len + 5) % 16 == 0) {
                ASSERT_CMPSIZE_T(res.v.len, ==, last_len + 16);
            } else {
                ASSERT_CMPSIZE_T(res.v.len, ==, last_len);
            }

            // Similarly, we expect a step in suffix count at the same point:
            if ((str_len + 5) % 16 == 0) {
                size_t expected_suffix_count = BSON_MIN(100 /* ub */, last_suffix_count + 16);
                ASSERT_CMPSIZE_T(suffix_count, ==, expected_suffix_count);
            } else {
                size_t expected_suffix_count = BSON_MIN(100 /* ub */, last_suffix_count);
                ASSERT_CMPSIZE_T(suffix_count, ==, expected_suffix_count);
            }
        }
        last_len = res.v.len;
        last_suffix_count = suffix_count;

        bson_destroy(&ciphertext_bson);
        bson_free(markingJSON);
        bson_free(v);
        mongocrypt_destroy(crypt);
        _mongocrypt_ciphertext_cleanup(&ciphertext);
        // Clean up marking_bson and decrement the tester bson_count so we can reuse the space.
        bson_destroy(marking_bson);
        tester->bson_count--;
    }

#undef MARKING_JSON_FORMAT
}

void _mongocrypt_tester_install_marking(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_marking_parse);
    INSTALL_TEST(test_mc_get_mincover_from_FLE2RangeFindSpec);
    INSTALL_TEST(test_mc_marking_to_ciphertext_fle2_range);
    INSTALL_TEST(test_mc_marking_to_ciphertext_fle2_text_search);
    INSTALL_TEST(test_ciphertext_len_steps_fle2_text_search);
}
libmongocrypt-1.19.0/test/test-mongocrypt-opts.c000066400000000000000000000030101521103432300217420ustar00rootroot00000000000000/*
 * Copyright 2023-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 

#include 

#define BSON_STR(...) #__VA_ARGS__

static void test_mongocrypt_opts_kms_providers_lookup(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *bson = TEST_BSON(BSON_STR({"azure" : {"accessToken" : "bar"}}));

    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, bson), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mc_kms_creds_t got;
    ASSERT(_mongocrypt_opts_kms_providers_lookup(&crypt->opts.kms_providers, "azure", &got));
    ASSERT(got.type == MONGOCRYPT_KMS_PROVIDER_AZURE);

    ASSERT(!_mongocrypt_opts_kms_providers_lookup(&crypt->opts.kms_providers, "local", &got));
    ASSERT(got.type == MONGOCRYPT_KMS_PROVIDER_NONE);

    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_opts(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_opts_kms_providers_lookup);
}
libmongocrypt-1.19.0/test/test-mongocrypt-status.c000066400000000000000000000056031521103432300223120ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt.h"
#include "test-mongocrypt.h"

static void _test_status_len(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status;
    const char *out;
    char *somestring = "somestring";
    char *largestring;
    uint32_t out_len;
    const uint32_t errcode = 123;

    status = mongocrypt_status_new();

    /* Due to legacy behavior, the string length, if not specified as -1, is 1 +
     * string's length + 1 */
    mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, errcode, somestring, 3 /* strlen + 1 */);
    out = mongocrypt_status_message(status, &out_len);
    BSON_ASSERT(0 == strcmp("so", out));
    /* But the returned length is normal. */
    BSON_ASSERT(2 == out_len);

    /* With passing -1, the entire string should be copied */
    mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, errcode, somestring, -1);
    out = mongocrypt_status_message(status, &out_len);
    BSON_ASSERT(0 == strcmp("somestring", out));
    /* But the returned length is normal. */
    BSON_ASSERT(strlen(somestring) == out_len);

    /* Test setting a large string. */
    largestring = bson_malloc(4096);
    BSON_ASSERT(largestring);

    memset(largestring, 'a', 4096);
    mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, errcode, largestring, 4097);
    out = mongocrypt_status_message(status, &out_len);
    BSON_ASSERT(0 == strncmp(largestring, out, 4096));
    /* But the returned length is normal. */
    BSON_ASSERT(4096 == out_len);

    /* Test passing 0 as the length. Despite the fact that the length should be 1
     * + the string length, this is treated as a special case as if it were
     * passing the empty string. */
    mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, errcode, somestring, 0);
    out = mongocrypt_status_message(status, &out_len);
    BSON_ASSERT(0 == strcmp("", out));
    BSON_ASSERT(0 == out_len);

    /* Test passing 1 as the length */
    mongocrypt_status_set(status, MONGOCRYPT_STATUS_ERROR_CLIENT, errcode, somestring, 1);
    out = mongocrypt_status_message(status, &out_len);
    BSON_ASSERT(0 == strcmp("", out));
    BSON_ASSERT(0 == out_len);

    bson_free(largestring);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_status(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(_test_status_len);
}
libmongocrypt-1.19.0/test/test-mongocrypt-traverse-util.c000066400000000000000000000417121521103432300235760ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mongocrypt-traverse-util-private.h"
#include "test-mongocrypt.h"
#include 

static void _append_marking(bson_t *bson, const char *key, int key_len) {
    uint8_t *data;
    bson_t *marking_bson;

    marking_bson = BCON_NEW("a",
                            BCON_INT32(MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM),
                            "v",
                            BCON_UTF8("abc"),
                            "ka",
                            BCON_UTF8("Nancy"));

    data = bson_malloc0(marking_bson->len + 1);
    BSON_ASSERT(data);

    data[0] = 0;
    memcpy(data + 1, marking_bson, marking_bson->len);

    BSON_ASSERT(bson_append_binary(bson, key, key_len, 0x06, data, marking_bson->len + 1));
    bson_free(data);
    bson_destroy(marking_bson);
}

static void _append_ciphertext_with_subtype(bson_t *bson,
                                            const char *key,
                                            int key_len,
                                            int subtype,
                                            int first_byte,
                                            _mongocrypt_tester_t *tester) {
    uint8_t *data;
    char *utf8;
    int data_len;
    int key_id_len = 16;

    utf8 = "Mary";
    data_len = (int)(1 + key_id_len + 1 + strlen(utf8));

    data = bson_malloc0(data_len);
    BSON_ASSERT(data);

    data[0] = first_byte;
    memcpy(data + 1, (TEST_BIN(16))->data, key_id_len);
    data[1 + key_id_len] = 0x02; /* BSON type UTF8 */
    memcpy(data + key_id_len + 2, utf8, strlen(utf8));

    BSON_ASSERT(bson_append_binary(bson, key, key_len, subtype, data, data_len));

    bson_free(data);
}

typedef enum { NEST_IN_NONE, NEST_IN_DOCUMENT, NEST_IN_ARRAY } _nesting_t;

typedef struct {
    _mongocrypt_tester_t *tester;
    _nesting_t parent;
} _util_tester_t;

static void _reset_nesting(bson_t *parent, bson_t *bson, _nesting_t nesting, char *name) {
    if (nesting == NEST_IN_DOCUMENT) {
        BSON_ASSERT(bson_append_document_end(parent, bson));
        bson_append_document_begin(parent, name, -1, bson);
    } else if (nesting == NEST_IN_ARRAY) {
        BSON_ASSERT(bson_append_array_end(parent, bson));
        bson_append_array_unsafe_begin(parent, name, -1, bson);
    }
}

static int _field_count(bson_iter_t *iter) {
    int i = 0;

    while (bson_iter_next(iter)) {
        i++;
    }

    return i;
}

static void _assert_correct_fields(bson_t *bson,
                                   int num_markings,
                                   int num_deterministic,
                                   int num_random,
                                   int num_binary,
                                   int num_other,
                                   _util_tester_t *tester) {
    bson_iter_t iter;
    bson_iter_t child;
    int i;

    BSON_ASSERT(num_markings < 10);
    BSON_ASSERT(num_deterministic < 10);
    BSON_ASSERT(num_random < 10);
    BSON_ASSERT(num_binary < 10);
    BSON_ASSERT(num_other < 10);

    if (tester->parent != NEST_IN_NONE) {
        for (i = 0; i < num_other; i++) {
            char *key = bson_strdup_printf("other.field%d", i);
            bson_iter_init(&iter, bson);
            BSON_ASSERT(bson_iter_find_descendant(&iter, key, &child));
            bson_free(key);
        }

        for (i = 0; i < num_markings; i++) {
            char *key = bson_strdup_printf("markings.marking%d", i);
            bson_iter_init(&iter, bson);
            BSON_ASSERT(bson_iter_find_descendant(&iter, key, &child));
            bson_free(key);
        }
        for (i = 0; i < num_random; i++) {
            char *key = bson_strdup_printf("random.random%d", i);
            bson_iter_init(&iter, bson);
            BSON_ASSERT(bson_iter_find_descendant(&iter, key, &child));
            bson_free(key);
        }
        for (i = 0; i < num_deterministic; i++) {
            char *key = bson_strdup_printf("deterministic.deterministic%d", i);
            bson_iter_init(&iter, bson);
            BSON_ASSERT(bson_iter_find_descendant(&iter, key, &child));
            bson_free(key);
        }
        for (i = 0; i < num_binary; i++) {
            char *key = bson_strdup_printf("binary.binary%d", i);
            bson_iter_init(&iter, bson);
            BSON_ASSERT(bson_iter_find_descendant(&iter, key, &child));
            bson_free(key);
        }

    } else {
        /* When there's no nesting, just count fields. */
        bson_iter_init(&iter, bson);
        BSON_ASSERT(_field_count(&iter) == (num_other + num_binary + num_markings + num_deterministic + num_random));
    }
}

static bson_t *_assemble_bson(int num_markings,
                              int num_deterministic,
                              int num_random,
                              int num_binary,
                              int num_other,
                              _util_tester_t *tester) {
    bson_t *parent = NULL;
    bson_t bson;
    int i;

    /* Field names only take single-digit add-ons, for now */
    BSON_ASSERT(num_markings < 10);
    BSON_ASSERT(num_deterministic < 10);
    BSON_ASSERT(num_random < 10);
    BSON_ASSERT(num_binary < 10);
    BSON_ASSERT(num_other < 10);

    /* If we have a nesting type, nest each kind of field inside one */
    if (tester->parent == NEST_IN_DOCUMENT) {
        parent = bson_new();
        bson_append_document_begin(parent, "other", -1, &bson);
    } else if (tester->parent == NEST_IN_ARRAY) {
        parent = bson_new();
        bson_append_array_unsafe_begin(parent, "other", -1, &bson);
    } else {
        bson_init(&bson);
    }

    /* Append some other filler fields */
    for (i = 0; i < num_other; i++) {
        char *key = bson_strdup_printf("field%d", i);
        BSON_ASSERT(bson_append_utf8(&bson, key, 6, "hi", -1));
        bson_free(key);
    }

    _reset_nesting(parent, &bson, tester->parent, "markings");

    /* Append some number of markings */
    for (i = 0; i < num_markings; i++) {
        char *key = bson_strdup_printf("marking%d", i);
        _append_marking(&bson, key, 8);
        bson_free(key);
    }

    _reset_nesting(parent, &bson, tester->parent, "random");

    /* Append some number of random ciphertexts */
    for (i = 0; i < num_random; i++) {
        char *key = bson_strdup_printf("random%d", i);
        _append_ciphertext_with_subtype(&bson, key, 7, 6, MONGOCRYPT_ENCRYPTION_ALGORITHM_RANDOM, tester->tester);
        bson_free(key);
    }

    _reset_nesting(parent, &bson, tester->parent, "deterministic");

    /* Append some number of deterministic ciphertexts */
    for (i = 0; i < num_deterministic; i++) {
        char *key = bson_strdup_printf("deterministic%d", i);
        _append_ciphertext_with_subtype(&bson,
                                        key,
                                        14,
                                        6,
                                        MONGOCRYPT_ENCRYPTION_ALGORITHM_DETERMINISTIC,
                                        tester->tester);
        bson_free(key);
    }

    _reset_nesting(parent, &bson, tester->parent, "binary");

    /* Append some number of other bson subtype 6 fields */
    for (i = 0; i < num_binary; i++) {
        char *key = bson_strdup_printf("binary%d", i);
        _append_ciphertext_with_subtype(&bson, key, 7, 5, MONGOCRYPT_ENCRYPTION_ALGORITHM_NONE, tester->tester);
        bson_free(key);
    }

    if (tester->parent == NEST_IN_DOCUMENT) {
        BSON_ASSERT(bson_append_document_end(parent, &bson));
        return parent;
    } else if (tester->parent == NEST_IN_ARRAY) {
        BSON_ASSERT(bson_append_array_end(parent, &bson));
        return parent;
    }

    parent = bson_copy(&bson);
    bson_destroy(&bson);
    return parent;
}

static bool test_traverse_cb(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    int *matched = (int *)ctx;

    *matched += 1;

    return true;
}

static void test_traverse(int num_markings,
                          int num_deterministic,
                          int num_random,
                          int num_binary,
                          int num_other,
                          traversal_match_t match,
                          _util_tester_t *tester,
                          int num_matches) {
    mongocrypt_status_t *status;
    bson_iter_t iter;
    bson_t *bson;
    int matched = 0;

    status = mongocrypt_status_new();
    /* First, assemble the requested bson document */
    bson = _assemble_bson(num_markings, num_deterministic, num_random, num_binary, num_other, tester);

    /* Traverse */
    BSON_ASSERT(bson_iter_init(&iter, bson));
    BSON_ASSERT(_mongocrypt_traverse_binary_in_bson(test_traverse_cb, &matched, match, &iter, status));

    /* Count matches */
    BSON_ASSERT(matched == num_matches);

    bson_destroy(bson);
    mongocrypt_status_destroy(status);
}

static void test_mongocrypt_traverse_util_nesting(_util_tester_t *tester) {
    /* Empty document */
    test_traverse(0, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(0, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with only non-binary fields */
    test_traverse(0, 0, 0, 0, 2, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(0, 0, 0, 0, 2, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with only binary fields that aren't subtype 0x06 */
    test_traverse(0, 0, 0, 2, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(0, 0, 0, 2, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with a single marking */
    test_traverse(1, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(1, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 1);

    /* Document with multiple markings */
    test_traverse(3, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(3, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 3);

    /* Document with multiple markings and other fields */
    test_traverse(2, 0, 0, 1, 1, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_traverse(2, 0, 0, 1, 1, TRAVERSE_MATCH_MARKING, tester, 2);

    /* Document with a single random ciphertext */
    test_traverse(0, 0, 1, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 1);
    test_traverse(0, 0, 1, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with a single deterministic ciphertext */
    test_traverse(0, 1, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 1);
    test_traverse(0, 1, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with multiple ciphertexts */
    test_traverse(0, 1, 1, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 2);
    test_traverse(0, 1, 1, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with multiple ciphertexts and other fields */
    test_traverse(0, 1, 2, 0, 2, TRAVERSE_MATCH_CIPHERTEXT, tester, 3);
    test_traverse(0, 1, 2, 0, 2, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Documents with all types of fields */
    test_traverse(1, 1, 1, 1, 1, TRAVERSE_MATCH_CIPHERTEXT, tester, 2);
    test_traverse(1, 1, 1, 1, 1, TRAVERSE_MATCH_MARKING, tester, 1);
}

static bool post_transform_traverse_check(void *ctx, _mongocrypt_buffer_t *in, mongocrypt_status_t *status) {
    int *matches = (int *)ctx;
    uint8_t *message;

    message = in->data;
    message += 1;
    if (memcmp("secretmessage", message, 13) == 0) {
        *matches += 1;
    }

    return true;
}

static bool test_transform_cb(void *ctx, _mongocrypt_buffer_t *in, bson_value_t *out, mongocrypt_status_t *status) {
    int *matches = (int *)ctx;

    *matches += 1;

    /* When we transform, keep first byte so we get the
       same number of matches in the followup traverse */
    out->value_type = BSON_TYPE_BINARY;
    out->value.v_binary.subtype = 6;
    out->value.v_binary.data = bson_malloc0(14);
    BSON_ASSERT(out->value.v_binary.data);

    out->value.v_binary.data[0] = in->data[0];
    memcpy(out->value.v_binary.data + 1, "secretmessage", 13);
    out->value.v_binary.data_len = 14;

    return true;
}

static void test_transform(int num_markings,
                           int num_deterministic,
                           int num_random,
                           int num_binary,
                           int num_other,
                           traversal_match_t match,
                           _util_tester_t *tester,
                           int num_matches) {
    mongocrypt_status_t *status;
    bson_iter_t iter;
    bson_t *bson;
    bson_t out = BSON_INITIALIZER;
    int matches = 0;

    status = mongocrypt_status_new();
    /* First, assemble the requested bson document */
    bson = _assemble_bson(num_markings, num_deterministic, num_random, num_binary, num_other, tester);

    /* Perform a transformation, count matches */
    BSON_ASSERT(bson_iter_init(&iter, bson));
    BSON_ASSERT(_mongocrypt_transform_binary_in_bson(test_transform_cb, &matches, match, &iter, &out, status));

    /* Make sure we had the correct number of matches */
    BSON_ASSERT(matches == num_matches);

    /* Now, traverse through the document again and
       count the actual transformations */
    matches = 0;
    BSON_ASSERT(bson_iter_init(&iter, &out));
    BSON_ASSERT(_mongocrypt_traverse_binary_in_bson(post_transform_traverse_check, &matches, match, &iter, status));

    /* Also, make sure we have the correct number of
       non-matching fields */
    _assert_correct_fields(&out, num_markings, num_deterministic, num_random, num_binary, num_other, tester);

    BSON_ASSERT(matches == num_matches);

    bson_destroy(bson);
    bson_destroy(&out);
    mongocrypt_status_destroy(status);
}

static void test_mongocrypt_transform_util_nesting(_util_tester_t *tester) {
    /* Empty document */
    test_transform(0, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(0, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with only non-binary fields */
    test_transform(0, 0, 0, 0, 2, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(0, 0, 0, 0, 2, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with only binary fields that aren't subtype 0x06 */
    test_transform(0, 0, 0, 2, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(0, 0, 0, 2, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with a single marking */
    test_transform(1, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(1, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 1);

    /* Document with multiple markings */
    test_transform(3, 0, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(3, 0, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 3);

    /* Document with multiple markings and other fields */
    test_transform(2, 0, 0, 1, 1, TRAVERSE_MATCH_CIPHERTEXT, tester, 0);
    test_transform(2, 0, 0, 1, 1, TRAVERSE_MATCH_MARKING, tester, 2);

    /* Document with a single random ciphertext */
    test_transform(0, 0, 1, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 1);
    test_transform(0, 0, 1, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with a single deterministic ciphertext */
    test_transform(0, 1, 0, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 1);
    test_transform(0, 1, 0, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with multiple ciphertexts */
    test_transform(0, 1, 1, 0, 0, TRAVERSE_MATCH_CIPHERTEXT, tester, 2);
    test_transform(0, 1, 1, 0, 0, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Document with multiple ciphertexts and other fields */
    test_transform(0, 1, 2, 0, 2, TRAVERSE_MATCH_CIPHERTEXT, tester, 3);
    test_transform(0, 1, 2, 0, 2, TRAVERSE_MATCH_MARKING, tester, 0);

    /* Documents with all types of fields */
    test_transform(1, 1, 1, 1, 1, TRAVERSE_MATCH_CIPHERTEXT, tester, 2);
    test_transform(1, 1, 1, 1, 1, TRAVERSE_MATCH_MARKING, tester, 1);
}

static void test_mongocrypt_transform_util(_mongocrypt_tester_t *tester) {
    _util_tester_t ctx = {0};

    ctx.tester = tester;

    ctx.parent = NEST_IN_NONE;
    test_mongocrypt_transform_util_nesting(&ctx);

    ctx.parent = NEST_IN_DOCUMENT;
    test_mongocrypt_transform_util_nesting(&ctx);

    ctx.parent = NEST_IN_ARRAY;
    test_mongocrypt_transform_util_nesting(&ctx);
}

static void test_mongocrypt_traverse_util(_mongocrypt_tester_t *tester) {
    _util_tester_t ctx;

    ctx.tester = tester;

    ctx.parent = NEST_IN_NONE;
    test_mongocrypt_traverse_util_nesting(&ctx);

    ctx.parent = NEST_IN_DOCUMENT;
    test_mongocrypt_traverse_util_nesting(&ctx);

    ctx.parent = NEST_IN_ARRAY;
    test_mongocrypt_traverse_util_nesting(&ctx);
}

void _mongocrypt_tester_install_traverse_util(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_mongocrypt_traverse_util);
    INSTALL_TEST(test_mongocrypt_transform_util);
}
libmongocrypt-1.19.0/test/test-mongocrypt-util.c000066400000000000000000000062471521103432300217510ustar00rootroot00000000000000/*
 * 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 "test-mongocrypt-util.h"
#include "test-mongocrypt-assert.h"

#include 
#include 
#include 

const char *mongocrypt_ctx_state_to_string(mongocrypt_ctx_state_t state) {
    switch (state) {
    case MONGOCRYPT_CTX_ERROR: return "MONGOCRYPT_CTX_ERROR";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO";
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: return "MONGOCRYPT_CTX_NEED_MONGO_MARKINGS";
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: return "MONGOCRYPT_CTX_NEED_MONGO_KEYS";
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: return "MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS";
    case MONGOCRYPT_CTX_NEED_KMS: return "MONGOCRYPT_CTX_NEED_KMS";
    case MONGOCRYPT_CTX_READY: return "MONGOCRYPT_CTX_READY";
    case MONGOCRYPT_CTX_DONE: return "MONGOCRYPT_CTX_DONE";
    default: return "UNKNOWN";
    }
}

char *data_to_hex(const uint8_t *buf, size_t len) {
    char *hex_chars = malloc(len * 2 + 1);

    char *p = hex_chars;
    size_t i;

    for (i = 0; i < len; i++) {
        ASSERT(2 == bson_snprintf(p, 3, "%02x", buf[i]));
        p += 2;
    }

    *p = '\0';

    return hex_chars;
}

void bson_iter_bson(bson_iter_t *iter, bson_t *bson) {
    uint32_t len;
    const uint8_t *data = NULL;
    if (BSON_ITER_HOLDS_DOCUMENT(iter)) {
        bson_iter_document(iter, &len, &data);
    }
    if (BSON_ITER_HOLDS_ARRAY(iter)) {
        bson_iter_array(iter, &len, &data);
    }
    BSON_ASSERT(data);
    bson_init_static(bson, data, len);
}

bool kms_ctx_feed_all(mongocrypt_kms_ctx_t *kms_ctx, const uint8_t *data, uint32_t datalen) {
    mongocrypt_binary_t *bytes;
    uint32_t offset = 0;

    while (mongocrypt_kms_ctx_bytes_needed(kms_ctx) > 0) {
        uint32_t len = mongocrypt_kms_ctx_bytes_needed(kms_ctx);

        // Cap `len` to the total input length.
        // `mongocrypt_kms_ctx_bytes_needed` may request more bytes than is in the response.
        // The HTTP response parser defaults to requesting 1024 bytes initially.
        if (len > datalen - offset) {
            len = datalen - offset;
        }
        ASSERT_CMPINT(offset + len, <=, datalen);
        bytes = mongocrypt_binary_new_from_data((uint8_t *)data + offset, len);
        if (!mongocrypt_kms_ctx_feed(kms_ctx, bytes)) {
            mongocrypt_binary_destroy(bytes);
            return false;
        }

        offset += len;
        mongocrypt_binary_destroy(bytes);
    }
    ASSERT_CMPINT(0, ==, mongocrypt_kms_ctx_bytes_needed(kms_ctx));

    return true;
}
libmongocrypt-1.19.0/test/test-mongocrypt-util.h000066400000000000000000000025051521103432300217470ustar00rootroot00000000000000/*
 * 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.
 */

#ifndef TEST_MONGOCRYPT_UTIL_H
#define TEST_MONGOCRYPT_UTIL_H

#include "mongocrypt.h"

#include 

#include 
#include 

const char *mongocrypt_ctx_state_to_string(mongocrypt_ctx_state_t state);

char *data_to_hex(const uint8_t *data, size_t len);

/* bson_iter_bson iterates a document or array into a bson_t. */
void bson_iter_bson(bson_iter_t *iter, bson_t *bson);

// `kms_ctx_feed_all` repeatedly calls `mongocrypt_kms_ctx_feed`.
// Returns false on a failed call to `mongocrypt_kms_ctx_feed`.
// Useful for KMIP. The KMIP response parser expects two calls: (length, then data).
bool kms_ctx_feed_all(mongocrypt_kms_ctx_t *kms_ctx, const uint8_t *data, uint32_t datalen);

#endif /* TEST_MONGOCRYPT_UTIL_H */
libmongocrypt-1.19.0/test/test-mongocrypt.c000066400000000000000000001346221521103432300207750ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include 
#include 

#include 

#include "mongocrypt-config.h"
#include "mongocrypt-crypto-private.h"
#include "mongocrypt-marking-private.h"
#include "mongocrypt.h"
#include "test-mongocrypt.h"
#include  // kms_message_b64_pton

#ifdef MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO
#include 
#endif

/* Return a repeated character with no null terminator. */
char *_mongocrypt_repeat_char(char c, uint32_t times) {
    char *result;
    uint32_t i;

    result = bson_malloc(times);
    BSON_ASSERT(result);

    for (i = 0; i < times; i++) {
        result[i] = c;
    }

    return result;
}

void _load_json_as_bson(const char *path, bson_t *out) {
    bson_error_t error;
    bson_json_reader_t *reader;
    bool ret;

    reader = bson_json_reader_new_from_file(path, &error);
    if (!reader) {
        TEST_STDERR_PRINTF("error reading: %s\n", path);
    }
    ASSERT_OR_PRINT_BSON(reader, error);
    bson_init(out);
    ret = bson_json_reader_read(reader, out, &error);
    ASSERT_OR_PRINT_BSON(ret, error);

    bson_json_reader_destroy(reader);
}

void _usleep(int64_t usec) {
#ifdef _WIN32
    LARGE_INTEGER ft;
    HANDLE timer;

    BSON_ASSERT(usec >= 0);

    ft.QuadPart = -(10 * usec);
    timer = CreateWaitableTimer(NULL, true, NULL);
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
    WaitForSingleObject(timer, INFINITE);
    CloseHandle(timer);
#else
    BSON_ASSERT(usec >= 0);
    usleep((useconds_t)usec);
#endif
}

#define TEST_DATA_COUNT_INC(var)                                                                                       \
    if (1) {                                                                                                           \
        (var)++;                                                                                                       \
        if ((var) >= TEST_DATA_COUNT) {                                                                                \
            TEST_ERROR("TEST_DATA_COUNT exceeded for %s. Increment TEST_DATA_COUNT.", #var);                           \
        }                                                                                                              \
    } else                                                                                                             \
        ((void)0)

static void _load_json(_mongocrypt_tester_t *tester, const char *path) {
    bson_t as_bson;
    _mongocrypt_buffer_t *buf;

    _load_json_as_bson(path, &as_bson);

    buf = &tester->file_bufs[tester->file_count];
    _mongocrypt_buffer_steal_from_bson(buf, &as_bson);
    tester->file_paths[tester->file_count] = bson_strdup(path);
    TEST_DATA_COUNT_INC(tester->file_count);
}

static void _load_http(_mongocrypt_tester_t *tester, const char *path) {
    int fd;
    char *contents;
    int n_read;
    int filesize;
    char storage[512];
    int i;
    _mongocrypt_buffer_t *buf;

    filesize = 0;
    contents = NULL;
    fd = open(path, O_RDONLY);
    while ((n_read = read(fd, storage, sizeof(storage))) > 0) {
        filesize += n_read;
        /* Append storage. Performance does not matter. */
        contents = bson_realloc(contents, filesize);
        memcpy(contents + (filesize - n_read), storage, n_read);
    }

    if (n_read < 0) {
        TEST_STDERR_PRINTF("failed to read %s\n", path);
        abort();
    }

    close(fd);

    buf = &tester->file_bufs[tester->file_count];
    /* copy and fix newlines */
    _mongocrypt_buffer_init(buf);
    /* allocate twice the size since \n may become \r\n */
    buf->data = bson_malloc0(filesize * 2);
    BSON_ASSERT(buf->data);

    buf->len = 0;
    buf->owned = true;
    for (i = 0; i < filesize; i++) {
        if (contents[i] == '\n' && contents[i - 1] != '\r') {
            buf->data[buf->len++] = '\r';
        }
        buf->data[buf->len++] = contents[i];
    }

    bson_free(contents);
    tester->file_paths[tester->file_count] = bson_strdup(path);
    TEST_DATA_COUNT_INC(tester->file_count);
}

void _mongocrypt_tester_install(_mongocrypt_tester_t *tester,
                                char *name,
                                _mongocrypt_test_fn fn,
                                _mongocrypt_tester_crypto_spec_t crypto_spec) {
    bool crypto_enabled;

#ifdef MONGOCRYPT_ENABLE_CRYPTO
    crypto_enabled = true;
#else
    crypto_enabled = false;
#endif

    if (crypto_spec == CRYPTO_REQUIRED && !crypto_enabled) {
        TEST_PRINTF("Skipping test: %s – requires crypto to be enabled\n", name);
        return;
    }

    if (crypto_spec == CRYPTO_PROHIBITED && crypto_enabled) {
        TEST_PRINTF("Skipping test: %s – requires crypto to be disabled\n", name);
        return;
    }

    tester->test_fns[tester->test_count] = fn;
    tester->test_names[tester->test_count] = bson_strdup(name);
    TEST_DATA_COUNT_INC(tester->test_count);
}

mongocrypt_binary_t *_mongocrypt_tester_file(_mongocrypt_tester_t *tester, const char *path) {
    int i;
    mongocrypt_binary_t *to_return;

    to_return = mongocrypt_binary_new();
    tester->test_bin[tester->bin_count] = to_return;
    TEST_DATA_COUNT_INC(tester->bin_count);

    for (i = 0; i < tester->file_count; i++) {
        if (0 == strcmp(tester->file_paths[i], path)) {
            _mongocrypt_buffer_to_binary(&tester->file_bufs[i], to_return);
            return to_return;
        }
    }

    /* File not found, load it. */
    if (strstr(path, ".json")) {
        _load_json(tester, path);
    } else if (strstr(path, ".txt")) {
        _load_http(tester, path);
    }

    _mongocrypt_buffer_to_binary(&tester->file_bufs[tester->file_count - 1], to_return);
    return to_return;
}

bson_t *_mongocrypt_tester_file_as_bson(_mongocrypt_tester_t *tester, const char *path) {
    mongocrypt_binary_t *bin = _mongocrypt_tester_file(tester, path);
    bson_t *bson = &tester->test_bson[tester->bson_count];
    TEST_DATA_COUNT_INC(tester->bson_count);
    ASSERT(_mongocrypt_binary_to_bson(bin, bson));
    return bson;
}

bson_t *_mongocrypt_tester_bson_from_str(_mongocrypt_tester_t *tester, const char *json) {
    char *const full_json = bson_strdup(json);

    /* Replace ' with " */
    for (char *c = full_json; *c; c++) {
        if (*c == '\'') {
            *c = '"';
        }
    }

    bson_error_t error;
    bson_t *const bson = &tester->test_bson[tester->bson_count];
    TEST_DATA_COUNT_INC(tester->bson_count);
    if (!bson_init_from_json(bson, full_json, -1, &error)) {
        TEST_STDERR_PRINTF("%s", error.message);
        abort();
    }
    bson_free(full_json);
    return bson;
}

bson_t *_mongocrypt_tester_bson_from_json(_mongocrypt_tester_t *tester, const char *json, ...) {
    va_list ap;
    va_start(ap, json);
    char *const full_json = bson_strdupv_printf(json, ap);
    va_end(ap);

    bson_t *const bson = _mongocrypt_tester_bson_from_str(tester, full_json);
    bson_free(full_json);
    return bson;
}

bson_t *tmp_bsonf(_mongocrypt_tester_t *tester, const char *fmt, ...) {
    va_list arg;
    va_start(arg, fmt);

    size_t dst_capacity = strlen(fmt);
    char *dst = bson_malloc(dst_capacity);
    size_t dst_len = 0;

#define ENSURE_CAPACITY(len)                                                                                           \
    if (1) {                                                                                                           \
        if ((len) + dst_len >= dst_capacity) {                                                                         \
            dst_capacity = (len) + dst_len;                                                                            \
            dst_capacity *= 2;                                                                                         \
            dst = bson_realloc(dst, dst_capacity);                                                                     \
        }                                                                                                              \
    } else                                                                                                             \
        (void)0

    for (const char *ptr = fmt; *ptr != '\0'; ptr++) {
        // TODO: consider optimizing this loop to use strstr to find MC_STR or MC_BSON.
        if (0 == strncmp(ptr, "MC_STR", strlen("MC_STR"))) {
            const char *src = va_arg(arg, const char *);
            const size_t src_len = strlen(src);
            ENSURE_CAPACITY(src_len + 2);
            dst[dst_len++] = '"';
            memcpy(dst + dst_len, src, src_len);
            dst_len += src_len;
            dst[dst_len++] = '"';
            ptr += strlen("MC_STR") - 1;
            continue;
        }

        if (0 == strncmp(ptr, "MC_BSON", strlen("MC_BSON"))) {
            const bson_t *src_bson = va_arg(arg, const bson_t *);
            size_t src_len;
            char *src = bson_as_canonical_extended_json(src_bson, &src_len);
            ENSURE_CAPACITY(src_len);
            strcpy(dst + dst_len, src);
            dst_len += src_len;
            bson_free(src);
            ptr += strlen("MC_BSON") - 1;
            continue;
        }

        ENSURE_CAPACITY(1);
        dst[dst_len++] = *ptr;
    }
#undef ENSURE_CAPACITY

    dst[dst_len] = '\0';
    bson_t *tmp = TMP_BSON_STR(dst);
    va_end(arg);
    bson_free(dst);
    return tmp;
}

mongocrypt_binary_t *_mongocrypt_tester_bin_from_str(_mongocrypt_tester_t *tester, const char *json) {
    mongocrypt_binary_t *const bin = mongocrypt_binary_new();
    tester->test_bin[tester->bin_count] = bin;
    TEST_DATA_COUNT_INC(tester->bin_count);

    bson_t *const bson = _mongocrypt_tester_bson_from_str(tester, json);
    bin->data = (uint8_t *)bson_get_data(bson);
    bin->len = bson->len;
    return bin;
}

mongocrypt_binary_t *_mongocrypt_tester_bin_from_json(_mongocrypt_tester_t *tester, const char *json, ...) {
    va_list ap;
    va_start(ap, json);
    char *const full_json = bson_strdupv_printf(json, ap);
    va_end(ap);

    mongocrypt_binary_t *const bin = _mongocrypt_tester_bin_from_str(tester, full_json);
    bson_free(full_json);
    return bin;
}

mongocrypt_binary_t *_mongocrypt_tester_bin(_mongocrypt_tester_t *tester, int size) {
    mongocrypt_binary_t *bin;
    uint8_t *blob;
    int i;

    if (size == 0) {
        return NULL;
    }
    blob = bson_malloc(size);
    BSON_ASSERT(blob);

    for (i = 0; i < size; i++) {
        blob[i] = (i % 3) + 1; /* 1, 2, 3, 1, 2, 3, ... */
    }

    bin = mongocrypt_binary_new_from_data(blob, size);

    tester->test_blob[tester->blob_count] = blob;
    TEST_DATA_COUNT_INC(tester->blob_count);
    tester->test_bin[tester->bin_count] = bin;
    TEST_DATA_COUNT_INC(tester->bin_count);
    return bin;
}

void _mongocrypt_tester_satisfy_kms(_mongocrypt_tester_t *tester, mongocrypt_kms_ctx_t *kms) {
    const char *endpoint;

    BSON_ASSERT(mongocrypt_kms_ctx_endpoint(kms, &endpoint));
    BSON_ASSERT(endpoint == strstr(endpoint, "kms.") && strstr(endpoint, ".amazonaws.com"));
    mongocrypt_kms_ctx_feed(kms, TEST_FILE("./test/data/kms-aws/decrypt-response.txt"));
    BSON_ASSERT(0 == mongocrypt_kms_ctx_bytes_needed(kms));
}

/* Run the state machine on example data until hitting stop_state or a
 * terminal state. */
void _mongocrypt_tester_run_ctx_to(_mongocrypt_tester_t *tester,
                                   mongocrypt_ctx_t *ctx,
                                   mongocrypt_ctx_state_t stop_state) {
    mongocrypt_ctx_state_t state;
    mongocrypt_kms_ctx_t *kms;
    mongocrypt_status_t *status;
    mongocrypt_binary_t *bin;
    bool res;

    status = mongocrypt_status_new();
    state = mongocrypt_ctx_state(ctx);
    while (state != stop_state) {
        switch (state) {
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
            if (tester->paths.collection_info) {
                bin = TEST_FILE(tester->paths.collection_info);
            } else {
                bin = TEST_FILE("./test/example/collection-info.json");
            }
            BSON_ASSERT(ctx->type == _MONGOCRYPT_TYPE_ENCRYPT);
            BSON_ASSERT(mongocrypt_ctx_mongo_feed(ctx, bin));
            BSON_ASSERT(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
            if (tester->paths.mongocryptd_reply) {
                bin = TEST_FILE(tester->paths.mongocryptd_reply);
            } else {
                bin = TEST_FILE("./test/example/mongocryptd-reply.json");
            }
            BSON_ASSERT(ctx->type == _MONGOCRYPT_TYPE_ENCRYPT);
            res = mongocrypt_ctx_mongo_feed(ctx, bin);
            mongocrypt_ctx_status(ctx, status);
            ASSERT_OR_PRINT(res, status);
            BSON_ASSERT(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
            if (tester->paths.key_file) {
                bin = TEST_FILE(tester->paths.key_file);
            } else {
                bin = TEST_FILE("./test/example/key-document.json");
            }
            res = mongocrypt_ctx_mongo_feed(ctx, bin);
            mongocrypt_ctx_status(ctx, status);
            ASSERT_OR_PRINT(res, status);
            BSON_ASSERT(mongocrypt_ctx_mongo_done(ctx));
            break;
        case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
            bin = TEST_BSON("{}");
            mongocrypt_ctx_provide_kms_providers(ctx, bin);
            break;
        case MONGOCRYPT_CTX_NEED_KMS:
            kms = mongocrypt_ctx_next_kms_ctx(ctx);
            while (kms) {
                _mongocrypt_tester_satisfy_kms(tester, kms);
                kms = mongocrypt_ctx_next_kms_ctx(ctx);
            }
            res = mongocrypt_ctx_kms_done(ctx);
            mongocrypt_ctx_status(ctx, status);
            ASSERT_OR_PRINT(res, status);
            break;
        case MONGOCRYPT_CTX_READY:
            bin = mongocrypt_binary_new();
            res = mongocrypt_ctx_finalize(ctx, bin);
            mongocrypt_ctx_status(ctx, status);
            ASSERT_OR_PRINT(res, status);
            mongocrypt_binary_destroy(bin);
            break;
        case MONGOCRYPT_CTX_ERROR:
            mongocrypt_ctx_status(ctx, status);
            TEST_STDERR_PRINTF("Got error: %s\n", mongocrypt_status_message(status, NULL));
            ASSERT_STATE_EQUAL(state, stop_state);
            mongocrypt_status_destroy(status);
            return;
        case MONGOCRYPT_CTX_DONE:
            ASSERT_STATE_EQUAL(state, stop_state);
            mongocrypt_status_destroy(status);
            return;
        default: BSON_ASSERT(false && "Invalid state");
        }
        state = mongocrypt_ctx_state(ctx);
    }
    ASSERT_STATE_EQUAL(state, stop_state);
    mongocrypt_status_destroy(status);
}

/* Get the plaintext associated with the encrypted doc for assertions. */
const char *_mongocrypt_tester_plaintext(_mongocrypt_tester_t *tester) {
    bson_t as_bson;
    bson_iter_t iter;
    _mongocrypt_marking_t marking;
    _mongocrypt_buffer_t buf;
    mongocrypt_status_t *status;

    BSON_ASSERT(_mongocrypt_binary_to_bson(TEST_FILE("./test/example/mongocryptd-reply.json"), &as_bson));
    /* Underlying binary data lives on in tester */
    BSON_ASSERT(bson_iter_init(&iter, &as_bson));
    BSON_ASSERT(bson_iter_find_descendant(&iter, "result.filter.ssn", &iter));
    BSON_ASSERT(_mongocrypt_buffer_from_binary_iter(&buf, &iter));
    status = mongocrypt_status_new();
    ASSERT_OR_PRINT(_mongocrypt_marking_parse_unowned(&buf, &marking, status), status);
    mongocrypt_status_destroy(status);
    BSON_ASSERT(BSON_ITER_HOLDS_UTF8(&marking.u.fle1.v_iter));
    return bson_iter_utf8(&marking.u.fle1.v_iter, NULL);
}

mongocrypt_binary_t *_mongocrypt_tester_encrypted_doc(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;

    bin = mongocrypt_binary_new();
    if (!_mongocrypt_buffer_empty(&tester->encrypted_doc)) {
        _mongocrypt_buffer_to_binary(&tester->encrypted_doc, bin);
        return bin;
    }

    crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_encrypt_init(ctx, "test", -1, TEST_FILE("./test/example/cmd.json")), ctx);

    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_READY);
    mongocrypt_ctx_finalize(ctx, bin);
    _mongocrypt_buffer_copy_from_binary(&tester->encrypted_doc, bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    _mongocrypt_buffer_to_binary(&tester->encrypted_doc, bin);
    return bin;
}

void _mongocrypt_tester_fill_buffer(_mongocrypt_buffer_t *buf, int n) {
    uint8_t i;

    memset(buf, 0, sizeof(*buf));
    buf->data = bson_malloc(n);
    BSON_ASSERT(buf->data);

    for (i = 0; i < n; i++) {
        buf->data[i] = i;
    }
    buf->len = n;
    buf->owned = true;
}

#define PRIVATE_KEY_FOR_TESTING                                                                                        \
    "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4JOyv5z05cL18ztpknRC7C"                                        \
    "FY2gYol4DAKerdVUoDJxCTmFMf39dVUEqD0WDiw/qcRtSO1/"                                                                 \
    "FRut08PlSPmvbyKetsLoxlpS8lukSzEFpFK7+L+R4miFOl6HvECyg7lbC1H/"                                                     \
    "WGAhIz9yZRlXhRo9qmO/"                                                                                             \
    "fB6PV9IeYtU+"                                                                                                     \
    "1xYuXicjCDPp36uuxBAnCz7JfvxJ3mdVc0vpSkbSb141nWuKNYR1mgyvvL6KzxO6mYsCo4hRA"                                        \
    "dhuizD9C4jDHk0V2gDCFBk0h8SLEdzStX8L0jG90/Og4y7J1b/cPo/"                                                           \
    "kbYokkYisxe8cPlsvGBf+rZex7XPxc1yWaP080qeABJb+S88O//"                                                              \
    "LAgMBAAECggEBAKVxP1m3FzHBUe2NZ3fYCc0Qa2zjK7xl1KPFp2u4CU+"                                                         \
    "9sy0oZJUqQHUdm5CMprqWwIHPTftWboFenmCwrSXFOFzujljBO7Z3yc1WD3NJl1ZNepLcsRJ3"                                        \
    "WWFH5V+NLJ8Bdxlj1DMEZCwr7PC5+vpnCuYWzvT0qOPTl9RNVaW9VVjHouJ9Fg+"                                                  \
    "s2DrShXDegFabl1iZEDdI4xScHoYBob06A5lw0WOCTayzw0Naf37lM8Y4psRAmI46XLiF/"                                           \
    "Vbuorna4hcChxDePlNLEfMipICcuxTcei1RBSlBa2t1tcnvoTy6cuYDqqImRYjp1KnMKlKQBn"                                        \
    "Q1NjS2TsRGm+F0FbreVCECgYEA4IDJlm8q/hVyNcPe4OzIcL1rsdYN3bNm2Y2O/"                                                  \
    "YtRPIkQ446ItyxD06d9VuXsQpFp9jNACAPfCMSyHpPApqlxdc8z/"                                                             \
    "xATlgHkcGezEOd1r4E7NdTpGg8y6Rj9b8kVlED6v4grbRhKcU6moyKUQT3+"                                                      \
    "1B6ENZTOKyxuyDEgTwZHtFECgYEA0fqdv9h9s77d6eWmIioP7FSymq93pC4umxf6TVicpjpME"                                        \
    "rdD2ZfJGulN37dq8FOsOFnSmFYJdICj/PbJm6p1i8O21lsFCltEqVoVabJ7/"                                                     \
    "0alPfdG2U76OeBqI8ZubL4BMnWXAB/"                                                                                   \
    "VVEYbyWCNpQSDTjHQYs54qa2I0dJB7OgJt1sCgYEArctFQ02/"                                                                \
    "7H5Rscl1yo3DBXO94SeiCFSPdC8f2Kt3MfOxvVdkAtkjkMACSbkoUsgbTVqTYSEOEc2jTgR3i"                                        \
    "Q13JgpHaFbbsq64V0QP3TAxbLIQUjYGVgQaF1UfLOBv8hrzgj45z/ST/"                                                         \
    "G80lOl595+0nCUbmBcgG1AEWrmdF0/"                                                                                   \
    "3RmECgYAKvIzKXXB3+19vcT2ga5Qq2l3TiPtOGsppRb2XrNs9qKdxIYvHmXo/"                                                    \
    "9QP1V3SRW0XoD7ez8FpFabp42cmPOxUNk3FK3paQZABLxH5pzCWI9PzIAVfPDrm+"                                                 \
    "sdnbgG7vAnwfL2IMMJSA3aDYGCbF9EgefG+"                                                                              \
    "STcpfqq7fQ6f5TBgLFwKBgCd7gn1xYL696SaKVSm7VngpXlczHVEpz3kStWR5gfzriPBxXgMV"                                        \
    "cWmcbajRser7ARpCEfbxM1UJyv6oAYZWVSNErNzNVb4POqLYcCNySuC6xKhs9FrEQnyKjyk8w"                                        \
    "I4VnrEMGrQ8e+qYSwYk9Gh6dKGoRMAPYVXQAO0fIsHF/T0a"

mongocrypt_t *_mongocrypt_tester_mongocrypt(tester_mongocrypt_flags flags) {
    mongocrypt_t *crypt;
    char localkey_data[MONGOCRYPT_KEY_LEN] = {0};
    mongocrypt_binary_t *localkey;
    bson_t *kms_providers;
    mongocrypt_binary_t *bin;

    crypt = mongocrypt_new();
    mongocrypt_setopt_log_handler(crypt, _mongocrypt_stdout_log_fn, NULL);
    mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1);
    mongocrypt_setopt_retry_kms(crypt, true);
    localkey = mongocrypt_binary_new_from_data((uint8_t *)localkey_data, sizeof localkey_data);
    mongocrypt_setopt_kms_provider_local(crypt, localkey);
    mongocrypt_binary_destroy(localkey);
    kms_providers = BCON_NEW("azure",
                             "{",
                             "tenantId",
                             "",
                             "clientId",
                             "",
                             "clientSecret",
                             "",
                             "}",
                             "gcp",
                             "{",
                             "email",
                             "test@example.com",
                             "privateKey",
                             PRIVATE_KEY_FOR_TESTING,
                             "}",
                             "kmip",
                             "{",
                             "endpoint",
                             "localhost",
                             "}");
    bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(kms_providers), kms_providers->len);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, bin), crypt);
    bson_destroy(kms_providers);
    mongocrypt_binary_destroy(bin);
    if (flags & TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB) {
        mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt, "$ORIGIN");
    }
    if (flags & TESTER_MONGOCRYPT_WITH_SHORT_CACHE) {
        ASSERT(mongocrypt_setopt_key_expiration(crypt, 1));
    }
    ASSERT_OK(mongocrypt_setopt_enable_multiple_collinfo(crypt), crypt);
    if (!(flags & TESTER_MONGOCRYPT_SKIP_INIT)) {
        ASSERT_OK(mongocrypt_init(crypt), crypt);
        if (flags & TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB) {
            if (mongocrypt_crypt_shared_lib_version(crypt) == 0) {
                BSON_ASSERT(false
                            && "tester mongocrypt requested WITH_CRYPT_SHARED_LIB, but "
                               "no crypt_shared library was loaded by mongocrypt_init");
            }
        }
    }
    return crypt;
}

bool _mongocrypt_init_for_test(mongocrypt_t *crypt) {
    BSON_ASSERT_PARAM(crypt);
    return mongocrypt_init(crypt);
}

static void _test_mongocrypt_bad_init(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_binary_t *local_key;
    char tmp;

    /* Omitting a KMS provider must fail. */
    crypt = mongocrypt_new();
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, "no kms provider set");
    mongocrypt_destroy(crypt);

    /* Bad KMS provider options must fail. */
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, NULL, -1),
                 crypt,
                 "invalid aws secret access key");
    mongocrypt_destroy(crypt);

    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_aws(crypt, NULL, -1, "example", -1),
                 crypt,
                 "invalid aws access key id");
    mongocrypt_destroy(crypt);

    /* Malformed UTF8 */
    /* An orphaned UTF-8 continuation byte (10xxxxxx) is malformed UTF-8. */
    tmp = (char)0x80;
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, &tmp, 1),
                 crypt,
                 "invalid aws secret access key");
    mongocrypt_destroy(crypt);

    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_local(crypt, NULL), crypt, "passed null key");
    mongocrypt_destroy(crypt);

    crypt = mongocrypt_new();
    local_key = mongocrypt_binary_new();
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_local(crypt, local_key), crypt, "local key must be 96 bytes");
    mongocrypt_binary_destroy(local_key);
    mongocrypt_destroy(crypt);

    /* Reinitialization must fail. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, "already initialized");
    mongocrypt_destroy(crypt);
    /* Setting options after initialization must fail. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    ASSERT_FAILS(mongocrypt_setopt_kms_provider_aws(crypt, "example", -1, "example", -1),
                 crypt,
                 "options cannot be set after initialization");
    mongocrypt_destroy(crypt);
}

static void _test_setopt_schema(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;

    /* Test double setting. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_FILE("./test/data/schema-map.json")), crypt);
    ASSERT_FAILS(mongocrypt_setopt_schema_map(crypt, TEST_FILE("./test/data/schema-map.json")),
                 crypt,
                 "already set schema");

    /* Test NULL/empty input */
    mongocrypt_destroy(crypt);
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_schema_map(crypt, NULL), crypt, "passed null schema");

    mongocrypt_destroy(crypt);
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_schema_map(crypt, TEST_BIN(0)), crypt, "passed null schema");

    /* Test malformed BSON */
    mongocrypt_destroy(crypt);
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_schema_map(crypt, TEST_BIN(10)), crypt, "invalid bson");
    mongocrypt_destroy(crypt);
}

static void _test_setopt_encrypted_field_config_map(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;

    /* Test success. */
    crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_OK(
        mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_FILE("./test/data/encrypted-field-config-map.json")),
        crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    mongocrypt_destroy(crypt);

    /* Test double setting. */
    crypt = mongocrypt_new();
    ASSERT_OK(
        mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_FILE("./test/data/encrypted-field-config-map.json")),
        crypt);
    ASSERT_FAILS(
        mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_FILE("./test/data/encrypted-field-config-map.json")),
        crypt,
        "already set encrypted_field_config_map");
    mongocrypt_destroy(crypt);

    /* Test NULL/empty input */
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_encrypted_field_config_map(crypt, NULL),
                 crypt,
                 "passed null encrypted_field_config_map");
    mongocrypt_destroy(crypt);

    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BIN(0)),
                 crypt,
                 "passed null encrypted_field_config_map");
    mongocrypt_destroy(crypt);

    /* Test malformed BSON */
    crypt = mongocrypt_new();
    ASSERT_FAILS(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BIN(10)), crypt, "invalid bson");
    mongocrypt_destroy(crypt);

    /* Test that it is OK to set both the encrypted field config map and schema
     * map if there are no intersecting collections. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_BSON("{'db.coll1': {}, 'db.coll2': {}}")), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll3': {}, 'db.coll3': {}}")),
              crypt);
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
    mongocrypt_destroy(crypt);

    /* Test that it is an error to set both the encrypted field config map and
     * schema map referencing the same collection. */
    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_schema_map(crypt, TEST_BSON("{'db.coll1': {}, 'db.coll2': {}}")), crypt);
    ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TEST_BSON("{'db.coll1': {}, 'db.coll3': {}}")),
              crypt);
    ASSERT_OK(
        mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{'aws': {'accessKeyId': 'foo', 'secretAccessKey': 'bar'}}")),
        crypt);
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt),
                 crypt,
                 "db.coll1 is present in both schema_map and encrypted_field_config_map");
    mongocrypt_destroy(crypt);
}

static void _test_setopt_invalid_kms_providers(_mongocrypt_tester_t *tester) {
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_status_t *status;

    crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "", 0, "", 0), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_masterkey_aws(ctx, "region", -1, "cmk", 3), ctx);
    mongocrypt_ctx_datakey_init(ctx);
    _mongocrypt_tester_run_ctx_to(tester, ctx, MONGOCRYPT_CTX_ERROR);

    status = mongocrypt_status_new();
    BSON_ASSERT(!mongocrypt_ctx_status(ctx, status));
    ASSERT_STATUS_CONTAINS(status, "failed to create KMS message");

    mongocrypt_status_destroy(status);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);

    crypt = mongocrypt_new();
    mongocrypt_setopt_use_need_kms_credentials_state(crypt);
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON("{}")), crypt);
    ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, "no kms provider set");
    mongocrypt_destroy(crypt);
}

typedef struct {
    char *value;
    /* errmsg is the expected error message from mongocrypt_setopt_kms_providers
     */
    char *errmsg;
    /* errmsg_init is the expected error message from mongocrypt_init */
    char *errmsg_init;
    bool use_need_kms_credentials_state;
} setopt_kms_providers_testcase_t;

#define EXAMPLE_LOCAL_MATERIAL                                                                                         \
    "TlPm8M/Uxs0eK13ReFeOUyxVC2rarCf5+LbKuY/dnFxS/"                                                                    \
    "LoYc1CZqnfSXujqqWrrt3fOTQ2TdtNhO4bBfamOyJPx4uJSstehc7It4pLp3LHes70z64AYqJ"                                        \
    "Uemk4G+2He"

static void _test_setopt_kms_providers(_mongocrypt_tester_t *tester) {
    setopt_kms_providers_testcase_t *test;
    size_t i;
    setopt_kms_providers_testcase_t tests[] = {
        {"{'azure': {'tenantId': '', 'clientId': '', 'clientSecret': '', "
         "'identityPlatformEndpoint': 'example.com' }}",
         NULL},
        {"{'azure': {'tenantId': '', 'clientId': '', 'clientSecret': '' }}", NULL},
        {"{'azure': {'tenantId': '', 'clientId': '', 'clientSecret': '', "
         "'identityPlatformEndpoint': 'example' }}",
         "Invalid endpoint"},
        {"{'azure': {'tenantId': '', 'clientSecret': '' }}", "clientId"},
        {"{'aws': {'accessKeyId': 'abc', 'secretAccessKey': 'def'}}", NULL},
        {"{'local': {'key': {'$binary': {'base64': '" EXAMPLE_LOCAL_MATERIAL "', 'subType': '00'}} }}", NULL},
        {"{'local': {'key': '" EXAMPLE_LOCAL_MATERIAL "' }}", NULL},
        {"{'local': {'key': 'invalid base64' }}", "unable to parse base64"},
        /* either base64 string or binary is acceptable for privateKey */
        {"{'gcp': {'endpoint': 'oauth2.googleapis.com', 'email': 'test', "
         "'privateKey': 'AAAA' }}"},
        {"{'gcp': {'endpoint': 'oauth2.googleapis.com', 'email': 'test', "
         "'privateKey': {'$binary': {'base64': 'AAAA', 'subType': '00'}} }}"},
        /* endpoint is not required. */
        {"{'gcp': {'email': 'test', 'privateKey': 'AAAA' }}"},
        {"{'gcp': {'privateKey': 'AAAA'}}", "Failed to parse KMS provider `gcp`: expected UTF-8 email"},
        {"{'gcp': {'email': 'test', 'privateKey': 'invalid base64' }}", "unable to parse base64"},
        {"{'gcp': {'endpoint': 'example', 'email': 'test', 'privateKey': "
         "'AAAA'}}",
         "Invalid endpoint"},
        {"{'azure': {'tenantId': '', 'clientId': '', 'clientSecret': '', "
         "'identityPlatformEndpoint': 'example.com', 'extra': 'invalid' }}",
         "Unexpected field: 'extra'"},
        {"{'aws': {'accessKeyId': 'abc', 'secretAccessKey': 'def', 'extra': "
         "'invalid'}}",
         "Unexpected field: 'extra'"},
        {"{'gcp': {'endpoint': 'oauth2.googleapis.com', 'email': 'test', "
         "'privateKey': 'AAAA', 'extra': 'invalid' }}",
         "Unexpected field: 'extra'"},
        {"{'local': {'key': '" EXAMPLE_LOCAL_MATERIAL "', 'extra': 'invalid' }}", "Unexpected field: 'extra'"},
        {"{'local': {'key': 'AAAA'}}", "local key must be 96 bytes"},
        /* KMIP test cases. */
        {"{'kmip': {'endpoint': '127.0.0.1:5696' }}", NULL},
        /* localhost is a valid endpoint for KMIP.
         * Unlike Azure, GCP, and AWS, applications run their own KMIP servers. */
        {"{'kmip': {'endpoint': 'localhost' }}", NULL},
        {"{'kmip': {'endpoint': '127.0.0.1:5696', 'extra': 'invalid' }}", "Unexpected field: 'extra'"},
        /* Empty documents are OK if on-demand KMS credentials are opted-in with
         * a call to mongocrypt_setopt_use_need_kms_credentials_state. */
        {"{'aws': {}}", NULL, NULL, true},
        {"{'azure': {}}", NULL, NULL, true},
        {"{'local': {}}", NULL, NULL, true},
        {"{'gcp': {}}", NULL, NULL, true},
        {"{'kmip': {}}", NULL, NULL, true},
        /* Empty documents are not OK if on-demand KMS credentials are not
           opted-in. */
        {"{'aws': {}}", NULL, "on-demand credentials not enabled", false},
        {"{'azure': {}}", NULL, "on-demand credentials not enabled", false},
        {"{'local': {}}", NULL, "on-demand credentials not enabled", false},
        {"{'gcp': {}}", NULL, "on-demand credentials not enabled", false},
        {"{'kmip': {}}", NULL, "on-demand credentials not enabled", false},
        {"{'gcp': {'accessToken': 'foobar', 'email': 'foo@bar.com' }}", "Unexpected field: 'email'"},
        {.value = "{ 'azure': { 'accessToken': 'secret' } }"},
    };

    for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
        mongocrypt_t *crypt;

        test = tests + i;
        crypt = mongocrypt_new();
        if (test->use_need_kms_credentials_state) {
            mongocrypt_setopt_use_need_kms_credentials_state(crypt);
        }
        if (!test->errmsg) {
            ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, TEST_BSON_STR(test->value)), crypt);
            if (!test->errmsg_init) {
                ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
            } else {
                ASSERT_FAILS(_mongocrypt_init_for_test(crypt), crypt, test->errmsg_init);
            }
        } else {
            ASSERT_FAILS(mongocrypt_setopt_kms_providers(crypt, TEST_BSON_STR(test->value)), crypt, test->errmsg);
        }
        mongocrypt_destroy(crypt);
    }

    // Errors if followed by call to `mongocrypt_setopt_kms_providers` configuring "local".
    // This is a regression test for: MONGOCRYPT-610
    {
        _mongocrypt_buffer_t local_kek_buf;
        // Create buffer for local KEK to pass data.
        {
            _mongocrypt_buffer_init(&local_kek_buf);
            _mongocrypt_buffer_resize(&local_kek_buf, MONGOCRYPT_KEY_LEN);
            int result_len =
                kms_message_b64_pton(EXAMPLE_LOCAL_MATERIAL, local_kek_buf.data, (size_t)local_kek_buf.len);
            ASSERT_CMPINT(result_len, ==, MONGOCRYPT_KEY_LEN);
        }

        mongocrypt_binary_t *more = TEST_BSON("{'local' : {'key' : '%s'}}", EXAMPLE_LOCAL_MATERIAL);
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_provider_local(crypt, _mongocrypt_buffer_as_binary(&local_kek_buf)), crypt);
        ASSERT_FAILS(mongocrypt_setopt_kms_providers(crypt, more), crypt, "already set");
        mongocrypt_destroy(crypt);
        _mongocrypt_buffer_cleanup(&local_kek_buf);
    }

    // Errors if followed by call to `mongocrypt_setopt_kms_providers` configuring "aws".
    // This is a regression test for: MONGOCRYPT-610
    {
        mongocrypt_binary_t *more = TEST_BSON("{'aws' : {'accessKeyId' : 'foo', 'secretAccessKey' : 'bar'}}");
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_provider_aws(crypt, "foo", -1, "bar", -1), crypt);
        ASSERT_FAILS(mongocrypt_setopt_kms_providers(crypt, more), crypt, "already set");
        mongocrypt_destroy(crypt);
    }
}

static void test_tmp_bsonf(_mongocrypt_tester_t *tester) {
    bson_t *one = TMP_BSONF("{'foo' : MC_STR}", "bar");
    ASSERT_EQUAL_BSON(one, TMP_BSONF("{'foo': 'bar'}"));
    bson_t *two = TMP_BSONF("{'blah': MC_BSON}", one);
    ASSERT_EQUAL_BSON(two, TMP_BSONF("{'blah': {'foo': 'bar'}}"));
}

bool _aes_ctr_is_supported_by_os = true;

int main(int argc, char **argv) {
    static _mongocrypt_tester_t tester = {0};
    int i;

    TEST_PRINTF("Pass a list of patterns to run a subset of tests.\n");
    TEST_PRINTF("Patterns may be exact matches or include a trailing wildcard (*). Examples:\n");
    TEST_PRINTF("  test-mongocrypt _mongocrypt_test_mcgrew\n");
    TEST_PRINTF("  test-mongocrypt 'test_mc_*'\n");
    TEST_PRINTF("  test-mongocrypt test_mc_named_kms_provider_parse test_mc_named_kms_provider_map\n");

    /* Install all tests. */
    _mongocrypt_tester_install_crypto(&tester);
    _mongocrypt_tester_install_log(&tester);
    _mongocrypt_tester_install_data_key(&tester);
    _mongocrypt_tester_install_ctx_encrypt(&tester);
    _mongocrypt_tester_install_ctx_decrypt(&tester);
    _mongocrypt_tester_install_ctx_rewrap_many_datakey(&tester);
    _mongocrypt_tester_install_ciphertext(&tester);
    _mongocrypt_tester_install_key_broker(&tester);
    _mongocrypt_tester_install(&tester, "_test_mongocrypt_bad_init", _test_mongocrypt_bad_init, CRYPTO_REQUIRED);
    _mongocrypt_tester_install_local_kms(&tester);
    _mongocrypt_tester_install_cache(&tester);
    _mongocrypt_tester_install_buffer(&tester);
    _mongocrypt_tester_install_ctx_setopt(&tester);
    _mongocrypt_tester_install_key(&tester);
    _mongocrypt_tester_install_marking(&tester);
    _mongocrypt_tester_install_traverse_util(&tester);
    _mongocrypt_tester_install(&tester, "_test_setopt_schema", _test_setopt_schema, CRYPTO_REQUIRED);
    _mongocrypt_tester_install(&tester,
                               "_test_setopt_encrypted_field_config_map",
                               _test_setopt_encrypted_field_config_map,
                               CRYPTO_REQUIRED);
    _mongocrypt_tester_install(&tester,
                               "_test_setopt_invalid_kms_providers",
                               _test_setopt_invalid_kms_providers,
                               CRYPTO_REQUIRED);
    _mongocrypt_tester_install_crypto_hooks(&tester);
    _mongocrypt_tester_install_key_cache(&tester);
    _mongocrypt_tester_install_kms_responses(&tester);
    _mongocrypt_tester_install_status(&tester);
    _mongocrypt_tester_install_endpoint(&tester);
    _mongocrypt_tester_install(&tester, "_test_setopt_kms_providers", _test_setopt_kms_providers, CRYPTO_OPTIONAL);
    _mongocrypt_tester_install_kek(&tester);
    _mongocrypt_tester_install_cache_oauth(&tester);
    _mongocrypt_tester_install_kms_ctx(&tester);
    _mongocrypt_tester_install_csfle_lib(&tester);
    _mongocrypt_tester_install_dll(&tester);
    _mongocrypt_tester_install_mc_tokens(&tester);
    _mongocrypt_tester_install_fle2_payloads(&tester);
    _mongocrypt_tester_install_fle2_iev_v2_payloads(&tester);
    _mongocrypt_tester_install_efc(&tester);
    _mongocrypt_tester_install_cleanup(&tester);
    _mongocrypt_tester_install_compact(&tester);
    _mongocrypt_tester_install_fle2_encryption_placeholder(&tester);
    _mongocrypt_tester_install_fle2_payload_uev(&tester);
    _mongocrypt_tester_install_fle2_payload_uev_v2(&tester);
    _mongocrypt_tester_install_fle2_payload_iup(&tester);
    _mongocrypt_tester_install_fle2_payload_iup_v2(&tester);
    _mongocrypt_tester_install_fle2_payload_find_equality_v2(&tester);
    _mongocrypt_tester_install_fle2_payload_find_range_v2(&tester);
    _mongocrypt_tester_install_fle2_payload_find_text(&tester);
    _mongocrypt_tester_install_fle2_tag_and_encrypted_metadata_block(&tester);
    _mongocrypt_tester_install_range_encoding(&tester);
    _mongocrypt_tester_install_range_edge_generation(&tester);
    _mongocrypt_tester_install_range_mincover(&tester);
    _mongocrypt_tester_install_mc_RangeOpts(&tester);
    _mongocrypt_tester_install_mc_TextOpts(&tester);
    _mongocrypt_tester_install_mc_FLE2RangeFindDriverSpec(&tester);
    _mongocrypt_tester_install_gcp_auth(&tester);
    _mongocrypt_tester_install_mc_reader(&tester);
    _mongocrypt_tester_install_mc_writer(&tester);
    _mongocrypt_tester_install_opts(&tester);
    _mongocrypt_tester_install_named_kms_providers(&tester);
    _mongocrypt_tester_install_mc_cmp(&tester);
    _mongocrypt_tester_install_text_search_str_encode(&tester);
    _mongocrypt_tester_install_unicode_fold(&tester);
    _mongocrypt_tester_install(&tester, "test_tmp_bsonf", test_tmp_bsonf, CRYPTO_OPTIONAL);
    _mongocrypt_tester_install_mc_schema_broker(&tester);

#ifdef MONGOCRYPT_ENABLE_CRYPTO_COMMON_CRYPTO
    char osversion[32];
    size_t osversion_len = sizeof(osversion) - 1;
    int osversion_name[] = {CTL_KERN, KERN_OSRELEASE};

    _aes_ctr_is_supported_by_os = false;

    if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
        goto get_os_version_failed;
    }

    uint32_t major, minor;
    if (sscanf(osversion, "%u.%u", &major, &minor) != 2) {
        goto get_os_version_failed;
    }

    if (major >= 20) {
        // macOS 11 and newer
        _aes_ctr_is_supported_by_os = true;
    } else {
        major -= 4;
        // macOS 10.1.1 and newer. CTR unsupported in 10.14 and earlier
        _aes_ctr_is_supported_by_os = major > 14;
    }
get_os_version_failed:
#endif

    TEST_PRINTF("Running tests...\n");
    for (i = 0; tester.test_names[i]; i++) {
        if (argc > 1) {
            for (int j = 1; j < argc; j++) {
                char *const pattern = argv[j];
                const size_t pattern_len = strlen(pattern);
                const bool match_prefix_only = pattern_len > 0u && pattern[pattern_len - 1u] == '*';

                if (match_prefix_only) {
                    if (0 == strncmp(pattern, tester.test_names[i], pattern_len - 1u)) {
                        goto found_match;
                    }
                } else {
                    if (0 == strcmp(pattern, tester.test_names[i])) {
                        goto found_match;
                    }
                }
            }

            continue; // No match found.
        }
    found_match : {}

        TEST_PRINTF("  begin %s\n", tester.test_names[i]);
        tester.test_fns[i](&tester);
        /* Clear state. */
        memset(&tester.paths, 0, sizeof(tester.paths));
        TEST_PRINTF("  end %s\n", tester.test_names[i]);
    }
    TEST_PRINTF("... done running tests\n");

    if (i == 0) {
        TEST_PRINTF("WARNING - no tests run.\n");
    }

    /* Clean up tester. */
    for (i = 0; i < tester.test_count; i++) {
        bson_free(tester.test_names[i]);
    }

    for (i = 0; i < tester.file_count; i++) {
        _mongocrypt_buffer_cleanup(&tester.file_bufs[i]);
        bson_free(tester.file_paths[i]);
    }

    for (i = 0; i < tester.bin_count; i++) {
        mongocrypt_binary_destroy(tester.test_bin[i]);
    }

    for (i = 0; i < tester.bson_count; i++) {
        bson_destroy(&tester.test_bson[i]);
    }

    for (i = 0; i < tester.blob_count; i++) {
        bson_free(tester.test_blob[i]);
    }

    _mongocrypt_buffer_cleanup(&tester.encrypted_doc);
}

void _test_ctx_wrap_and_feed_key(mongocrypt_ctx_t *ctx,
                                 const _mongocrypt_buffer_t *id,
                                 _mongocrypt_buffer_t *key,
                                 mongocrypt_status_t *status) {
    mc_kms_creds_t kc;
    ASSERT(_mongocrypt_opts_kms_providers_lookup(_mongocrypt_ctx_kms_providers(ctx), "local", &kc));
    // Wrap key using local provider.
    _mongocrypt_buffer_t kek = kc.value.local.key;
    _mongocrypt_buffer_t encrypted_key;
    _mongocrypt_buffer_init(&encrypted_key);
    ASSERT_OK_STATUS(_mongocrypt_wrap_key(ctx->crypt->crypto, &kek, key, &encrypted_key, status), status);

    bson_t doc;
    bson_init(&doc);
    ASSERT(bson_append_binary(&doc, "_id", (int)strlen("_id"), BSON_SUBTYPE_UUID, id->data, id->len));
    ASSERT(bson_append_binary(&doc,
                              "keyMaterial",
                              (int)strlen("keyMaterial"),
                              BSON_SUBTYPE_BINARY,
                              encrypted_key.data,
                              encrypted_key.len));
    ASSERT(bson_append_now_utc(&doc, "creationDate", (int)strlen("creationDate")));
    ASSERT(bson_append_now_utc(&doc, "updateDate", (int)strlen("updateDate")));
    ASSERT(bson_append_int32(&doc, "status", (int)strlen("status"), 0));
    bson_t masterKey;
    bson_init(&masterKey);
    ASSERT(bson_append_document_begin(&doc, "masterKey", (int)strlen("masterKey"), &masterKey));
    ASSERT(bson_append_utf8(&masterKey, "provider", (int)strlen("provider"), "local", (int)strlen("local")));
    ASSERT(bson_append_document_end(&doc, &masterKey));
    mongocrypt_binary_t *bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&doc), doc.len);
    ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, bin), ctx);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&doc);

    _mongocrypt_buffer_cleanup(&encrypted_key);
}
libmongocrypt-1.19.0/test/test-mongocrypt.h000066400000000000000000000260211521103432300207730ustar00rootroot00000000000000/*
 * Copyright 2019-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef TEST_MONGOCRYPT_H
#define TEST_MONGOCRYPT_H

#include 
#include 

#include "mongocrypt-buffer-private.h"
#include "mongocrypt-ctx-private.h"
#include "mongocrypt-key-broker-private.h"
#include "mongocrypt-kms-ctx-private.h"
#include "mongocrypt-private.h"
#include "mongocrypt.h"

#include "test-mongocrypt-assert.h"
#include "test-mongocrypt-util.h"

struct __mongocrypt_tester_t;
typedef void (*_mongocrypt_test_fn)(struct __mongocrypt_tester_t *tester);

typedef enum tester_mongocrypt_flags {
    /// Default settings for creating a mongocrypt_t for testing
    TESTER_MONGOCRYPT_DEFAULT = 0,
    /// Create a mongocrypt_t that has the crypt_shared library loaded. A
    /// crypt_shared library must be present in the same directory as the test
    /// executable.
    TESTER_MONGOCRYPT_WITH_CRYPT_SHARED_LIB = 1 << 0,
    /// Short cache expiration
    TESTER_MONGOCRYPT_WITH_SHORT_CACHE = 1 << 3,
    /// Do not call `mongocrypt_init` yet to allow for further configuration of the resulting `mongocrypt_t`.
    TESTER_MONGOCRYPT_SKIP_INIT = 1 << 4,
} tester_mongocrypt_flags;

/* Arbitrary max of 2148 instances of temporary test data. Increase as needed.
 */
#define TEST_DATA_COUNT 4096

typedef struct __mongocrypt_tester_t {
    int test_count;
    char *test_names[TEST_DATA_COUNT];
    _mongocrypt_test_fn test_fns[TEST_DATA_COUNT];

    int file_count;
    char *file_paths[TEST_DATA_COUNT];
    _mongocrypt_buffer_t file_bufs[TEST_DATA_COUNT];

    int bson_count;
    bson_t test_bson[TEST_DATA_COUNT];

    int bin_count;
    mongocrypt_binary_t *test_bin[TEST_DATA_COUNT];

    int blob_count;
    uint8_t *test_blob[TEST_DATA_COUNT];

    // Overides used in run_ctx_to
    struct {
        char *collection_info;
        char *mongocryptd_reply;
        char *key_file;
    } paths;

    /* Example encrypted doc. */
    _mongocrypt_buffer_t encrypted_doc;
} _mongocrypt_tester_t;

// `_mongocrypt_tester_t` inherits extended alignment from libbson. To dynamically allocate, use aligned allocation
// (e.g. BSON_ALIGNED_ALLOC)
BSON_STATIC_ASSERT2(alignof__mongocrypt_tester_t, BSON_ALIGNOF(_mongocrypt_tester_t) >= BSON_ALIGNOF(bson_t));

/* Load a .json file as bson */
void _load_json_as_bson(const char *path, bson_t *out);

void _mongocrypt_tester_satisfy_kms(_mongocrypt_tester_t *tester, mongocrypt_kms_ctx_t *kms);

void _mongocrypt_tester_run_ctx_to(_mongocrypt_tester_t *tester,
                                   mongocrypt_ctx_t *ctx,
                                   mongocrypt_ctx_state_t stop_state);

mongocrypt_binary_t *_mongocrypt_tester_encrypted_doc(_mongocrypt_tester_t *tester);

/* Return a repeated character with no null terminator. */
char *_mongocrypt_repeat_char(char c, uint32_t times);

void _mongocrypt_tester_fill_buffer(_mongocrypt_buffer_t *buf, int n);

/* Return a new initialized mongocrypt_t for testing. */
mongocrypt_t *_mongocrypt_tester_mongocrypt(tester_mongocrypt_flags options);

/* Initialize a new mongocrypt_t for use in testing. */
bool _mongocrypt_init_for_test(mongocrypt_t *);

typedef enum { CRYPTO_REQUIRED, CRYPTO_OPTIONAL, CRYPTO_PROHIBITED } _mongocrypt_tester_crypto_spec_t;

void _mongocrypt_tester_install(_mongocrypt_tester_t *tester,
                                char *name,
                                _mongocrypt_test_fn fn,
                                _mongocrypt_tester_crypto_spec_t crypto_spec);

const char *_mongocrypt_tester_plaintext(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_crypto(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_log(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_data_key(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ctx(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ctx_decrypt(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ctx_rewrap_many_datakey(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ciphertext(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_key_broker(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_local_kms(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_cache(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_buffer(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_ctx_setopt(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_key(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_marking(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_traverse_util(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_crypto_hooks(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_key_cache(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_kms_responses(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_status(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_csfle_lib(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_dll(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_endpoint(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_kek(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_cache_oauth(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_kms_ctx(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_tokens(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payloads(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_iev_v2_payloads(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_efc(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_cleanup(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_compact(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_encryption_placeholder(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_uev(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_uev_v2(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_iup(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_iup_v2(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_find_equality_v2(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_find_range_v2(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_payload_find_text(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_fle2_tag_and_encrypted_metadata_block(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_gcp_auth(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_range_encoding(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_range_edge_generation(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_range_mincover(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_RangeOpts(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_TextOpts(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_FLE2RangeFindDriverSpec(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_reader(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_writer(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_opts(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_named_kms_providers(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_cmp(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_text_search_str_encode(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_unicode_fold(_mongocrypt_tester_t *tester);

void _mongocrypt_tester_install_mc_schema_broker(_mongocrypt_tester_t *tester);

/* Conveniences for getting test data. */

/* Get a temporary bson_t from a JSON string. Do not free it. */
bson_t *_mongocrypt_tester_bson_from_str(_mongocrypt_tester_t *tester, const char *json);
#define TMP_BSON_STR(...) _mongocrypt_tester_bson_from_str(tester, __VA_ARGS__)

/* Get a temporary bson_t from a formattable JSON string. Do not free it. */
MLIB_ANNOTATE_PRINTF(2, 3)
bson_t *_mongocrypt_tester_bson_from_json(_mongocrypt_tester_t *tester, const char *json, ...);
#define TMP_BSON(...) _mongocrypt_tester_bson_from_json(tester, __VA_ARGS__)

// TMP_BSONF builds a temporary bson_t from an extended JSON string. Do not free it.
// Useful with BSON_STR to write JSON in-place.
// Supports tokens MC_BSON and MC_STR.
//
// Examples:
// bson_t *b = TMP_BSONF(BSON_STR({"foo": MC_STR}), "bar"); // { "foo" : "bar" }
// bson_t *b2 = TMP_BSONF(BSON_STR({"buzz": MC_BSON }), b); // { "buzz": { "foo": "bar" }}
bson_t *tmp_bsonf(_mongocrypt_tester_t *tester, const char *fmt, ...);
#define TMP_BSONF(...) tmp_bsonf(tester, __VA_ARGS__)

/* Get a temporary bson_t from a JSON file. Do not free it. */
bson_t *_mongocrypt_tester_file_as_bson(_mongocrypt_tester_t *tester, const char *path);
#define TEST_FILE_AS_BSON(path) _mongocrypt_tester_file_as_bson(tester, path)

/* Get a temporary binary from a JSON string. Do not free it. */
mongocrypt_binary_t *_mongocrypt_tester_bin_from_str(_mongocrypt_tester_t *tester, const char *json);
#define TEST_BSON_STR(...) _mongocrypt_tester_bin_from_str(tester, __VA_ARGS__)

/* Get a temporary binary from a formattable JSON string. Do not free it. */
MLIB_ANNOTATE_PRINTF(2, 3)
mongocrypt_binary_t *_mongocrypt_tester_bin_from_json(_mongocrypt_tester_t *tester, const char *json, ...);
#define TEST_BSON(...) _mongocrypt_tester_bin_from_json(tester, __VA_ARGS__)

/* Return a binary blob with the repeating sequence of 123. Do not free it. */
mongocrypt_binary_t *_mongocrypt_tester_bin(_mongocrypt_tester_t *tester, int size);
#define TEST_BIN(size) _mongocrypt_tester_bin(tester, size)

/* Return either a .json file as BSON or a .txt file as characters. Do not free
 * it. */
mongocrypt_binary_t *_mongocrypt_tester_file(_mongocrypt_tester_t *tester, const char *path);
#define TEST_FILE(path) _mongocrypt_tester_file(tester, path)

#define INSTALL_TEST(fn) _mongocrypt_tester_install(tester, #fn, fn, CRYPTO_REQUIRED)
#define INSTALL_TEST_CRYPTO(fn, crypto) _mongocrypt_tester_install(tester, #fn, fn, crypto)

void _load_json_as_bson(const char *path, bson_t *out);

extern bool _aes_ctr_is_supported_by_os;

void _test_ctx_wrap_and_feed_key(mongocrypt_ctx_t *ctx,
                                 const _mongocrypt_buffer_t *id,
                                 _mongocrypt_buffer_t *key,
                                 mongocrypt_status_t *status);

void _usleep(int64_t usec);

#endif
libmongocrypt-1.19.0/test/test-named-kms-providers.c000066400000000000000000003552701521103432300224670ustar00rootroot00000000000000/*
 * Copyright 2023-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 

#include 
#include 

#define LOCAL_KEK1_BASE64                                                                                              \
    "+ol0TFyLuVvKFSqGzOFGuaOGQnnyfAqalhOv3II/VSxQTCORCGhOmw/IxhthGx0r"                                                 \
    "2R/NpMWc91qQ8Ieho4QuE9ucToTnpJ4OquFpdZv2IcO4gey3ecZGCl9jPDig8F+a"

#define LOCAL_KEK2_BASE64                                                                                              \
    "yPSpsO8FoVkmt+qdTDnw/pJaKriwfI6NLD1yse3BZLd3ZcXb3rAVJEA+/yu/vPzE"                                                 \
    "8ju7OYTV63AwfLor8Hg9qzo8lyYC6H3RSfdJ9g9aXdCRfGZJgpbpchJUjR06JMLR"

// clang-format off
// GCP_PRIVATEKEY1 was generated with: `openssl genrsa -out private-key.pem 2048`
#define GCP_PRIVATEKEY1 "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCaqoCoH23dVS8see2DPOpHF3VHtKrXED2zcTkr+C15dDsw3hEl7123xwby/nSg08TMN9uzWkTaIP/CRNhN/VO3dmlCoRy/1Tyx8r3P7mELNPv7X6FP3MgcRMwSesvp7RYnTsImxQ6c48yTd2a4KnjFkJ9HkbOdxjoK2FENwPMdKNgU66XdzqIJBTeqSmx4FuTKdjQm3wi6vHYZgkZ1CKn90oDzpUwwIk3O4614Hxw8Gq40HgpwuTxKLFmfEgqyTHn54pw7plVivwwaUE2tXv3T4TIb0C8J9qquRtWSJuBVBM7yNucsNuWIXzW9jOT6PGcK30OIpkmf6n0Ib2pgY+WhAgMBAAECggEADypgDGwtg4YEZsPrX0KYNcGV90KevFEMPcXAvYAbpGS6X5WswIeerMQkGSxMbw8oxT4GackUhn91GJ1TyNzpyivfESCXXzOHXKsAw+xbwWOwABNdz8UGLahsyrSV/VFpKlBJjw/kOYvILd8HuE/40OV4CsZdgn9TBmh01SiJ5h5JtRP+kZycYluiUo0zLBReI8bpFPnWlCBDJJPE+UpFUCcifw/2xTkACJChtLmDDJ3NkUYACWQZ8HDf3UG3yE+OAGtP27srn1DYs6aNsr/dGfVU6Lu1mO8HswlSm4XHckdVdEmPaj/g5+TGBidnli8yMOhaPk5oDyD0DSltz4Ss2QKBgQDQ8xkFOCPQxnj6eO0s/8SXfoLQyCpHLqXNF7rw5H6VHo+5iyOt146XeLJNQeTYi/JX6bAlyPfc51ESZsjG1NucyLToJf7+Q+o/w/dNq3eNvGH1rxfYdalD4woQL+JQcgnelnMInyTCPS7drXbugl2AH2JzbwwyBHMB5KeW9paXSQKBgQC9fjo9G/ULae+w27aGSOYCnaFb0LlOJ5merjaZF3GkZPE3RhGIY19C2HRcjre4qRZbrPqyLkF8hoiLMTEd+n+vn8jUQaw/A67tVS1egnJlhut8BcFxW/jhmE80fIzRfxtRQhB/mevxTAIhhLUks3I7CZAjUabx9F+RAYLg3ZCjmQKBgBaj0Hk1TQQpDSCui5xNlkKH7aqrlZEi58oiIRpK18BWkGIdRl9mtMeKx18Bnccs2rRV2MUvUlP4KFujEWwh0i3ZvWhN/aQVPcNs+1XKF2kfGUoij6XfkdiOOB/q4E2xHYqlqI8tlzEIqhRQ4EsViwX/4I37YUnmG4P//3ym+UgpAoGAUMAx4UjArBSA6EU5CxCVtBeoY5AW549Ij6595c9vxjad9IgPgKeYOMw1ChxnfnHP7VFRpAzCK2bJWUelPrk5IIZe9tTlqhTPvqPFqbi9Nza/syJgxQYEkV5uoldRSxV3drFIhpf5S+KwJch/yRwPWclBe0uYcRNKhmi2dUz2DkECgYB4MazvGlNzdAGGt9ah+Ytq0ZZLaJlZMjlNZdsmiWn3Lhxy/l3ibTb1vALo7rQoH3Fpb55B+aiEPMtWVx0yVeIBaHmxQ+Q1Qes8vkL0ONAc4ZUPN9IVu2l8TE+cM6qzoZjpJMwK/JiFmqkE/Gss7gBgCr5JEQrXj2VecBSAcZXMwA=="
// GCP_PRIVATEKEY2 was generated with: `openssl genrsa -out private-key.pem 2048`
#define GCP_PRIVATEKEY2 "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4OWLdpC6AKXjskPiMa/oomIlT5RqeDvcDjQBKyhBTJ8cj3brB4xTBYVNAjBuHZeD8hKGs+6m4PPBxFEt5i7dPZNQZzDJ8Gk4xEkL4Ukgzx1rYwAI1f/Ef45gqUGZJC/kSCAUoGG5FUr+o23W6OCrx302ZVPGB+MbvO6cuTv6DlpHw3w9djpy4l6aMlj+JFCKnR7ai9yoAia8IYa0edMPi2HxdQZziHdlkw3wp+jBvN/706a24TzZKa1ewz2K2lQlc8G2iLy9+AcbHwYKo07ETKmu5azOr4yJ47aC2+SnXQzSFmDOJVTAq8/PXNQVscialqOdwMVpnWE5AFw/DxJibAgMBAAECgf9IZdgVXa7+XTg7lEWWkpdCZ9Xcdu44JJOiVwtcInRCexBzRoy5qv6w3+iIzzUzZXbyihHOhKCNkzm5uX6z1er1nP7OCV5xIEdpSEll60dUDH7gc6UurX3b/MPCauwT5CCphb63gyO9BNWkfk5Qt7HGyQ3/bVo/8u7TqJsYeeKA9B5j6LrnW/gITJGOSzq9j3PpWTMGbi0T57tMBZhRmncZ4jfDruXvPsZlXFnV2sfbtgCzpaOfAQ/CKa5PHz382b7sitEoKuwtYVBrTxLKTTUhRneGaK47lhBFsbEtqhicVYOUfplgE/wLrhmFcjRkmxAzPYq89QfaQ60LGiBrMUUCgYEA5NrMJgvife4DdhtFR2yiN3ufXJtqvMUtfZZAZoPD7fVz0VSwYEfkhuNCtt0U6CD3xjpWFOL372YycNI+apyhL3kL2Jnrpsh5mjjCnLoFqKVVgTWK9GFNSzVeO9LNjGJpR1nZDorZ2FHeUW4SAfMs5b1wek7xAnPrNHYEE8TgRx8CgYEAzhNgl++WWvj/pLKwLc/Gxz03rOoXYPnAnsfyO4g9F883Ps9uVgTro9EKeD9n+nPRbSRISZExe5P8EVUXrIpviO3oHE7fvdzoBfr2Vf5c/qQ0anD47RJODjJyKwrkuWxkxotPDWU/msZdyO7hf3v5BxevrP0Bzg2+zmbVE3dvKwUCgYEAt+Gdusw91hVSLqnGxpbg2Fe6OjyeTMLZxFjfsf8ZhK99uaqkdRgO5NrhlfCZhdJHg70HwYyEzpR83u3vPNZRJMXL4OP71myqWGJW7HsDZPhDdahB2A3+fvmIl+TPR4cjNDNbFjY2x3sweJlKWsq7PnUyVPPs7p2ZVPOmXwQHeN0CgYATslZxLz03xMTqgQnF1y4wrPE9XcKOSlDW3FWSyxrLw8qL/leVcTL0nW5av/S4Q4mo3Obr4SzRmvtkzLVOkIzIkbS1v/QyuYKTz8DrxzwsOpWn9tRUFIPRZ5Dx/ECQWIPpVjdgGGVT7dHY+rwi6z6KJwFrj2M0xquOHtYO3kOJ4QKBgQDDHjZTAyzMVWJiUDI6ENSKqTUDQh/UxI1zpMxp9qoHUwO8iHF4BLastquvK/sUuRbF6mI6z9/theDEqzP5Ytd5YrD2QOBJQhX8NDmO575+N7AldsxupVqjJWrivWo/cJMOwhLZ+OHLJjkfrZQSDjg70gS7AsFeLweLO3Aa9thVYw=="
// clang-format on

#define BSON_STR(...) #__VA_ARGS__

// `kmip_get_operation_type` returns a string representation of an Operation in a KMIP request.
// Useful for tests wanting to assert the type of KMIP request contained in a `mongocrypt_kms_ctx_t`.
static const char *kmip_get_operation_type(mongocrypt_binary_t *bin) {
    // Convert to hex for easier searching with `strstr`.
    char *as_hex = data_to_hex(mongocrypt_binary_data(bin), mongocrypt_binary_len(bin));
    const char *needle = "42005c"   // Tag=Operation
                         "05"       // Type=Enum
                         "00000004" // Length
                         "000000";  // First three bytes of four byte value.
    char *found = strstr(as_hex, needle);
    if (!found) {
        bson_free(as_hex);
        return "Not found";
    }
    // Read the next two hex characters.
    found += strlen(needle);
    const char *got = "Unknown";
    // Refer to section 9.1.3.2.26 of KMIP 1.0 specification for the Operation values.
    if (0 == strncmp(found, "01", 2)) {
        got = "Create";
    } else if (0 == strncmp(found, "02", 2)) {
        got = "Create_Key_Pair";
    } else if (0 == strncmp(found, "03", 2)) {
        got = "Register";
    } else if (0 == strncmp(found, "04", 2)) {
        got = "Rekey";
    } else if (0 == strncmp(found, "05", 2)) {
        got = "Derive_Key";
    } else if (0 == strncmp(found, "06", 2)) {
        got = "Certify";
    } else if (0 == strncmp(found, "07", 2)) {
        got = "Recertify";
    } else if (0 == strncmp(found, "08", 2)) {
        got = "Locate";
    } else if (0 == strncmp(found, "09", 2)) {
        got = "Check";
    } else if (0 == strncmp(found, "0a", 2)) {
        got = "Get";
    } else if (0 == strncmp(found, "0b", 2)) {
        got = "Get_Attributes";
    } else if (0 == strncmp(found, "0c", 2)) {
        got = "Get_Attribute_List";
    } else if (0 == strncmp(found, "0d", 2)) {
        got = "Add_Attribute";
    } else if (0 == strncmp(found, "0e", 2)) {
        got = "Modify_Attribute";
    } else if (0 == strncmp(found, "0f", 2)) {
        got = "Delete_Attribute";
    } else if (0 == strncmp(found, "10", 2)) {
        got = "Obtain_Lease";
    } else if (0 == strncmp(found, "11", 2)) {
        got = "Get_Usage_Allocation";
    } else if (0 == strncmp(found, "12", 2)) {
        got = "Activate";
    } else if (0 == strncmp(found, "13", 2)) {
        got = "Revoke";
    } else if (0 == strncmp(found, "14", 2)) {
        got = "Destroy";
    } else if (0 == strncmp(found, "15", 2)) {
        got = "Archive";
    } else if (0 == strncmp(found, "16", 2)) {
        got = "Recover";
    } else if (0 == strncmp(found, "17", 2)) {
        got = "Validate";
    } else if (0 == strncmp(found, "18", 2)) {
        got = "Query";
    } else if (0 == strncmp(found, "19", 2)) {
        got = "Cancel";
    } else if (0 == strncmp(found, "1a", 2)) {
        got = "Poll";
    } else if (0 == strncmp(found, "1b", 2)) {
        got = "Notify";
    } else if (0 == strncmp(found, "1c", 2)) {
        got = "Put";
    }
    bson_free(as_hex);
    return got;
}

/*
clang-format off

`KMIP_REGISTER_RESPONSE` represents:


 
  
   
   
  
  
  
 
 
  
  
  
   
  
 


clang-format on
*/
static const uint8_t KMIP_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, 0x65, 0x82, 0xec, 0x0b, 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, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/*
clang-format off

`KMIP_ACTIVATE_RESPONSE` represents:


 
  
   
   
  
  
  
 
 
  
  
  
   
  
 


clang-format on
*/
static const uint8_t KMIP_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, 0x65, 0x82, 0xec, 0x0b, 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, 0x12, 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, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/*
clang-format off

`KMIP_GET_RESPONSE` represents:


 
  
   
   
  
  
  
 
 
  
  
  
   
   
   
    
    
     
     
      
     
    
   
  
 


clang-format on
*/
static const uint8_t KMIP_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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
    0x00, 0x92, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x65, 0x82, 0xec, 0x0b, 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, 0x31, 0x32, 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, 0xe6, 0xe4, 0xb2, 0x50, 0x4f, 0xc6, 0x1c, 0x4e, 0x8e, 0x45, 0x69, 0x1b, 0x17, 0x19, 0xd5,
    0xd6, 0x61, 0x8b, 0x74, 0x91, 0x67, 0x10, 0x06, 0x83, 0xf4, 0xf8, 0xe5, 0x5d, 0xf1, 0xf2, 0x2a, 0xd3, 0xcb, 0xba,
    0x2a, 0xb4, 0x21, 0xed, 0x9d, 0xae, 0xb7, 0x3b, 0xc2, 0x5c, 0x03, 0x1a, 0xde, 0x82, 0x05, 0x80, 0xa5, 0xc7, 0x55,
    0x18, 0xab, 0x21, 0x37, 0x9b, 0x39, 0x36, 0xef, 0xfb, 0xcf, 0xcd, 0xa1, 0x46, 0xb2, 0x16, 0x71, 0xf2, 0xcf, 0x9f,
    0x80, 0x12, 0xb0, 0xe1, 0x83, 0xd7, 0x19, 0x16, 0x64, 0x81, 0x07, 0x5d, 0x6c, 0x39, 0x32, 0xa5, 0xae, 0xd5, 0x41,
    0x1f, 0x0c, 0x26, 0xe1, 0x57};

static void test_configuring_named_kms_providers(_mongocrypt_tester_t *tester) {
    // Test that a named KMS provider can be set.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"local" : {"key" : "%s"}, "local:name1" : {"key" : "%s"}}),
                      LOCAL_KEK1_BASE64,
                      LOCAL_KEK2_BASE64);
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(ok, crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        mongocrypt_destroy(crypt);
    }

    // Test that an unrecognized named KMS provider errors.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"foo:bar" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_FAILS(ok, crypt, "unrecognized type");
        mongocrypt_destroy(crypt);
    }

    // Test character validation. Only valid characters are: [a-zA-Z0-9_]
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"local:name_with_invalid_character_?" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_FAILS(ok, crypt, "unsupported character `?`");
        mongocrypt_destroy(crypt);
    }

    // Test configuring a named KMS provider with an empty document is prohibited.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"local:name1" : {}}));
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_FAILS(ok, crypt, "Unexpected empty document for named KMS provider");
        mongocrypt_destroy(crypt);

        // An empty document is allowed for a non-named KMS provider to configure on-demand credentials.
        crypt = mongocrypt_new();
        kms_providers = TEST_BSON(BSON_STR({"local" : {}}));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        mongocrypt_destroy(crypt);
    }

    // Test that duplicate named KMS providers is an error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"local:name1" : {"key" : "%s"}, "local:name1" : {"key" : "%s"}}),
                      LOCAL_KEK1_BASE64,
                      LOCAL_KEK2_BASE64);
        ASSERT_FAILS(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt, "duplicate entry");
        mongocrypt_destroy(crypt);
    }

    // Test that a named KMS provider can be set with Azure.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({
                          "local" : {"key" : "%s"},
                          "azure:name1" : {
                              "tenantId" : "placeholder1-tenantId",
                              "clientId" : "placeholder1-clientId",
                              "clientSecret" : "placeholder1-clientSecret",
                              "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
                          }
                      }),
                      LOCAL_KEK1_BASE64);
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(ok, crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        mongocrypt_destroy(crypt);
    }

    // Test that only configuring named KMS provider is OK.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"local:name1" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        bool ok = mongocrypt_setopt_kms_providers(crypt, kms_providers);
        ASSERT_OK(ok, crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);
        mongocrypt_destroy(crypt);
    }

    // Test configuring with an empty name is an error.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"local:" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        ASSERT_FAILS(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt, "empty name");
        mongocrypt_destroy(crypt);
    }
}

static void test_create_datakey_with_named_kms_provider(_mongocrypt_tester_t *tester) {
    // Test creating with an unconfigured KMS provider.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"local:name1" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:not_configured"}))),
            ctx);
        ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx),
                     ctx,
                     "requested kms provider not configured: `local:not_configured`");

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test creating with an unconfigured KMS provider, when provider of same type is configured.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"local" : {"key" : "%s"}, "local:name1" : {"key" : "%s"}}),
                      LOCAL_KEK1_BASE64,
                      LOCAL_KEK2_BASE64);
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:not_configured"}))),
            ctx);
        ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx),
                     ctx,
                     "requested kms provider not configured: `local:not_configured`");

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test creating with an unconfigured KMS provider, when provider of same type is configured with an
    // empty document.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"local" : {}, "local:name1" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        mongocrypt_setopt_use_need_kms_credentials_state(crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:not_configured"}))),
            ctx);
        ASSERT_FAILS(mongocrypt_ctx_datakey_init(ctx),
                     ctx,
                     "requested kms provider required by datakey is not configured: `local:not_configured`");

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test successfully creating a local DEK with a named KMS provider.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"local:name1" : {"key" : "%s"}}), LOCAL_KEK1_BASE64);
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:name1"}))),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        // Check that `out` contains name.
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        char *pattern = BSON_STR({"masterKey" : {"provider" : "local:name1"}});
        _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
        bson_destroy(&out_bson);
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test successfully creating an Azure DEK with a named KMS provider
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
            "azure:name1" : {
                "tenantId" : "placeholder1-tenantId",
                "clientId" : "placeholder1-clientId",
                "clientSecret" : "placeholder1-clientSecret",
                "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
            }
        }));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({
                                                               "provider" : "azure:name1",
                                                               "keyName" : "placeholder1-keyName",
                                                               "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                                           }))),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

        // Needs KMS for oauth token.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to encrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        // Check that `out` contains name.
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        char *pattern = BSON_STR({"masterKey" : {"provider" : "azure:name1"}});
        _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
        bson_destroy(&out_bson);
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test successfully creating an Azure DEK when `accessToken` is passed.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({"azure:name1" : {"accessToken" : "foo"}}));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({
                                                               "provider" : "azure:name1",
                                                               "keyName" : "placeholder1-keyName",
                                                               "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                                           }))),
                  ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

        // Needs KMS to encrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        // Check that `out` contains name.
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        char *pattern = BSON_STR({"masterKey" : {"provider" : "azure:name1"}});
        _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
        bson_destroy(&out_bson);
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test creating two Azure keys with different named providers.
    // This is intended to test that they do not share cache entries.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
            "azure:name1" : {
                "tenantId" : "placeholder1-tenantId",
                "clientId" : "placeholder1-clientId",
                "clientSecret" : "placeholder1-clientSecret",
                "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
            },
            "azure:name2" : {
                "tenantId" : "placeholder2-tenantId",
                "clientId" : "placeholder2-clientId",
                "clientSecret" : "placeholder2-clientSecret",
                "identityPlatformEndpoint" : "placeholder2-identityPlatformEndpoint.com"
            }
        }));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with `azure:name1`.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(
                mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({
                                                             "provider" : "azure:name1",
                                                             "keyName" : "placeholder1-keyName",
                                                             "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                                         }))),
                ctx);
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to encrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            // Check that `out` contains name.
            bson_t out_bson;
            ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
            char *pattern = BSON_STR({"masterKey" : {"provider" : "azure:name1"}});
            _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
            bson_destroy(&out_bson);
            mongocrypt_binary_destroy(out);
            mongocrypt_ctx_destroy(ctx);
        }

        // Create with `azure:name2`. Expect a separate oauth token is needed.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(
                mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({
                                                             "provider" : "azure:name2",
                                                             "keyName" : "placeholder2-keyName",
                                                             "keyVaultEndpoint" : "placeholder2-keyVaultEndpoint.com"
                                                         }))),
                ctx);
            ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to encrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *out = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
            // Check that `out` contains name.
            bson_t out_bson;
            ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
            char *pattern = BSON_STR({"masterKey" : {"provider" : "azure:name2"}});
            _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
            bson_destroy(&out_bson);
            mongocrypt_binary_destroy(out);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    // Test successfully creating an KMIP DEK with a named KMS provider
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"kmip:name1" : {"endpoint" : "placeholder1-endpoint.com"}}));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(
            mongocrypt_ctx_setopt_key_encryption_key(ctx,
                                                     TEST_BSON(BSON_STR({"provider" : "kmip:name1", "keyId" : "12"}))),
            ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

        // Needs KMS to Get KEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
            // Assert request is a Get.
            {
                mongocrypt_binary_t *request = mongocrypt_binary_new();
                ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                ASSERT_STREQUAL(kmip_get_operation_type(request), "Get");
                mongocrypt_binary_destroy(request);
            }
            // Feed response to Get.
            ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_GET_RESPONSE, (uint32_t)(sizeof(KMIP_GET_RESPONSE))), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        // Check that `out` contains name.
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        char *pattern = BSON_STR({"masterKey" : {"provider" : "kmip:name1"}});
        _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
        bson_destroy(&out_bson);
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test creating a KMIP key when `keyId` is not passed.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers =
            TEST_BSON(BSON_STR({"kmip:name1" : {"endpoint" : "placeholder1-endpoint.com"}}));
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create with named KMS provider.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "kmip:name1"}))), ctx);
        ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

        // Needs KMS to Register KEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
            // Assert request is a Register.
            {
                mongocrypt_binary_t *request = mongocrypt_binary_new();
                ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                ASSERT_STREQUAL(kmip_get_operation_type(request), "Register");
                mongocrypt_binary_destroy(request);
            }
            // Feed response to Register.
            ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_REGISTER_RESPONSE, (uint32_t)(sizeof(KMIP_REGISTER_RESPONSE))), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to Activate KEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
            // Assert request is a Activate.
            {
                mongocrypt_binary_t *request = mongocrypt_binary_new();
                ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                ASSERT_STREQUAL(kmip_get_operation_type(request), "Activate");
                mongocrypt_binary_destroy(request);
            }
            // Feed response to Activate.
            ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_ACTIVATE_RESPONSE, (uint32_t)(sizeof(KMIP_ACTIVATE_RESPONSE))), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to Get KEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
            // Assert request is a Get.
            {
                mongocrypt_binary_t *request = mongocrypt_binary_new();
                ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                ASSERT_STREQUAL(kmip_get_operation_type(request), "Get");
                mongocrypt_binary_destroy(request);
            }
            // Feed response to Get.
            ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_GET_RESPONSE, (uint32_t)(sizeof(KMIP_GET_RESPONSE))), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *out = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, out), ctx);
        // Check that `out` contains name.
        bson_t out_bson;
        ASSERT(_mongocrypt_binary_to_bson(out, &out_bson));
        char *pattern = BSON_STR({"masterKey" : {"provider" : "kmip:name1"}});
        _assert_match_bson(&out_bson, TMP_BSON_STR(pattern));
        bson_destroy(&out_bson);
        mongocrypt_binary_destroy(out);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }
}

typedef struct {
    mongocrypt_binary_t *kms_providers;
    const char *key_alt_name;
    mongocrypt_binary_t *kek;
    mongocrypt_binary_t *kms_response_1;
    mongocrypt_binary_t *kms_response_2;
} create_dek_args;

// `create_dek` is a test helper to create a Data Encryption Key (DEK).
static void create_dek(_mongocrypt_tester_t *tester, create_dek_args args, _mongocrypt_buffer_t *dek) {
    BSON_ASSERT_PARAM(args.kms_providers);
    BSON_ASSERT_PARAM(args.key_alt_name);
    BSON_ASSERT_PARAM(args.kek);
    // kms_response_1 and kms_response_2 may be NULL.

    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, args.kms_providers), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, args.kek), ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "%s"}), args.key_alt_name)),
              ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

    if (args.kms_response_1) {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx);
        ASSERT_OK(kms_ctx_feed_all(kctx,
                                   mongocrypt_binary_data(args.kms_response_1),
                                   mongocrypt_binary_len(args.kms_response_1)),
                  kctx);
        kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(!kctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    if (args.kms_response_2) {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx);
        ASSERT_OK(kms_ctx_feed_all(kctx,
                                   mongocrypt_binary_data(args.kms_response_2),
                                   mongocrypt_binary_len(args.kms_response_2)),
                  kctx);
        kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(!kctx);
        ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
    }

    ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
    mongocrypt_binary_t *bin = mongocrypt_binary_new();
    ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
    _mongocrypt_buffer_copy_from_binary(dek, bin);
    mongocrypt_binary_destroy(bin);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

static void test_explicit_with_named_kms_provider_for_azure(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
        "azure:name1" : {
            "tenantId" : "placeholder1-tenantId",
            "clientId" : "placeholder1-clientId",
            "clientSecret" : "placeholder1-clientSecret",
            "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
        },
        "azure:name2" : {
            "tenantId" : "placeholder2-tenantId",
            "clientId" : "placeholder2-clientId",
            "clientSecret" : "placeholder2-clientSecret",
            "identityPlatformEndpoint" : "placeholder2-identityPlatformEndpoint.com"
        }
    }));

    // Create `dek1` from `azure:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "azure1",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "azure:name1",
                                     "keyName" : "placeholder1-keyName",
                                     "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-azure/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-azure/encrypt-response.txt")},
               &dek1);

    // Create `dek2` from `azure:name2`
    _mongocrypt_buffer_t dek2;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "azure2",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "azure:name2",
                                     "keyName" : "placeholder2-keyName",
                                     "keyVaultEndpoint" : "placeholder2-keyVaultEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-azure/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-azure/encrypt-response.txt")},
               &dek2);

    // Test encrypting.
    _mongocrypt_buffer_t ciphertext;
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test encrypting without cached DEK. Store result for later decryption.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "azure1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            _mongocrypt_buffer_copy_from_binary(&ciphertext, bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test encrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "azure1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        mongocrypt_destroy(crypt);
    }

    // Test decrypting.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test decrypting without cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test decrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    // Test decrypting with a cached oauth token, but not a cached DEK.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Decrypt.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Recreate the `mongocrypt_ctx_t`. Expect the oauth token to be cached but the DEK not to be cached.
        mongocrypt_ctx_destroy(ctx);

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS to decrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *bin = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
        mongocrypt_binary_destroy(bin);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test encrypting with two different named Azure.
    // Expect two separate oauth token requests.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Encrypt with azure:name1
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "azure1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Encrypt with azure:name2
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "azure2"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek2)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-identityPlatformEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    // Test encrypting when access token is included in KMS providers.
    {
        mongocrypt_t *crypt = mongocrypt_new();

        mongocrypt_binary_t *kms_providers_withAccessToken =
            TEST_BSON(BSON_STR({"azure:name3_withAccessToken" : {"accessToken" : "placeholder3-accesstoken"}}));

        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers_withAccessToken), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create `dek3` from `azure:name3_withAccessToken`
        _mongocrypt_buffer_t dek3;
        create_dek(tester,
                   (create_dek_args){.kms_providers = kms_providers_withAccessToken,
                                     .key_alt_name = "azure3",
                                     .kek = TEST_BSON(BSON_STR({
                                         "provider" : "azure:name3_withAccessToken",
                                         "keyName" : "placeholder3-keyName",
                                         "keyVaultEndpoint" : "placeholder3-keyVaultEndpoint.com"
                                     })),
                                     // Does not need KMS for oauth token.
                                     .kms_response_1 = TEST_FILE("./test/data/kms-azure/encrypt-response.txt")},
                   &dek3);

        // Encrypt with `dek3`.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "azure3"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek3)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Does not need KMS for oauth token.

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder3-keyVaultEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        _mongocrypt_buffer_cleanup(&dek3);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&dek2);
    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_explicit_with_named_kms_provider_for_gcp(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers = TEST_BSON(
        BSON_STR({
            "gcp:name1" :
                {"email" : "placeholder1-email", "privateKey" : "%s", "endpoint" : "placeholder1-oauthEndpoint.com"},
            "gcp:name2" :
                {"email" : "placeholder2-email", "privateKey" : "%s", "endpoint" : "placeholder2-oauthEndpoint.com"}
        }),
        GCP_PRIVATEKEY1,
        GCP_PRIVATEKEY2);

    // Create `dek1` from `gcp:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "gcp1",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "gcp:name1",
                                     "projectId" : "placeholder1-projectId",
                                     "location" : "placeholder1-location",
                                     "keyRing" : "placeholder1-keyRing",
                                     "keyName" : "placeholder1-keyName",
                                     "endpoint" : "placeholder1-kmsEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-gcp/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-gcp/encrypt-response.txt")},
               &dek1);

    // Create `dek2` from `gcp:name2`
    _mongocrypt_buffer_t dek2;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "gcp2",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "gcp:name2",
                                     "projectId" : "placeholder2-projectId",
                                     "location" : "placeholder2-location",
                                     "keyRing" : "placeholder2-keyRing",
                                     "keyName" : "placeholder2-keyName",
                                     "endpoint" : "placeholder2-kmsEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-gcp/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-gcp/encrypt-response.txt")},
               &dek2);

    // Test encrypting.
    _mongocrypt_buffer_t ciphertext;
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test encrypting without cached DEK. Store result for later decryption.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-oauthEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-kmsEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            _mongocrypt_buffer_copy_from_binary(&ciphertext, bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test encrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        mongocrypt_destroy(crypt);
    }

    // Test decrypting.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test decrypting without cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-oauthEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-kmsEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test decrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    // Test decrypting with a cached oauth token, but not a cached DEK.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Decrypt.
        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-oauthEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Recreate the `mongocrypt_ctx_t`. Expect the oauth token to be cached but the DEK not to be cached.
        mongocrypt_ctx_destroy(ctx);

        ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS to decrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-kmsEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *bin = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
        ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
        mongocrypt_binary_destroy(bin);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test encrypting with two different named Gcp.
    // Expect two separate oauth token requests.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Encrypt with gcp:name1
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-oauthEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-kmsEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Encrypt with gcp:name2
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp2"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek2)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS for oauth token.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-oauthEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder2-kmsEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    // Test calling `mongocrypt_ctx_kms_done` before responding to an oauth request.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp1"}))), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_FAILS(mongocrypt_ctx_kms_done(ctx), ctx, "KMS response unfinished");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test calling `mongocrypt_ctx_kms_done` before responding to a decrypt request.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp1"}))), ctx);
        ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
        ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-oauthEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to decrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-kmsEndpoint.com:443");
            ASSERT_FAILS(mongocrypt_ctx_kms_done(ctx), ctx, "KMS response unfinished");
        }

        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    // Test encrypting when access token is included in KMS providers.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        mongocrypt_binary_t *kms_providers_withAccessToken =
            TEST_BSON(BSON_STR({"gcp:name3_withAccessToken" : {"accessToken" : "placeholder3-accesstoken"}}));

        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers_withAccessToken), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Create `dek3` from `gcp:name3_withAccessToken`
        _mongocrypt_buffer_t dek3;
        {
            create_dek(tester,
                       (create_dek_args){.kms_providers = kms_providers_withAccessToken,
                                         .key_alt_name = "gcp3",
                                         .kek = TEST_BSON(BSON_STR({
                                             "provider" : "gcp:name3_withAccessToken",
                                             "projectId" : "placeholder3-projectId",
                                             "location" : "placeholder3-location",
                                             "keyRing" : "placeholder3-keyRing",
                                             "keyName" : "placeholder3-keyName",
                                             "endpoint" : "placeholder3-kmsEndpoint.com"
                                         })),
                                         // Does not need KMS for oauth token.

                                         .kms_response_1 = TEST_FILE("./test/data/kms-gcp/encrypt-response.txt")},
                       &dek3);
        }

        // Encrypt with `dek3`.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "gcp3"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek3)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Does not need KMS for oauth token.

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder3-kmsEndpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-gcp/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        _mongocrypt_buffer_cleanup(&dek3);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&dek2);
    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_explicit_with_named_kms_provider_for_aws(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
        "aws:name1" : {"accessKeyId" : "placeholder1-aki", "secretAccessKey" : "placeholder1-sak"},
        "aws:name2" : {"accessKeyId" : "placeholder2-aki", "secretAccessKey" : "placeholder2-sak"}
    }));

    // Create `dek1` from `aws:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "aws1",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "aws:name1",
                                     "region" : "placeholder1-region",
                                     "key" : "placeholder1-key",
                                     "endpoint" : "placeholder1-endpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-aws/encrypt-response.txt")},
               &dek1);

    // Create `dek2` from `aws:name2`
    _mongocrypt_buffer_t dek2;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "aws2",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "aws:name2",
                                     "region" : "placeholder2-region",
                                     "key" : "placeholder2-key",
                                     "endpoint" : "placeholder2-endpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-aws/encrypt-response.txt")},
               &dek2);

    // Test encrypting.
    _mongocrypt_buffer_t ciphertext;
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test encrypting without cached DEK. Store result for later decryption.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "aws1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-aws/decrypt-response.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            _mongocrypt_buffer_copy_from_binary(&ciphertext, bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test encrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "aws1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        mongocrypt_destroy(crypt);
    }

    // Test decrypting.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test decrypting without cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS to decrypt DEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:443");
                ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/example/kms-decrypt-reply.txt")), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test decrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&dek2);
    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_explicit_with_named_kms_provider_for_kmip(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
        "kmip:name1" : {"endpoint" : "placeholder1-endpoint.com"},
        "kmip:name2" : {"endpoint" : "placeholder2-endpoint.com"}
    }));

    mongocrypt_binary_t *get_response =
        mongocrypt_binary_new_from_data((uint8_t *)KMIP_GET_RESPONSE, (uint32_t)sizeof(KMIP_GET_RESPONSE));

    // Create `dek1` from `kmip:name1`
    _mongocrypt_buffer_t dek1;

    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "kmip1",
                                 .kek = TEST_BSON(BSON_STR({"provider" : "kmip:name1", "keyId" : "12"})),
                                 .kms_response_1 = get_response},
               &dek1);

    // Create `dek2` from `kmip:name2`
    _mongocrypt_buffer_t dek2;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "kmip2",
                                 .kek = TEST_BSON(BSON_STR({"provider" : "kmip:name2", "keyId" : "12"})),
                                 .kms_response_1 = get_response},
               &dek2);

    // Test encrypting.
    _mongocrypt_buffer_t ciphertext;
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test encrypting without cached DEK. Store result for later decryption.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "kmip1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS to Get KEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
                // Assert request is a Get.
                {
                    mongocrypt_binary_t *request = mongocrypt_binary_new();
                    ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                    ASSERT_STREQUAL(kmip_get_operation_type(request), "Get");
                    mongocrypt_binary_destroy(request);
                }
                // Feed response to Get.
                ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_GET_RESPONSE, (uint32_t)(sizeof(KMIP_GET_RESPONSE))), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            _mongocrypt_buffer_copy_from_binary(&ciphertext, bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test encrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "kmip1"}))), ctx);
            ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_DETERMINISTIC_STR, -1), ctx);
            ASSERT_OK(mongocrypt_ctx_explicit_encrypt_init(ctx, TEST_BSON(BSON_STR({"v" : "foo"}))), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        mongocrypt_destroy(crypt);
    }

    // Test decrypting.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        // Test decrypting without cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
            ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
            ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

            // Needs KMS to Get KEK.
            {
                ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
                mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(kctx);
                const char *endpoint;
                ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
                ASSERT_STREQUAL(endpoint, "placeholder1-endpoint.com:5696");
                // Assert request is a Get.
                {
                    mongocrypt_binary_t *request = mongocrypt_binary_new();
                    ASSERT_OK(mongocrypt_kms_ctx_message(kctx, request), kctx);
                    ASSERT_STREQUAL(kmip_get_operation_type(request), "Get");
                    mongocrypt_binary_destroy(request);
                }
                // Feed response to Get.
                ASSERT_OK(kms_ctx_feed_all(kctx, KMIP_GET_RESPONSE, (uint32_t)(sizeof(KMIP_GET_RESPONSE))), kctx);
                kctx = mongocrypt_ctx_next_kms_ctx(ctx);
                ASSERT(!kctx);
                ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
            }

            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }

        // Test decrypting with cached DEK.
        {
            mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
            ASSERT_OK(mongocrypt_ctx_explicit_decrypt_init(ctx, _mongocrypt_buffer_as_binary(&ciphertext)), ctx);
            // DEK is already cached. State transitions directly to ready.
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
            mongocrypt_binary_t *bin = mongocrypt_binary_new();
            ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
            ASSERT_MONGOCRYPT_BINARY_EQUAL_BSON(TEST_BSON(BSON_STR({"v" : "foo"})), bin);
            mongocrypt_binary_destroy(bin);
            mongocrypt_ctx_destroy(ctx);
        }
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&ciphertext);
    _mongocrypt_buffer_cleanup(&dek2);
    _mongocrypt_buffer_cleanup(&dek1);
    mongocrypt_binary_destroy(get_response);
}

static void test_rewrap_with_named_kms_provider_local2local(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers =
        TEST_BSON(BSON_STR({"local:name1" : {"key" : "%s"}, "local:name2" : {"key" : "%s"}}),
                  LOCAL_KEK1_BASE64,
                  LOCAL_KEK2_BASE64);

    // Create `dek1` from `local:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "local1",
                                 .kek = TEST_BSON(BSON_STR({"provider" : "local:name1"}))},
               &dek1);

    // Rewrap `dek1` with `local:name2`.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:name2"}))),
                  ctx);
        mongocrypt_binary_t *filter = TEST_BSON(BSON_STR({}));
        ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *bin = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
        // Check resulting document contains expected name.
        {
            bson_t bin_bson;
            ASSERT(_mongocrypt_binary_to_bson(bin, &bin_bson));
            char *pattern = BSON_STR({"v" : [ {"masterKey" : {"provider" : "local:name2"}} ]});
            _assert_match_bson(&bin_bson, TMP_BSON_STR(pattern));
        }
        mongocrypt_binary_destroy(bin);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_rewrap_with_named_kms_provider_azure2azure(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers = TEST_BSON(BSON_STR({
        "azure:name1" : {
            "tenantId" : "placeholder1-tenantId",
            "clientId" : "placeholder1-clientId",
            "clientSecret" : "placeholder1-clientSecret",
            "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
        },
        "azure:name2" : {
            "tenantId" : "placeholder2-tenantId",
            "clientId" : "placeholder2-clientId",
            "clientSecret" : "placeholder2-clientSecret",
            "identityPlatformEndpoint" : "placeholder2-identityPlatformEndpoint.com"
        }
    }));

    // Create `dek1` from `azure:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "azure1",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "azure:name1",
                                     "keyName" : "placeholder1-keyName",
                                     "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-azure/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-azure/encrypt-response.txt")},
               &dek1);

    // Rewrap `dek1` with `azure:name2`.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({
                                                               "provider" : "azure:name2",
                                                               "keyName" : "placeholder2-keyName",
                                                               "keyVaultEndpoint" : "placeholder2-keyVaultEndpoint.com"
                                                           }))),
                  ctx);
        mongocrypt_binary_t *filter = TEST_BSON(BSON_STR({}));
        ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token from azure:name1.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to decrypt DEK from azure:name1.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS for oauth token from azure:name2.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder2-identityPlatformEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to encrypt DEK.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder2-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/encrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *bin = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
        // Check resulting document contains expected name.
        {
            bson_t bin_bson;
            ASSERT(_mongocrypt_binary_to_bson(bin, &bin_bson));
            char *pattern = BSON_STR({"v" : [ {"masterKey" : {"provider" : "azure:name2"}} ]});
            _assert_match_bson(&bin_bson, TMP_BSON_STR(pattern));
        }
        mongocrypt_binary_destroy(bin);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_rewrap_with_named_kms_provider_azure2local(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers =
        TEST_BSON(BSON_STR({
                      "azure:name1" : {
                          "tenantId" : "placeholder1-tenantId",
                          "clientId" : "placeholder1-clientId",
                          "clientSecret" : "placeholder1-clientSecret",
                          "identityPlatformEndpoint" : "placeholder1-identityPlatformEndpoint.com"
                      },
                      "local:name1" : {"key" : "%s"}
                  }),
                  LOCAL_KEK1_BASE64);

    // Create `dek1` from `azure:name1`
    _mongocrypt_buffer_t dek1;
    create_dek(tester,
               (create_dek_args){.kms_providers = kms_providers,
                                 .key_alt_name = "azure1",
                                 .kek = TEST_BSON(BSON_STR({
                                     "provider" : "azure:name1",
                                     "keyName" : "placeholder1-keyName",
                                     "keyVaultEndpoint" : "placeholder1-keyVaultEndpoint.com"
                                 })),
                                 .kms_response_1 = TEST_FILE("./test/data/kms-azure/oauth-response.txt"),
                                 .kms_response_2 = TEST_FILE("./test/data/kms-azure/encrypt-response.txt")},
               &dek1);

    // Rewrap `dek1` with `local:name1`.
    {
        mongocrypt_t *crypt = mongocrypt_new();
        ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
        ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

        mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
        ASSERT_OK(mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "local:name1"}))),
                  ctx);
        mongocrypt_binary_t *filter = TEST_BSON(BSON_STR({}));
        ASSERT_OK(mongocrypt_ctx_rewrap_many_datakey_init(ctx, filter), ctx);
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_MONGO_KEYS);
        ASSERT_OK(mongocrypt_ctx_mongo_feed(ctx, _mongocrypt_buffer_as_binary(&dek1)), ctx);
        ASSERT_OK(mongocrypt_ctx_mongo_done(ctx), ctx);

        // Needs KMS for oauth token from azure:name1.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-identityPlatformEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/oauth-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        // Needs KMS to decrypt DEK from azure:name1.
        {
            ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
            mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(kctx);
            const char *endpoint;
            ASSERT_OK(mongocrypt_kms_ctx_endpoint(kctx, &endpoint), kctx);
            ASSERT_STREQUAL(endpoint, "placeholder1-keyVaultEndpoint.com:443");
            ASSERT_OK(mongocrypt_kms_ctx_feed(kctx, TEST_FILE("./test/data/kms-azure/decrypt-response.txt")), kctx);
            kctx = mongocrypt_ctx_next_kms_ctx(ctx);
            ASSERT(!kctx);
            ASSERT_OK(mongocrypt_ctx_kms_done(ctx), ctx);
        }

        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_READY);
        mongocrypt_binary_t *bin = mongocrypt_binary_new();
        ASSERT_OK(mongocrypt_ctx_finalize(ctx, bin), ctx);
        // Check resulting document contains expected name.
        {
            bson_t bin_bson;
            ASSERT(_mongocrypt_binary_to_bson(bin, &bin_bson));
            char *pattern = BSON_STR({"v" : [ {"masterKey" : {"provider" : "local:name1"}} ]});
            _assert_match_bson(&bin_bson, TMP_BSON_STR(pattern));
        }
        mongocrypt_binary_destroy(bin);
        mongocrypt_ctx_destroy(ctx);
        mongocrypt_destroy(crypt);
    }

    _mongocrypt_buffer_cleanup(&dek1);
}

static void test_mongocrypt_kms_ctx_get_kms_provider(_mongocrypt_tester_t *tester) {
    mongocrypt_binary_t *kms_providers =
        TEST_BSON(BSON_STR({"kmip:name1" : {"endpoint" : "placeholder1-endpoint.com"}}));

    mongocrypt_t *crypt = mongocrypt_new();
    ASSERT_OK(mongocrypt_setopt_kms_providers(crypt, kms_providers), crypt);
    ASSERT_OK(_mongocrypt_init_for_test(crypt), crypt);

    mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt);
    ASSERT_OK(
        mongocrypt_ctx_setopt_key_encryption_key(ctx, TEST_BSON(BSON_STR({"provider" : "kmip:name1", "keyId" : "12"}))),
        ctx);
    ASSERT_OK(mongocrypt_ctx_setopt_key_alt_name(ctx, TEST_BSON(BSON_STR({"keyAltName" : "kmip1"}))), ctx);
    ASSERT_OK(mongocrypt_ctx_datakey_init(ctx), ctx);

    // Needs KMS to Get KEK.
    {
        ASSERT_STATE_EQUAL(mongocrypt_ctx_state(ctx), MONGOCRYPT_CTX_NEED_KMS);
        mongocrypt_kms_ctx_t *kctx = mongocrypt_ctx_next_kms_ctx(ctx);
        ASSERT(kctx);
        ASSERT_STREQUAL(mongocrypt_kms_ctx_get_kms_provider(kctx, NULL /* len */), "kmip:name1");
    }

    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
}

void _mongocrypt_tester_install_named_kms_providers(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_configuring_named_kms_providers);
    INSTALL_TEST(test_create_datakey_with_named_kms_provider);
    INSTALL_TEST(test_explicit_with_named_kms_provider_for_azure);
    INSTALL_TEST(test_explicit_with_named_kms_provider_for_gcp);
    INSTALL_TEST(test_explicit_with_named_kms_provider_for_aws);
    INSTALL_TEST(test_explicit_with_named_kms_provider_for_kmip);
    INSTALL_TEST(test_rewrap_with_named_kms_provider_local2local);
    INSTALL_TEST(test_rewrap_with_named_kms_provider_azure2azure);
    INSTALL_TEST(test_rewrap_with_named_kms_provider_azure2local);
    INSTALL_TEST(test_mongocrypt_kms_ctx_get_kms_provider);
}
libmongocrypt-1.19.0/test/test-unicode-fold.c000066400000000000000000000147151521103432300211440ustar00rootroot00000000000000/*
 * Copyright 2025-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-status-private.h"
#include "test-mongocrypt-assert.h"
#include "test-mongocrypt.h"
#include "unicode/fold.h"

#define TEST_UNICODE_FOLD(expected, expected_len, input, input_len, options)                                           \
    do {                                                                                                               \
        char *_buf;                                                                                                    \
        size_t _len;                                                                                                   \
        ASSERT_OR_PRINT(unicode_fold(input, input_len, options, &_buf, &_len, status), status);                        \
        TEST_PRINTF("Testing: input=%.*s, expected=%.*s, output=%.*s\n",                                               \
                    (int)input_len,                                                                                    \
                    input,                                                                                             \
                    (int)expected_len,                                                                                 \
                    expected,                                                                                          \
                    (int)_len,                                                                                         \
                    _buf);                                                                                             \
        ASSERT_CMPSIZE_T(_len, ==, expected_len);                                                                      \
        ASSERT_CMPBYTES((uint8_t *)_buf, _len, (uint8_t *)expected, expected_len);                                     \
        ASSERT_CMPUINT8((uint8_t)(_buf[_len]), ==, 0);                                                                 \
        bson_free(_buf);                                                                                               \
    } while (0)

#define TEST_UNICODE_FOLD_ALL_CASES(input, case_folded, dia_folded, both_folded)                                       \
    do {                                                                                                               \
        size_t _input_len = strlen(input);                                                                             \
        size_t _cf_len = strlen(case_folded);                                                                          \
        size_t _df_len = strlen(dia_folded);                                                                           \
        size_t _both_len = strlen(both_folded);                                                                        \
        TEST_UNICODE_FOLD(case_folded, _cf_len, input, _input_len, kUnicodeFoldToLower);                               \
        TEST_UNICODE_FOLD(dia_folded, _df_len, input, _input_len, kUnicodeFoldRemoveDiacritics);                       \
        TEST_UNICODE_FOLD(both_folded,                                                                                 \
                          _both_len,                                                                                   \
                          input,                                                                                       \
                          _input_len,                                                                                  \
                          (unicode_fold_options_t)(kUnicodeFoldToLower | kUnicodeFoldRemoveDiacritics));               \
    } while (0)

static void test_unicode_fold(_mongocrypt_tester_t *tester) {
    mongocrypt_status_t *status = mongocrypt_status_new();
    // Test all ascii chars.
    char *buf1 = bson_malloc0(2);
    char *buf2 = bson_malloc0(2);
    for (unsigned char ch = 0; ch <= 0x7f; ch++) {
        buf1[0] = ch;
        if (ch >= 'A' && ch <= 'Z') {
            // Caps
            buf2[0] = ch + 0x20;
            TEST_UNICODE_FOLD_ALL_CASES(buf1, buf2, buf1, buf2);
        } else if (ch == '^' || ch == '`') {
            // Diacritics
            TEST_UNICODE_FOLD_ALL_CASES(buf1, buf1, "", "");
        } else {
            // Characters with no transformations
            TEST_UNICODE_FOLD_ALL_CASES(buf1, buf1, buf1, buf1);
        }
    }
    bson_free(buf1);
    bson_free(buf2);
    TEST_UNICODE_FOLD_ALL_CASES("abc", "abc", "abc", "abc");
    // Tests of composed unicode
    TEST_UNICODE_FOLD_ALL_CASES("¿CUÃNTOS AÑOS tienes Tú?",
                                "¿cuántos años tienes tú?",
                                "¿CUANTOS ANOS tienes Tu?",
                                "¿cuantos anos tienes tu?");
    TEST_UNICODE_FOLD_ALL_CASES("СКОЛЬКО ТЕБЕ ЛЕТ?", "Ñколько тебе лет?", "СКОЛЬКО ТЕБЕ ЛЕТ?", "Ñколько тебе лет?");
    TEST_UNICODE_FOLD_ALL_CASES("Πόσο χÏονών είσαι?", "πόσο χÏονών είσαι?", "Ποσο χÏονων εισαι?", "ποσο χÏονων εισαι?");
    // Tests of decomposed unicode
    TEST_UNICODE_FOLD_ALL_CASES("Cafe\xcc\x81", "cafe\xcc\x81", "Cafe", "cafe");
    TEST_UNICODE_FOLD_ALL_CASES("CafE\xcc\x81", "cafe\xcc\x81", "CafE", "cafe");
    // Test string with null bytes
    TEST_UNICODE_FOLD("fo\0bar",
                      6,
                      "fo\0bar",
                      6,
                      (unicode_fold_options_t)(kUnicodeFoldToLower | kUnicodeFoldRemoveDiacritics));
    // Test strings with folded representations longer in bytes than the input
    TEST_UNICODE_FOLD("\xe2\xb1\xa6", 3, "\xc8\xbe", 2, kUnicodeFoldToLower);
    TEST_UNICODE_FOLD("\xf0\xa4\x8b\xae", 4, "\xef\xa9\xac", 3, kUnicodeFoldRemoveDiacritics);
    mongocrypt_status_destroy(status);
}

void _mongocrypt_tester_install_unicode_fold(_mongocrypt_tester_t *tester) {
    INSTALL_TEST(test_unicode_fold);
}
libmongocrypt-1.19.0/test/util/000077500000000000000000000000001521103432300164205ustar00rootroot00000000000000libmongocrypt-1.19.0/test/util/HELP.autogen000066400000000000000000000072571521103432300205470ustar00rootroot00000000000000"`csfle` is a small utility for testing the state machine with real data.\n"
"\n"
"To build `csfle`, ensure libmongoc is installed (in addition to libbson) and set the option -DENABLE_ONLINE_TESTS=ON when configuring with `cmake`.\n"
"\n"
"Options can also be provided through a config flag.\n"
"\n"
"```\n"
"Global options\n"
"    --options_file \n"
"        Alternative way to pass all options.\n"
"    --kms_providers_file \n"
"        Defaults to ~/.csfle/kms_providers.json\n"
"    --mongocryptd_uri \n"
"        Defaults to 'mongodb://localhost:27020'.\n"
"    --mongodb_uri \n"
"        Defaults to 'mongodb://localhost:27017'.\n"
"    --mongodb_keyvault_uri \n"
"        Defaults to 'mongodb://localhost:27017'.\n"
"    --keyvault_namespace \n"
"        Defaults to 'keyvault.datakeys'.\n"
"    --schema_map_file  (optional)\n"
"        Defaults to using remote schemas.\n"
"    --trace \n"
"        Defaults to false.\n"
"    --tls_ca_file \n"
"        Set a custom CA to verify server certificates in TLS connections. If not set, uses system defaults. Useful for KMIP.\n"
"    --tls_certificate_key_file \n"
"        The client certificate and private key. If not set, a client certificate is not sent in TLS connections. Useful for KMIP.\n"
"\n"
"csfle create_datakey\n"
"    --kms_provider \n"
"    --key_alt_names \n"
"    --key_material \n"
"\n"
"    AWS options.\n"
"    --aws_kek_region \n"
"    --aws_kek_key \n"
"    --aws_kek_endpoint \n"
"\n"
"    Azure options.\n"
"    --azure_kek_keyvaultendpoint \n"
"    --azure_kek_keyname \n"
"    --azure_kek_keyversion  (optional)\n"
"\n"
"    GCP options.\n"
"    --gcp_kek_endpoint \n"
"    --gcp_kek_projectid \n"
"    --gcp_kek_location \n"
"    --gcp_kek_keyring \n"
"    --gcp_kek_keyname \n"
"    --gcp_kek_keyversion  (optional)\n"
"\n"
"    KMIP options.\n"
"    --kmip_kek_endpoint \n"
"    --kmip_kek_keyid \n"
"    --kmip_kek_delegated \n"
"\n"
"csfle auto_encrypt\n"
"    --command  or --command_file \n"
"    --db \n"
"\n"
"csfle auto_decrypt\n"
"    --document  or --document_file \n"
"\n"
"csfle explicit_encrypt\n"
"    --value  Document must have form { 'v': ... }\n"
"    --key_id \n"
"    --key_alt_name \n"
"    --algorithm \n"
"\n"
"csfle explicit_decrypt\n"
"    --value  Document must have form { 'v': ... }\n"
"```\n"
"\n"
"\n"
"The KMS providers file must be extended canonical JSON of the following form.\n"
"\n"
"```\n"
"{\n"
"    'aws': {\n"
"        'accessKeyId': ,\n"
"        'secretAccessKey': \n"
"    }\n"
"\n"
"    'local': {\n"
"        'key': \n"
"    }\n"
"}\n"
"```\n"
"\n"
"No KMS providers are required.\n"
"\n"
"\n"
"## Examples\n"
"\n"
"```\n"
"csfle create_datakey --kms_provider aws --aws_kek_region us-east-1 --aws_kek_key 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0'\n"
"\n"
"csfle auto_encrypt --command '{'insert': 'coll', 'documents': [{'ssn': '123'}]}' --db 'db' --schema_map_file ./.csfle/schema_map.json\n"
"\n"
"csfle auto_decrypt --document '{ 'insert' : 'coll', 'documents' : [ { 'ssn' : { '$binary' : { 'base64': 'ARG+PK8ud0RZlDIzKwQmFoMCOuSIPyrfYleSqMZRXgaPCQOAurv0LTLNL6Tn/G7TuVOyf/Qv3j6VxSxCQEeu/yO7vv/UDE5niDE0itjOqjmf5Q==', 'subType' : '06' } } } ] }'\n"
"\n"
"csfle explicit_encrypt --key_id 'Eb48ry53RFmUMjMrBCYWgw==' --value '{'v': 'test'}' --algorithm 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic'\n"
"```\n"
libmongocrypt-1.19.0/test/util/README.md000066400000000000000000000064171521103432300177070ustar00rootroot00000000000000`csfle` is a small utility for testing the state machine with real data.

To build `csfle`, ensure libmongoc is installed (in addition to libbson) and set the option -DENABLE_ONLINE_TESTS=ON when configuring with `cmake`.

Options can also be provided through a config flag.

```
Global options
    --options_file 
        Alternative way to pass all options.
    --kms_providers_file 
        Defaults to ~/.csfle/kms_providers.json
    --mongocryptd_uri 
        Defaults to "mongodb://localhost:27020".
    --mongodb_uri 
        Defaults to "mongodb://localhost:27017".
    --mongodb_keyvault_uri 
        Defaults to "mongodb://localhost:27017".
    --keyvault_namespace 
        Defaults to "keyvault.datakeys".
    --schema_map_file  (optional)
        Defaults to using remote schemas.
    --trace 
        Defaults to false.
    --tls_ca_file 
        Set a custom CA to verify server certificates in TLS connections. If not set, uses system defaults. Useful for KMIP.
    --tls_certificate_key_file 
        The client certificate and private key. If not set, a client certificate is not sent in TLS connections. Useful for KMIP.

csfle create_datakey
    --kms_provider 
    --key_alt_names 
    --key_material 

    AWS options.
    --aws_kek_region 
    --aws_kek_key 
    --aws_kek_endpoint 

    Azure options.
    --azure_kek_keyvaultendpoint 
    --azure_kek_keyname 
    --azure_kek_keyversion  (optional)

    GCP options.
    --gcp_kek_endpoint 
    --gcp_kek_projectid 
    --gcp_kek_location 
    --gcp_kek_keyring 
    --gcp_kek_keyname 
    --gcp_kek_keyversion  (optional)

    KMIP options.
    --kmip_kek_endpoint 
    --kmip_kek_keyid 
    --kmip_kek_delegated 

csfle auto_encrypt
    --command  or --command_file 
    --db 

csfle auto_decrypt
    --document  or --document_file 

csfle explicit_encrypt
    --value  Document must have form { "v": ... }
    --key_id 
    --key_alt_name 
    --algorithm 

csfle explicit_decrypt
    --value  Document must have form { "v": ... }
```


The KMS providers file must be extended canonical JSON of the following form.

```
{
    "aws": {
        "accessKeyId": ,
        "secretAccessKey": 
    }

    "local": {
        "key": 
    }
}
```

No KMS providers are required.


## Examples

```
csfle create_datakey --kms_provider aws --aws_kek_region us-east-1 --aws_kek_key "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"

csfle auto_encrypt --command '{"insert": "coll", "documents": [{"ssn": "123"}]}' --db "db" --schema_map_file ./.csfle/schema_map.json

csfle auto_decrypt --document '{ "insert" : "coll", "documents" : [ { "ssn" : { "$binary" : { "base64": "ARG+PK8ud0RZlDIzKwQmFoMCOuSIPyrfYleSqMZRXgaPCQOAurv0LTLNL6Tn/G7TuVOyf/Qv3j6VxSxCQEeu/yO7vv/UDE5niDE0itjOqjmf5Q==", "subType" : "06" } } } ] }'

csfle explicit_encrypt --key_id "Eb48ry53RFmUMjMrBCYWgw==" --value '{"v": "test"}' --algorithm "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
```
libmongocrypt-1.19.0/test/util/csfle.c000066400000000000000000000415731521103432300176720ustar00rootroot00000000000000/*
 * Copyright 2020-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.
 */

#define MONGOC_LOG_DOMAIN "csfle"

#include 
#include 
#include 
#include 

#include "util.h"

static const char *help_text = ""
#include "HELP.autogen"
    ;

static void _exit_help(void) {
    printf("Usage: csfle  [options...]\n");
    printf("%s", help_text);
    exit(0);
}

static void set_kms_providers(mongocrypt_t *crypt, bson_t *args) {
    bson_t *kms_providers;
    mongocrypt_binary_t *bin;

    kms_providers = bson_get_json(args, "kms_providers_file");
    if (!kms_providers) {
        kms_providers = util_read_json_file(".csfle/kms_providers.json");
    }

    bin = util_bson_to_bin(kms_providers);
    if (!mongocrypt_setopt_kms_providers(crypt, bin)) {
        ERREXIT_MONGOCRYPT(crypt);
    }
    mongocrypt_binary_destroy(bin);

    bson_destroy(kms_providers);
}

static mongocrypt_t *crypt_new(bson_t *args) {
    mongocrypt_t *crypt;
    bson_t *schema_map;
    mongocrypt_binary_t *bin;

    crypt = mongocrypt_new();
    if (!mongocrypt_setopt_log_handler(crypt, _log_to_stdout, NULL)) {
        ERREXIT_MONGOCRYPT(crypt);
    }

    set_kms_providers(crypt, args);

    schema_map = bson_get_json(args, "schema_map_file");
    if (schema_map) {
        bin = util_bson_to_bin(schema_map);
        if (!mongocrypt_setopt_schema_map(crypt, bin)) {
            ERREXIT_MONGOCRYPT(crypt);
        }
        mongocrypt_binary_destroy(bin);
    }
    bson_destroy(schema_map);

    if (!mongocrypt_init(crypt)) {
        ERREXIT_MONGOCRYPT(crypt);
    }

    return crypt;
}

typedef struct {
    mongoc_client_t *keyvault_client;
    mongoc_client_t *mongocryptd_client;
    mongoc_client_t *mongodb_client;

    char *keyvault_db;
    char *keyvault_coll;

    _state_machine_t machine;
} state_t;

static void state_init(state_t *state, bson_t *args, mongocrypt_ctx_t *ctx) {
    const char *keyvault_ns;
    char *pos;

    state->keyvault_client = mongoc_client_new(bson_get_utf8(args, "mongodb_uri", "mongodb://localhost:27017"));
    state->mongocryptd_client = mongoc_client_new(bson_get_utf8(args, "mongocryptd_uri", "mongodb://localhost:27020"));
    state->mongodb_client = mongoc_client_new(bson_get_utf8(args, "mongodb_uri", "mongodb://localhost:27017"));

    keyvault_ns = bson_get_utf8(args, "keyvault_ns", "keyvault.datakeys");
    pos = strstr(keyvault_ns, ".");
    if (!pos) {
        ERREXIT("Key vault collection namespace invalid: %s", keyvault_ns);
    }
    state->keyvault_db = bson_strndup(keyvault_ns, pos - keyvault_ns);
    state->keyvault_coll = bson_strdup(pos + 1);

    state->machine.ctx = ctx;
    state->machine.keyvault_coll =
        mongoc_client_get_collection(state->keyvault_client, state->keyvault_db, state->keyvault_coll);
    state->machine.mongocryptd_client = state->mongocryptd_client;
    state->machine.collinfo_client = state->mongodb_client;
    state->machine.db_name = bson_get_utf8(args, "db", NULL);
    state->machine.trace = bson_get_bool(args, "trace", false);
    state->machine.tls_ca_file = bson_get_utf8(args, "tls_ca_file", NULL);
    state->machine.tls_certificate_key_file = bson_get_utf8(args, "tls_certificate_key_file", NULL);
}

static void state_cleanup(state_t *state) {
    mongoc_collection_destroy(state->machine.keyvault_coll);
    mongoc_client_destroy(state->keyvault_client);
    mongoc_client_destroy(state->mongocryptd_client);
    mongoc_client_destroy(state->mongodb_client);
    bson_free(state->keyvault_db);
    bson_free(state->keyvault_coll);
}

static void fn_createdatakey(bson_t *args) {
    state_t state;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    const char *kms_provider;
    bson_t result;
    bson_error_t error;
    char *result_utf8;
    mongocrypt_binary_t *bin;

    crypt = crypt_new(args);
    ctx = mongocrypt_ctx_new(crypt);

    kms_provider = bson_req_utf8(args, "kms_provider");

    if (bson_has_field(args, "key_material")) {
        const char *key_material_base64 = bson_get_utf8(args, "key_material", NULL);
        uint8_t key_material[512];

        int len = kms_message_b64_pton(key_material_base64, key_material, sizeof(key_material));
        if (len < 0) {
            ERREXIT("Could not base64 decode: %s", key_material_base64);
        }

        bson_t *key_material_bson = BCON_NEW("keyMaterial", BCON_BIN(BSON_SUBTYPE_BINARY, key_material, len));
        mongocrypt_binary_t *key_material_bin =
            mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(key_material_bson), key_material_bson->len);
        if (!mongocrypt_ctx_setopt_key_material(ctx, key_material_bin)) {
            ERREXIT_CTX(ctx);
        }
        bson_destroy(key_material_bson);
    }

    /* Set the key encryption key (KEK). */
    if (0 == strcmp("aws", kms_provider)) {
        bson_t aws_kek = BSON_INITIALIZER;

        BSON_APPEND_UTF8(&aws_kek, "provider", "aws");
        BSON_APPEND_UTF8(&aws_kek, "region", bson_req_utf8(args, "aws_kek_region"));
        BSON_APPEND_UTF8(&aws_kek, "key", bson_req_utf8(args, "aws_kek_key"));

        if (bson_has_field(args, "aws_kek_endpoint")) {
            BSON_APPEND_UTF8(&aws_kek, "endpoint", bson_get_utf8(args, "aws_kek_endpoint", NULL));
        }
        bin = util_bson_to_bin(&aws_kek);
        if (!mongocrypt_ctx_setopt_key_encryption_key(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
        bson_destroy(&aws_kek);
    } else if (0 == strcmp("local", kms_provider)) {
        bson_t local_kek = BSON_INITIALIZER;

        BSON_APPEND_UTF8(&local_kek, "provider", "local");
        bin = util_bson_to_bin(&local_kek);
        if (!mongocrypt_ctx_setopt_key_encryption_key(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
        bson_destroy(&local_kek);
    } else if (0 == strcmp("azure", kms_provider)) {
        bson_t azure_kek = BSON_INITIALIZER;

        BSON_APPEND_UTF8(&azure_kek, "provider", "azure");
        BSON_APPEND_UTF8(&azure_kek, "keyVaultEndpoint", bson_req_utf8(args, "azure_kek_keyvaultendpoint"));
        BSON_APPEND_UTF8(&azure_kek, "keyName", bson_req_utf8(args, "azure_kek_keyname"));
        if (bson_has_field(args, "azure_kek_keyversion")) {
            BSON_APPEND_UTF8(&azure_kek, "keyVersion", bson_req_utf8(args, "azure_kek_keyversion"));
        }

        bin = util_bson_to_bin(&azure_kek);
        if (!mongocrypt_ctx_setopt_key_encryption_key(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
        bson_destroy(&azure_kek);
    } else if (0 == strcmp("gcp", kms_provider)) {
        bson_t gcp_kek = BSON_INITIALIZER;

        BSON_APPEND_UTF8(&gcp_kek, "provider", "gcp");
        if (bson_has_field(args, "gcp_kek_endpoint")) {
            BSON_APPEND_UTF8(&gcp_kek, "endpoint", bson_req_utf8(args, "gcp_kek_endpoint"));
        }

        BSON_APPEND_UTF8(&gcp_kek, "projectId", bson_req_utf8(args, "gcp_kek_projectid"));
        BSON_APPEND_UTF8(&gcp_kek, "location", bson_req_utf8(args, "gcp_kek_location"));
        BSON_APPEND_UTF8(&gcp_kek, "keyRing", bson_req_utf8(args, "gcp_kek_keyring"));
        BSON_APPEND_UTF8(&gcp_kek, "keyName", bson_req_utf8(args, "gcp_kek_keyname"));

        if (bson_has_field(args, "gcp_kek_keyversion")) {
            BSON_APPEND_UTF8(&gcp_kek, "keyVersion", bson_req_utf8(args, "gcp_kek_keyversion"));
        }

        bin = util_bson_to_bin(&gcp_kek);
        if (!mongocrypt_ctx_setopt_key_encryption_key(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
        bson_destroy(&gcp_kek);
    } else if (0 == strcmp("kmip", kms_provider)) {
        bson_t kmip_kek = BSON_INITIALIZER;

        BSON_APPEND_UTF8(&kmip_kek, "provider", "kmip");
        if (bson_has_field(args, "kmip_kek_endpoint")) {
            BSON_APPEND_UTF8(&kmip_kek, "endpoint", bson_req_utf8(args, "kmip_kek_endpoint"));
        }

        if (bson_has_field(args, "kmip_kek_keyid")) {
            BSON_APPEND_UTF8(&kmip_kek, "keyId", bson_req_utf8(args, "kmip_kek_keyid"));
        }

        if (bson_has_field(args, "kmip_kek_delegated")) {
            BSON_APPEND_BOOL(&kmip_kek, "delegated", bson_get_bool(args, "kmip_kek_delegated", true));
        }

        bin = util_bson_to_bin(&kmip_kek);
        if (!mongocrypt_ctx_setopt_key_encryption_key(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
        bson_destroy(&kmip_kek);
    } else {
        ERREXIT("Unknown KMS provider: %s", kms_provider);
    }

    if (!mongocrypt_ctx_datakey_init(ctx)) {
        ERREXIT_CTX(ctx);
    }

    state_init(&state, args, ctx);

    if (state.machine.trace) {
        MONGOC_DEBUG("Running state machine");
    }

    if (!_csfle_state_machine_run(&state.machine, &result, &error)) {
        ERREXIT_BSON(&error);
    }

    if (state.machine.trace) {
        MONGOC_DEBUG("Finished running state machine");
    }

    if (!mongoc_collection_insert_one(state.machine.keyvault_coll, &result, NULL, NULL, &error)) {
        ERREXIT_BSON(&error);
    }

    result_utf8 = bson_as_canonical_extended_json(&result, NULL);
    printf("%s\n", result_utf8);
    bson_free(result_utf8);

    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    state_cleanup(&state);
}

static void fn_autoencrypt(bson_t *args) {
    state_t state;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    const char *db;
    mongocrypt_binary_t *bin;
    bson_t *cmd;
    bson_t result;
    bson_error_t error;
    char *result_utf8;

    crypt = crypt_new(args);
    ctx = mongocrypt_ctx_new(crypt);

    cmd = bson_get_json(args, "command_file");
    if (!cmd) {
        const char *cmd_utf8 = bson_req_utf8(args, "command");
        cmd = bson_new_from_json((const uint8_t *)cmd_utf8, strlen(cmd_utf8), &error);
        if (!cmd) {
            ERREXIT_BSON(&error);
        }
    }

    db = bson_req_utf8(args, "db");
    bin = util_bson_to_bin(cmd);
    if (!mongocrypt_ctx_encrypt_init(ctx, db, -1, bin)) {
        ERREXIT_CTX(ctx);
    }

    state_init(&state, args, ctx);

    if (state.machine.trace) {
        MONGOC_INFO("Running state machine");
    }

    if (!_csfle_state_machine_run(&state.machine, &result, &error)) {
        ERREXIT_BSON(&error);
    }

    if (state.machine.trace) {
        MONGOC_INFO("Finished running state machine");
    }

    result_utf8 = bson_as_canonical_extended_json(&result, NULL);
    printf("%s\n", result_utf8);
    bson_free(result_utf8);

    bson_destroy(cmd);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    state_cleanup(&state);
}

static void fn_autodecrypt(bson_t *args) {
    state_t state;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    mongocrypt_binary_t *bin;
    bson_t *doc;
    bson_t result;
    bson_error_t error;
    char *result_utf8;

    crypt = crypt_new(args);
    ctx = mongocrypt_ctx_new(crypt);

    doc = bson_get_json(args, "document_file");
    if (!doc) {
        const char *doc_utf8 = bson_req_utf8(args, "document");
        doc = bson_new_from_json((const uint8_t *)doc_utf8, strlen(doc_utf8), &error);
        if (!doc) {
            ERREXIT_BSON(&error);
        }
    }

    bin = util_bson_to_bin(doc);
    if (!mongocrypt_ctx_decrypt_init(ctx, bin)) {
        ERREXIT_CTX(ctx);
    }

    state_init(&state, args, ctx);

    if (state.machine.trace) {
        MONGOC_INFO("Running state machine");
    }

    if (!_csfle_state_machine_run(&state.machine, &result, &error)) {
        ERREXIT_BSON(&error);
    }

    if (state.machine.trace) {
        MONGOC_INFO("Finished running state machine");
    }

    result_utf8 = bson_as_canonical_extended_json(&result, NULL);
    printf("%s\n", result_utf8);
    bson_free(result_utf8);

    bson_destroy(doc);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    state_cleanup(&state);
}

static void fn_explicitencrypt(bson_t *args) {
    state_t state;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    const char *value;
    bson_t *value_doc;
    const char *key_id_base64;
    const char *key_alt_name;
    const char *algorithm;
    mongocrypt_binary_t *bin;
    bson_t result;
    bson_error_t error;
    char *result_utf8;
    uint8_t key_id[97];

    crypt = crypt_new(args);
    ctx = mongocrypt_ctx_new(crypt);
    value = bson_req_utf8(args, "value");
    value_doc = bson_new_from_json((const uint8_t *)value, strlen(value), &error);
    if (!value_doc) {
        ERREXIT_BSON(&error);
    }
    key_id_base64 = bson_get_utf8(args, "key_id", NULL);
    if (key_id_base64) {
        int len = kms_message_b64_pton(key_id_base64, key_id, sizeof(key_id));
        if (len < 0) {
            ERREXIT("Could not base64 decode: %s", key_id_base64);
        }
        bin = mongocrypt_binary_new_from_data(key_id, len);
        if (!mongocrypt_ctx_setopt_key_id(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
    }

    key_alt_name = bson_get_utf8(args, "key_alt_name", NULL);
    if (key_alt_name) {
        bson_t *wrapper;

        wrapper = BCON_NEW("keyAltName", key_alt_name);
        bin = util_bson_to_bin(wrapper);
        if (!mongocrypt_ctx_setopt_key_alt_name(ctx, bin)) {
            ERREXIT_CTX(ctx);
        }
        mongocrypt_binary_destroy(bin);
    }

    algorithm = bson_req_utf8(args, "algorithm");
    if (!mongocrypt_ctx_setopt_algorithm(ctx, algorithm, -1)) {
        ERREXIT_CTX(ctx);
    }

    bin = util_bson_to_bin(value_doc);
    if (!mongocrypt_ctx_explicit_encrypt_init(ctx, bin)) {
        ERREXIT_CTX(ctx);
    }

    state_init(&state, args, ctx);

    if (state.machine.trace) {
        MONGOC_INFO("Running state machine");
    }

    if (!_csfle_state_machine_run(&state.machine, &result, &error)) {
        ERREXIT_BSON(&error);
    }

    if (state.machine.trace) {
        MONGOC_INFO("Finished running state machine");
    }

    result_utf8 = bson_as_canonical_extended_json(&result, NULL);
    printf("%s\n", result_utf8);
    bson_free(result_utf8);

    bson_destroy(value_doc);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    state_cleanup(&state);
}

static void fn_explicitdecrypt(bson_t *args) {
    state_t state;
    mongocrypt_t *crypt;
    mongocrypt_ctx_t *ctx;
    const char *value;
    bson_t *value_doc;
    mongocrypt_binary_t *bin;
    bson_t result;
    bson_error_t error;
    char *result_utf8;

    crypt = crypt_new(args);
    ctx = mongocrypt_ctx_new(crypt);
    value = bson_req_utf8(args, "value");
    value_doc = bson_new_from_json((const uint8_t *)value, strlen(value), &error);
    if (!value_doc) {
        ERREXIT_BSON(&error);
    }

    bin = util_bson_to_bin(value_doc);
    if (!mongocrypt_ctx_explicit_decrypt_init(ctx, bin)) {
        ERREXIT_CTX(ctx);
    }

    state_init(&state, args, ctx);

    if (state.machine.trace) {
        MONGOC_INFO("Running state machine");
    }

    if (!_csfle_state_machine_run(&state.machine, &result, &error)) {
        ERREXIT_BSON(&error);
    }

    if (state.machine.trace) {
        MONGOC_INFO("Finished running state machine");
    }

    result_utf8 = bson_as_canonical_extended_json(&result, NULL);
    printf("%s\n", result_utf8);
    bson_free(result_utf8);

    bson_destroy(value_doc);
    mongocrypt_binary_destroy(bin);
    bson_destroy(&result);
    mongocrypt_ctx_destroy(ctx);
    mongocrypt_destroy(crypt);
    state_cleanup(&state);
}

int main(int argc, char **argv) {
    bson_t args;
    bson_t *options_file_bson;
    char *fn;

    mongoc_init();

    if (argc < 2) {
        _exit_help();
    }

    fn = argv[1];
    bson_init(&args);
    args_parse(&args, argc - 2, argv + 2);
    options_file_bson = bson_get_json(&args, "options_file");
    if (options_file_bson) {
        bson_concat(&args, options_file_bson);
    }

    if (0 == strcmp(fn, "create_datakey")) {
        fn_createdatakey(&args);
    } else if (0 == strcmp(fn, "auto_encrypt")) {
        fn_autoencrypt(&args);
    } else if (0 == strcmp(fn, "auto_decrypt")) {
        fn_autodecrypt(&args);
    } else if (0 == strcmp(fn, "explicit_encrypt")) {
        fn_explicitencrypt(&args);
    } else if (0 == strcmp(fn, "explicit_decrypt")) {
        fn_explicitdecrypt(&args);
    } else {
        ERREXIT("Unknown function: %s", fn);
    }

    bson_destroy(&args);
    bson_destroy(options_file_bson);

    mongoc_cleanup();
}
libmongocrypt-1.19.0/test/util/helpgen.py000066400000000000000000000003121521103432300204100ustar00rootroot00000000000000lines = []
for line in open ("README.md", "r"):
    line = line.replace("\"", "'")
    lines.append("\"" + line[:-1] + "\\n\"")

with open ("HELP.autogen", "w") as out:
    out.write ("\n".join(lines))
libmongocrypt-1.19.0/test/util/make_includes.py000066400000000000000000000652661521103432300216140ustar00rootroot00000000000000"""
Script to convert the MongoDB server test vector #include files into their
libmongocrypt C-equivalent counterpart.

Pass a single argument naming the "kind" of objects to parse.
Code is read from stdin and written to stdout::

    > python make_includes.py edges < from_server/edges.cstruct > for_libmongocrypt/edges.cstruct
    > python make_includes.py mincovers < from_server/mincovers.cstruct > for_libmongocrypt/mincovers.cstruct
"""

from __future__ import annotations

import argparse
import itertools
import json
import re
import sys
from itertools import chain
from typing import (Callable, Generic, Iterable, NamedTuple, Sequence, TypeVar,
                    Union, cast)

T = TypeVar('T')


class Number(NamedTuple):
    """Stores a numeric literal (integer or float)."""
    spell: str

    def __str__(self) -> str:
        return self.spell


class String(NamedTuple):
    """Stores a quoted string literal."""
    spell: str

    def __str__(self) -> str:
        return self.spell


class Ident(NamedTuple):
    """Stores a bare C-style identifier"""
    spell: str

    def __str__(self) -> str:
        return self.spell


class TmplIdent(NamedTuple):
    """
    Stores a template-specialization identifier.
    """
    name: str
    "The C identifier of the template's name"
    targs: Sequence[Expression]
    "The argument expressions given for the template specialization"

    def __str__(self) -> str:
        return f'{self.name}<{", ".join(map(str, self.targs))}>'


class PrefixExpr(NamedTuple):
    """Stores a unary prefix expression"""
    operator: str
    "The spelling of the prefix operator"
    operand: Expression
    "The expression on the right-hand side of the operator"

    def __str__(self) -> str:
        return f'{self.operator}{self.operand}'


class InfixExpr(NamedTuple):
    """Stores a binary infix expression"""
    lhs: Expression
    "The left-hand operand of the expression"
    oper: str
    "The spelling of the infix operator"
    rhs: Expression
    "The right-hand operand of the expression"

    def __str__(self) -> str:
        return f'{self.lhs} {self.oper} {self.rhs}'


class CallExpr(NamedTuple):
    """Stores a function-call expression"""
    fn: Expression
    "The callable expression being used as the function"
    args: Sequence[Expression]
    "The arguments being passed to the function"

    def __str__(self) -> str:
        return f'{self.fn}<{", ".join(map(str, self.args))}>'


class ScopeExpr(NamedTuple):
    """A scope-resolution '::' infix expression"""
    left: Expression
    "The left-hand operand of the scope-resolution"
    name: Ident | TmplIdent
    "The inner name being resolved"

    def __str__(self) -> str:
        return f'{self.left}::{self.name}'


class InitList(NamedTuple):
    """Stores a brace-enclosed initializer-list"""
    elems: Sequence[Expression]
    "The elements of the init-list"

    def __str__(self) -> str:
        return f'{{{", ".join(map(str, self.elems))}}}'


Expression = Union[PrefixExpr, CallExpr, String, Number, InitList, ScopeExpr,
                   InfixExpr, TmplIdent, Ident]
"An arbitrary expression (from a small subset of C++)"

BOOST_NONE = ScopeExpr(Ident('boost'), Ident('none'))
"Shorthand for the encoded 'boost::none' expression"


class EdgeInfo(NamedTuple):
    """Represents a single edge test vector from a C++ #include file"""
    func: Expression
    "The function that is used to generate the edge"
    value: Expression
    "The expression given as the first value"
    min: Expression
    "The min value for the edge"
    max: Expression
    "The max value for the edge"
    sparsity: Expression
    "The sparsity of the edge"
    precision: Expression
    "The precision of the edge"
    edges: Expression
    "The edges given for the test"


class MinCoverInfo(NamedTuple):
    """Represents a single mincover test vector"""
    lb: Expression
    "Lower bound for the operation, if provided"
    ub: Expression
    "Upper bound for the operation, if provided"
    min: Expression
    "Minimum value"
    max: Expression
    "Maximum value"
    sparsity: Expression
    "Sparsity of the mincover"
    precision: Expression
    "Optional: Precision of the range"
    expect_string: Expression
    "The expected result string"


class Token(NamedTuple):
    """A token consumed from the input"""
    spell: str
    "The spelling of the token"
    line: int
    "The line on which the token appears"

    @property
    def is_id(self) -> bool:
        """Is this an identifier?"""
        return IDENT_RE.match(self.spell) is not None

    @property
    def is_num(self) -> bool:
        """Is this a number?"""
        return NUM_RE.match(self.spell) is not None

    @property
    def is_str(self) -> bool:
        """Is this a string literal?"""
        return STRING_RE.match(self.spell) is not None

    def __repr__(self) -> str:
        return f''


IDENT_RE = re.compile(r'^[a-zA-Z_]\w*')
"Matches a C identifier"
NUM_RE = re.compile(
    r'''^
    # The unfortunate many ways to write a number
    # First: Floats:
    (
        ( \d+\.\d+  # Both whole and fractional part
        | \.\d+     # ".NNNN" (No whole part)
        | \d+\.     # "NNNN." (no fractional part)
        )
        # Optional: "f" suffix
        f?
    # Integers:
    )|(
        (
            0x              # Hex prefix
            [0-9a-fA-F']+   # Hex digits (with digit separator)

        |   0       # Octal prefix
            [0-7']*  # May have no digits. "0" is an octal literal oof.

        |   0b      # Binary prefix
            [01']   # Bits

        |   [1-9]   # Decimal integer
            [0-9']*
        )
        # There could be a type modifying suffix:
        [uU]?       # Unsigned
        [lL]{0,2}   # Long, or VERY long
    )
''', re.VERBOSE)
"Matches an integer or float numeric literal"
STRING_RE = re.compile(
    r'''
    # Regular strings:
    "(\\.|[^"])*"
  | # Raw strings:
    R
    "
    (?P.*?)  # The delimiter
    \(
       (?P(.|\s)*?)
    \)
    (?P=delim)  # Stop when we find the delimiter again
    "
''', re.VERBOSE)
"Matches a string literal"
LINE_COMMENT_RE = re.compile(r'^//.*?\n\s*')
WHITESPACE_RE = re.compile(r'[ \n\t\r\f]+')
"Matches one or more whitespace tokens"


def cquote(s: str) -> str:
    """Enquote a string to be used as a C string literal"""
    # Coincidentally, JSON strings are valid C strings
    return json.dumps(s)


def join_with(items: Iterable[T], by: Iterable[T]) -> Iterable[T]:
    """
    Yield every item X in 'items'. Between each X, yield each item in `by`.
    """
    STOP = object()
    # Iterate each item, and yield the sentinel STOP when we finish.
    # This is far easier than dancing around StopIteration.
    ch = chain(items, [STOP])
    it = iter(ch)
    item = next(it)
    while item is not STOP:
        yield cast(T, item)
        item = next(it)
        if item is not STOP:
            # Clone the iterable so we can re-iterate it again later:
            by, tee = itertools.tee(by, 2)
            yield from tee


class Scanner:
    """
    Consumes a given string, keeping track of line and column position information.
    """

    def __init__(self, s: str) -> None:
        self._str = s
        self._off = 0
        self._line = 1
        self._col = 1

    @property
    def string(self) -> str:
        """Return the current string content"""
        return self._str[self._off:]

    @property
    def line(self):
        """The current line number"""
        return self._line

    @property
    def col(self):
        """The current column number"""
        return self._col

    def consume(self, n: int) -> None:
        """
        Discard n characters from the input
        """
        skipped = self.string[:n]
        self._line += skipped.count('\n')
        nlpos = skipped.rfind('\n')
        if nlpos >= 0:
            self._col = len(skipped) - nlpos
        self._off += n

    def skipws(self):
        """
        Consume all whitespace at the beginning of the current input
        """
        ws = WHITESPACE_RE.match(self.string)
        if not ws:
            return
        self.consume(len(ws[0]))


def tokenize(sc: Scanner) -> Iterable[Token]:
    """Lazily extracts and yields tokens from the given code string"""
    # Discard leading space, of course:
    sc.skipws()
    while sc.string:
        # If we have a comment, just skip it:
        comment = LINE_COMMENT_RE.match(sc.string)
        if comment:
            sc.consume(len(comment[0]))
            sc.skipws()
            continue

        # Try to match basic primary tokens. A "real" C++ tokenizer needs to be
        # context-sensitive, but we only implement "just enough" to be useful.
        mat = (
            STRING_RE.match(sc.string)  #
            or IDENT_RE.match(sc.string)  #
            or NUM_RE.match(sc.string))
        if mat:
            # A basic token: Strings, identifiers, and number literals.
            tok = mat[0]
            yield Token(tok, sc.line)
            sc.consume(len(tok))
        elif sc.string.startswith('::'):
            # Scope resolution operator
            yield Token(sc.string[:2], sc.line)
            sc.consume(2)
        elif sc.string[0] in ',&}{[]().-<>+':
            # Basic one-character punctuators. We don't handle any digraphs.
            yield Token(sc.string[0], sc.line)
            sc.consume(1)
        else:
            # Unknown. Generate an error:
            snippet = sc.string[:min(len(sc.string), 30)]
            raise RuntimeError(f'Unknown token at {cquote(snippet)} '
                               f'(Line {sc.line}, column {sc.col})"')
        sc.skipws()


class LazyList(Generic[T]):
    """
    Create a forward-only list that lazily advances an iterable as items are
    requested, and drops items as they are discarded.
    """

    def __init__(self, it: Iterable[T]) -> None:
        self._iter = iter(it)
        self._acc: list[T | None] = []

    def at(self, n: int) -> T | None:
        """Return the Nth element from the sequence, or 'None' if at the end."""
        while n >= len(self._acc):
            # We need to add to the list. Append 'None' if there's nothing left
            self._acc.append(next(self._iter, None))
        return self._acc[n]

    def adv(self, n: int = 1) -> None:
        """Discard N elements from the beginning of the sequence."""
        self._acc = self._acc[n:]


Tokenizer = LazyList[Token]
"A token-generating lazy list"


def parse_ilist(toks: Tokenizer) -> InitList:
    """Parse a braced init-list, e.g. {1, 2, 3}"""
    lbr = toks.at(0)
    assert lbr and lbr.spell == '{', f'Expected left-brace "{{" (Got {lbr=})'
    toks.adv()
    acc: list[Expression] = []
    while 1:
        peek = toks.at(0)
        assert peek, 'Unexpected EOF parsing init-list'
        # If we see a closing brace, that's the end
        if peek.spell == '}':
            toks.adv()
            break
        # Expect an element:
        expr = parse_expr(toks)
        acc.append(expr)
        peek = toks.at(0)
        assert peek, 'Unexpected EOF parsing init-list'
        # We expect either a comma or a closing brace:
        assert peek.spell in (
            '}', ','
        ), f'Expected comma or closing brace following init-list element (Got "{peek=}")'
        if peek.spell == ',':
            # Just skip the comma. This may or may not be followed by another element.
            toks.adv()
    return InitList(acc)


def parse_call_args(toks: Tokenizer,
                    begin: str = '(',
                    end: str = ')') -> Sequence[Expression]:
    """
    Parse the argument list of a function/template call. The tokenizer must be
    positioned at the opening token. This function expects and will consume the
    closing token from the input.
    """
    lpar = toks.at(0)
    assert lpar and lpar.spell == begin, f'Expected opening "{begin}" (Got {lpar=})'
    toks.adv()
    acc: list[Expression] = []

    peek = toks.at(0)
    while peek and peek.spell != end:
        # Parse an argument:
        x = parse_expr(toks)
        acc.append(x)
        # We expect either a comma or a closing token next:
        peek = toks.at(0)
        assert peek, 'Unexpected EOF following argument in call expression'
        assert peek.spell in (
            ',', end
        ), f'Expected comma or "{end}" following argument (Got "{peek}")'
        # Skip over the comma, if present
        if peek.spell == ',':
            # Consume the comma:
            toks.adv()
    assert peek and peek.spell == end
    # Discard the closing token:
    toks.adv()
    return acc


def parse_nameid(toks: Tokenizer) -> Ident | TmplIdent:
    """
    Parse a name-id. This may be a bare identifier, or an identifier followed by
    template arguments. We don't handle less-than expressions correctly, but this
    dosen't matter for our current inputs. A more sofisticated system may be
    required later on.
    """
    idn = toks.at(0)
    assert idn and idn.is_id, f'Expected identifier beginning a nameid (Got {idn=}'
    toks.adv()
    angle = toks.at(0)
    if not angle or angle.spell != '<':
        # A regular identifier
        return Ident(idn.spell)
    # An identifier with template arguments:
    # (Or, an itentifier followed by a less-than symbol. We don't handle that
    # case, and don't currently need to.)
    targs = parse_call_args(toks, '<', '>')
    return TmplIdent(idn.spell, targs)


def parse_expr(toks: Tokenizer) -> Expression:
    """Parses an arbitrary C++-ish expression."""
    return parse_infix(toks)


def parse_infix(toks: Tokenizer) -> Expression:
    """Parse a binary infix-expression. Only handles leff-associativity."""
    x = parse_prefix_expr(toks)
    peek = toks.at(0)
    while peek:
        s = peek.spell
        if s not in '+-':
            break
        # Binary "+" or "-" (We don't care about other operators (yet))
        toks.adv()
        rhs = parse_prefix_expr(toks)
        x = InfixExpr(x, s, rhs)
        peek = toks.at(0)
    return x


def parse_prefix_expr(toks: Tokenizer) -> Expression:
    """Parses a unary prefix expression (currently, only '&' and '-' are handled)."""
    peek = toks.at(0)
    if peek and peek.spell in '-&':
        toks.adv()
        x = parse_prefix_expr(toks)
        return PrefixExpr(peek.spell, x)
    return parse_suffix_expr(toks)


def parse_suffix_expr(toks: Tokenizer) -> Expression:
    """Parses a suffix-expression (For now, that only includes call expressions and scope resolution)."""
    x = parse_primary_expr(toks)
    peek = toks.at(0)
    # Look ahead for scope resolution or function call (could also handle
    # dot '.' and subscript, but we don't care (yet))
    while peek:
        if peek.spell == '::':
            # Scope resolution:
            toks.adv()
            name = parse_nameid(toks)
            x = ScopeExpr(x, name)
        elif peek.spell == '(':
            # Function call:
            args = parse_call_args(toks)
            x = CallExpr(x, args)
        else:
            break
        peek = toks.at(0)
    return x


def parse_primary_expr(toks: Tokenizer) -> Expression:
    """Parses a primary expression (IDs, literals, parentheticals, and init-lists)."""
    x: Expression
    peek = toks.at(0)
    assert peek, 'Unexpected EOF when expected an expression'
    if peek.spell == '{':
        x = parse_ilist(toks)
    elif peek.is_str:
        x = String(peek.spell)
        toks.adv()
    elif peek.is_id:
        x = parse_nameid(toks)
    elif peek.is_num:
        x = Number(peek.spell)
        toks.adv()
    else:
        raise RuntimeError(f'Unknown expression beginning with token "{peek}"')
    return x


def parse_edges(toks: Tokenizer) -> Iterable[EdgeInfo]:
    """Lazily parses the edges from the given sequence of C++ tokens."""
    while toks.at(0):
        ilist = parse_expr(toks)
        assert isinstance(
            ilist, InitList
        ), f'Expected init-list for an edge element (Got {ilist!r})'
        fn, val, lb, ub, sparse, edges = ilist.elems
        yield EdgeInfo(
            fn,
            val,
            lb,
            ub,
            sparse,
            # Edges do not (yet) provide precision:
            BOOST_NONE,
            edges,
        )
        peek = toks.at(0)
        assert peek and peek.spell == ',', f'Expect a comma following edge element (Got {peek=})'
        toks.adv()


def parse_mincovers(toks: Tokenizer) -> Iterable[MinCoverInfo]:
    """Lazily parse MinCovert test vectors from the given C++ tokens."""
    while toks.at(0):
        ilist = parse_expr(toks)
        assert isinstance(
            ilist, InitList
        ), f'Expected init-list for a mincover element (Got {ilist!r})'
        # Grab the first five params:
        lb, ub, mn, mx, sparsity = ilist.elems[:5]
        # The precision element is optional, and may not be present:
        has_precision = len(ilist.elems) == 7
        prec = ilist.elems[5] if has_precision else BOOST_NONE
        # The final element is teh expectation:
        expect = ilist.elems[-1]
        yield MinCoverInfo(lb, ub, mn, mx, sparsity, prec, expect)
        peek = toks.at(0)
        assert peek and peek.spell == ',', f'Expected comma following edge element (Got {peek=})'
        toks.adv()


def _render_limit(typ: Expression, limit: Ident) -> Iterable[str]:
    """
    Render a value from std::numeric_limits<>. We only use a few of them so far.
    """
    if isinstance(typ, ScopeExpr) and str(typ.left) == 'std':
        typ = typ.name
    assert isinstance(typ,
                      Ident), f'Unimplemented type for numeric limits {typ=}'
    mapping: dict[tuple[str, str], Expression] = {
        ('int32_t', 'min'): Ident('INT32_MIN'),
        ('int64_t', 'min'): Ident('INT64_MIN'),
        ('double', 'min'): Ident('DBL_MIN'),
    }
    e = mapping[(typ.spell, limit.spell)]
    return _render_expr(e)


def _render_call(c: CallExpr) -> Iterable[str]:
    """
    Render a function call expression. This may render as some other arbitrary
    expression since we need to handle C++-isms.
    """
    # Intercept calls to numeric_limits:
    if (isinstance(c.fn, ScopeExpr)  #
            and c.args == []  #
            and isinstance(c.fn.left, ScopeExpr)  #
            and str(c.fn.left.left) == 'std'  #
            and isinstance(c.fn.left.name, TmplIdent)  #
            and c.fn.left.name.name == 'numeric_limits'):
        # We're looking for numeric limits
        assert isinstance(c.fn.name, Ident), f'Unimplemented limit: {c=}'
        return _render_limit(c.fn.left.name.targs[0], c.fn.name)

    if str(c.fn) == 'std::string':
        # We're constructing a std::string. All our inputs just use inline literals, so just render
        # those.
        assert len(c.args) == 1, c
        assert isinstance(c.args[0], String), c
        return _render_string(c.args[0])

    # Intercept calls to "Decimal128"
    if c.fn == Ident('Decimal128'):
        assert len(c.args) == 1, f'Too many args for Decimal128? {c=}'
        arg = c.args[0]
        if isinstance(arg, Number) and '.' not in arg.spell:
            # We can convert from an integer driectly
            return _render_call(CallExpr(Ident('MC_DEC128'), [arg]))
        if isinstance(arg, String):
            # They're passing a string to Decimal128(), so we do the same
            return _render_call(CallExpr(Ident('mc_dec128_from_string'),
                                         [arg]))
        # Other argument:
        assert isinstance(
            arg, (Number,
                  PrefixExpr)), f'Unimplemented argument to Decimal128: {arg=}'
        # Wrap the argument in a string, since a double literal may lose precision and generate an incorrect value:
        call = CallExpr(Ident('mc_dec128_from_string'),
                        [String(cquote(str(arg)))])
        return _render_call(call)
    # Otherwise: Render anything else as just a function call:
    fn = _render_expr(c.fn)
    each_arg = map(_render_expr, c.args)
    comma_args = join_with(each_arg, ', ')
    args = chain.from_iterable(comma_args)
    return chain(fn, '(', args, ')')


def _render_scope(e: ScopeExpr) -> Iterable[str]:
    """
    Render a scope resolution expression. This only cares about constants of
    Decimal128 yet.
    """
    if e.left == Ident('Decimal128') and isinstance(e.name, Ident):
        # Looking up a constant on Decimal128, presumably
        attr = e.name
        const = {
            'kLargestPositive': 'MC_DEC128_LARGEST_POSITIVE',
            'kLargestNegative': 'MC_DEC128_LARGEST_NEGATIVE',
        }[attr.spell]
        return [const]
    assert False, f'Unimplemented scope-resolution expression: {e=}'


def _render_infix(e: InfixExpr) -> Iterable[str]:
    if isinstance(e.rhs, String) and e.oper == '+':
        # We're building a string with operator+. We don't have a special
        # "string concat" to use, so just do preprocessor string splicing
        # (for now). This also assumes that the operands are also string literals or other string
        # concatenations, but that's all we need for now.
        return chain(_render_expr(e.lhs), _render_expr(e.rhs))
    # We don't implement any other infix expressions yet.
    assert False, f'Unimplemented infix expression: {e=}'


def _get_stdstring_concat_content(s: InfixExpr) -> str:
    left = get_string_content(s.lhs)
    right = get_string_content(s.rhs)
    return left + right


def get_string_content(s: Expression) -> str:
    if isinstance(s, InfixExpr):
        return _get_stdstring_concat_content(s)
    if isinstance(s, CallExpr) and str(s.fn) == 'std::string':
        # We're constructing a std::string. All our inputs just use inline literals, so just render
        # those.
        assert len(s.args) == 1, s
        return get_string_content(s.args[0])
    assert isinstance(
        s, String
    ), f'Attempting to pull the content of a non-string expression: {s!r}'
    if not s.spell.startswith('R'):
        # Just a regular string.
        return json.loads(s.spell)
    # C doesn't support the R"()" 'raw string' (yet). We'll un-prettify it:
    mat = STRING_RE.match(s.spell)
    assert mat, s
    return mat.group('raw_content')


def _render_string(s: String) -> Iterable[str]:
    """
    Render a string literal.
    """
    c = get_string_content(s)
    lines = c.splitlines()
    if '\n' in c:
        lines = (f'{l}\n' for l in lines)
    quoted = map(cquote, lines)
    return join_with(quoted, '\n    ')


def _render_initlist(i: InitList) -> Iterable[str]:
    exprs = map(_render_expr, i.elems)
    with_comma = chain.from_iterable(chain('\n    ', x, ',') for x in exprs)
    return chain('{', with_comma, '\n  }')


def _render_expr(e: Expression) -> Iterable[str]:
    """
    Generate a rendering of an arbitrary expression
    """
    if isinstance(e, (Number, Ident)):
        return [e.spell]
    elif isinstance(e, String):
        return _render_string(e)
    elif isinstance(e, CallExpr):
        return _render_call(e)
    elif isinstance(e, PrefixExpr):
        return chain(e.operator, _render_expr(e.operand))
    elif isinstance(e, ScopeExpr):
        return _render_scope(e)
    elif isinstance(e, InfixExpr):
        return _render_infix(e)
    elif isinstance(e, InitList):
        return _render_initlist(e)
    else:
        assert False, f'Do not know how to render expression: {e=}'


def _render_opt_wrap(e: Expression) -> Iterable[str]:
    """
    Render a value that is wrapped as an optional. If boost::none, emits a
    braced-init "{.set = false}", otherwise "{.set=true, .value = render(e) }"
    """
    if e == BOOST_NONE:
        return '{ .set = false }'
    return chain('{ .set = true, .value = ', _render_expr(e), ' }')


def designit(attr: str, x: Iterable[str]) -> Iterable[str]:
    """
    Render a 2-space indented designated initializer, with a trailing comma
    and newline
    """
    return chain(f'  .{attr} = ', x, ',\n')


def render_edge(e: EdgeInfo) -> Iterable[str]:
    # Permute the edge list for easier matching
    elist = e.edges
    assert isinstance(elist, InitList)
    fin = elist.elems[-1]
    leaf = elist.elems[-2]
    prefix = elist.elems[:-2]
    reordered = chain((fin, leaf), prefix)
    wrapped = (chain('\n    ', _render_expr(e), ',') for e in reordered)
    braced_edges = chain('{', chain.from_iterable(wrapped), '\n  }')

    return chain(
        '{\n',
        designit('value', _render_expr(e.value)),
        designit('min', _render_opt_wrap(e.min)),
        designit('max', _render_opt_wrap(e.max)),
        # The upstream vectors include a 'precision' field, but we don't use it (yet).
        # designit('precision', _render_opt_wrap(e.precision)),
        designit('sparsity', _render_expr(e.sparsity)),
        designit('expectEdges', braced_edges),
        '},\n',
    )


def split_mincover_string(s: Expression) -> Iterable[str]:
    content = get_string_content(s)
    lines = content.splitlines()
    quoted = map(cquote, lines)
    as_exprs = map(String, quoted)
    return _render_expr(InitList(list(as_exprs)))


def render_mincover(mc: MinCoverInfo) -> Iterable[str]:
    return chain(
        '{\n',
        designit('lowerBound', _render_expr(mc.lb)),
        designit('includeLowerBound', 'true'),
        designit('upperBound', _render_expr(mc.ub)),
        designit('includeUpperBound', 'true'),
        designit('sparsity', _render_expr(mc.sparsity)),
        designit('min', _render_opt_wrap(mc.min)),
        designit('max', _render_opt_wrap(mc.max)),
        () if mc.precision is BOOST_NONE  #
        else designit('precision', _render_opt_wrap(mc.precision)),
        designit('expectMincoverStrings',
                 split_mincover_string(mc.expect_string)),
        '},\n',
    )


def generate(code: str, parser: Callable[[Tokenizer], Iterable[T]],
             render: Callable[[T], Iterable[str]]):
    """
    Generate code.

    :param code: The input code to parse.
    :param parser: A parsing function that accepts a tokenizer and emits objects of type T.
    :param render: A renderer that accepts instances of T and returns an iterable of strings.

    For every object V yielded by parse(tokens), every string
    yielded from render(V) will be written to stdout.
    """
    scan = Scanner(code)
    toks = LazyList(tokenize(scan))
    print('// This code is GENERATED! Do not edit! Regenerate with `test/util/make_includes.py`')
    print('// clang-format off')
    items = parser(toks)
    each_rendered = map(render, items)
    strings = chain.from_iterable(each_rendered)
    for s in strings:
        sys.stdout.write(s)


def main(argv: Sequence[str]):
    parser = argparse.ArgumentParser()
    parser.add_argument('kind',
                        help='What kind of construct are we parsing',
                        choices=['edges', 'mincovers'])
    args = parser.parse_args(argv)
    code = sys.stdin.read()
    if args.kind == 'edges':
        generate(code, parse_edges, render_edge)
    elif args.kind == 'mincovers':
        generate(code, parse_mincovers, render_mincover)
    else:
        assert False


if __name__ == '__main__':
    main(sys.argv[1:])
libmongocrypt-1.19.0/test/util/util.c000066400000000000000000000705451521103432300175540ustar00rootroot00000000000000/*
 * Copyright 2020-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 "util.h"

/* Utilities for integration tests and example runners. */

#include "mongocrypt.h"
#include 

static void _errexit_status(mongocrypt_status_t *status, int line) {
    int code;
    const char *msg;

    code = mongocrypt_status_code(status);
    msg = mongocrypt_status_message(status, NULL);
    MONGOC_ERROR("Error at line %d with code %d and msg: %s", line, code, msg);
    exit(1);
}

void _errexit_mongocrypt(mongocrypt_t *crypt, int line) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    mongocrypt_status(crypt, status);
    _errexit_status(status, line);
    mongocrypt_status_destroy(status);
}

void _errexit_ctx(mongocrypt_ctx_t *ctx, int line) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    mongocrypt_ctx_status(ctx, status);
    _errexit_status(status, line);
    mongocrypt_status_destroy(status);
}

void _errexit_bson(bson_error_t *error, int line) {
    MONGOC_ERROR("Error at line %d with code %" PRIu32 " and msg: %s", line, error->code, error->message);
    exit(1);
}

void _log_to_stdout(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx) {
    switch (level) {
    case MONGOCRYPT_LOG_LEVEL_FATAL: printf("FATAL"); break;
    case MONGOCRYPT_LOG_LEVEL_ERROR: printf("ERROR"); break;
    case MONGOCRYPT_LOG_LEVEL_WARNING: printf("WARNING"); break;
    case MONGOCRYPT_LOG_LEVEL_INFO: printf("INFO"); break;
    case MONGOCRYPT_LOG_LEVEL_TRACE: printf("TRACE"); break; /* UNUSED */
    default: printf("?????"); break;
    }
    printf(" %s\n", message);
}

char *util_getenv(const char *key) {
    char *value = getenv(key);
    if (!value) {
        MONGOC_ERROR("Environment variable: %s not set", key);
    }
    return value;
}

mongocrypt_binary_t *util_bson_to_bin(bson_t *bson) {
    return mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(bson), bson->len);
}

bson_t *util_bin_to_bson(mongocrypt_binary_t *bin) {
    return bson_new_from_data(mongocrypt_binary_data(bin), mongocrypt_binary_len(bin));
}

static void _prefix_mongocryptd_error(bson_error_t *error) {
    char buf[sizeof(error->message)];

    BSON_ASSERT(0 < bson_snprintf(buf, sizeof(buf), "mongocryptd error: %s:", error->message)); // Truncation OK.
    memcpy(error->message, buf, sizeof(buf));
}

static void _prefix_keyvault_error(bson_error_t *error) {
    char buf[sizeof(error->message)];

    BSON_ASSERT(0 < bson_snprintf(buf, sizeof(buf), "key vault error: %s:", error->message)); // Truncation OK.
    memcpy(error->message, buf, sizeof(buf));
}

static void _status_to_error(mongocrypt_status_t *status, bson_error_t *error) {
    bson_set_error(error,
                   MONGOC_ERROR_CLIENT_SIDE_ENCRYPTION,
                   mongocrypt_status_code(status),
                   "%s",
                   mongocrypt_status_message(status, NULL));
}

/* Checks for an error on mongocrypt context.
 * If error_expected, then we expect mongocrypt_ctx_status to report a failure
 * status (due to a previous failed function call). If it did not, return a
 * generic error.
 * Returns true if ok, and does not modify @error.
 * Returns false if error, and sets @error.
 */
static bool _test_ctx_check_error(mongocrypt_ctx_t *ctx, bson_error_t *error, bool error_expected) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    if (!mongocrypt_ctx_status(ctx, status)) {
        _status_to_error(status, error);
        mongocrypt_status_destroy(status);
        return false;
    } else if (error_expected) {
        bson_set_error(error,
                       MONGOC_ERROR_CLIENT,
                       MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_STATE,
                       "generic error from libmongocrypt operation");
        mongocrypt_status_destroy(status);
        return false;
    }
    mongocrypt_status_destroy(status);
    return true;
}

static bool _test_kms_ctx_check_error(mongocrypt_kms_ctx_t *kms_ctx, bson_error_t *error, bool error_expected) {
    mongocrypt_status_t *status;

    status = mongocrypt_status_new();
    if (!mongocrypt_kms_ctx_status(kms_ctx, status)) {
        _status_to_error(status, error);
        mongocrypt_status_destroy(status);
        return false;
    } else if (error_expected) {
        bson_set_error(error,
                       MONGOC_ERROR_CLIENT,
                       MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_STATE,
                       "generic error from libmongocrypt KMS operation");
        mongocrypt_status_destroy(status);
        return false;
    }
    mongocrypt_status_destroy(status);
    return true;
}

/* Convert a mongocrypt_binary_t to a static bson_t */
static bool _bin_to_static_bson(mongocrypt_binary_t *bin, bson_t *out, bson_error_t *error) {
    /* Copy bin into bson_t result. */
    if (!bson_init_static(out, mongocrypt_binary_data(bin), mongocrypt_binary_len(bin))) {
        bson_set_error(error, MONGOC_ERROR_BSON, MONGOC_ERROR_BSON_INVALID, "invalid returned bson");
        return false;
    }
    return true;
}

/* State handler MONGOCRYPT_CTX_NEED_MONGO_COLLINFO */
static bool _state_need_mongo_collinfo(_state_machine_t *state_machine, bson_error_t *error) {
    mongoc_database_t *db = NULL;
    mongoc_cursor_t *cursor = NULL;
    bson_t filter_bson;
    const bson_t *collinfo_bson = NULL;
    bson_t opts = BSON_INITIALIZER;
    mongocrypt_binary_t *filter_bin = NULL;
    mongocrypt_binary_t *collinfo_bin = NULL;
    bool ret = false;

    /* 1. Run listCollections on the encrypted MongoClient with the filter
     * provided by mongocrypt_ctx_mongo_op */
    filter_bin = mongocrypt_binary_new();
    if (!mongocrypt_ctx_mongo_op(state_machine->ctx, filter_bin)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    if (!_bin_to_static_bson(filter_bin, &filter_bson, error)) {
        goto fail;
    }

    bson_append_document(&opts, "filter", -1, &filter_bson);
    db = mongoc_client_get_database(state_machine->collinfo_client, state_machine->db_name);

    if (state_machine->trace) {
        char *opts_str;

        opts_str = bson_as_canonical_extended_json(&filter_bson, NULL);
        MONGOC_DEBUG("--> sending listCollections cmd on db %s mongod with opts: %s", state_machine->db_name, opts_str);
        bson_free(opts_str);
    }
    cursor = mongoc_database_find_collections_with_opts(db, &opts);
    if (mongoc_cursor_error(cursor, error)) {
        goto fail;
    }

    /* 2. Return the first result (if any) with mongocrypt_ctx_mongo_feed or
     * proceed to the next step if nothing was returned. */
    if (mongoc_cursor_next(cursor, &collinfo_bson)) {
        if (state_machine->trace) {
            char *result_str;

            result_str = bson_as_canonical_extended_json(collinfo_bson, NULL);
            MONGOC_DEBUG("<-- got result: %s", result_str);
            bson_free(result_str);
        }
        collinfo_bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(collinfo_bson), collinfo_bson->len);
        if (!mongocrypt_ctx_mongo_feed(state_machine->ctx, collinfo_bin)) {
            _test_ctx_check_error(state_machine->ctx, error, true);
            goto fail;
        }
    } else if (mongoc_cursor_error(cursor, error)) {
        goto fail;
    }

    /* 3. Call mongocrypt_ctx_mongo_done */
    if (!mongocrypt_ctx_mongo_done(state_machine->ctx)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    ret = true;

fail:

    bson_destroy(&opts);
    mongocrypt_binary_destroy(filter_bin);
    mongocrypt_binary_destroy(collinfo_bin);
    mongoc_cursor_destroy(cursor);
    mongoc_database_destroy(db);
    return ret;
}

static bool _state_need_mongo_markings(_state_machine_t *state_machine, bson_error_t *error) {
    bool ret = false;
    mongocrypt_binary_t *mongocryptd_cmd_bin = NULL;
    mongocrypt_binary_t *mongocryptd_reply_bin = NULL;
    bson_t mongocryptd_cmd_bson;
    bson_t reply = BSON_INITIALIZER;

    mongocryptd_cmd_bin = mongocrypt_binary_new();

    if (!mongocrypt_ctx_mongo_op(state_machine->ctx, mongocryptd_cmd_bin)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    if (!_bin_to_static_bson(mongocryptd_cmd_bin, &mongocryptd_cmd_bson, error)) {
        goto fail;
    }

    if (state_machine->trace) {
        char *cmd_str;
        bson_iter_t iter;

        bson_iter_init(&iter, &mongocryptd_cmd_bson);
        bson_iter_next(&iter);
        cmd_str = bson_as_canonical_extended_json(&mongocryptd_cmd_bson, NULL);
        MONGOC_DEBUG("--> sending %s cmd to mongocryptd: %s", bson_iter_key(&iter), cmd_str);
        bson_free(cmd_str);
    }

    /* 1. Use db.runCommand to run the command provided by
     * mongocrypt_ctx_mongo_op on the MongoClient connected to mongocryptd. */
    bson_destroy(&reply);
    if (!mongoc_client_command_simple(state_machine->mongocryptd_client,
                                      "admin",
                                      &mongocryptd_cmd_bson,
                                      NULL /* read_prefs */,
                                      &reply,
                                      error)) {
        _prefix_mongocryptd_error(error);
        goto fail;
    }

    if (state_machine->trace) {
        char *reply_str;

        reply_str = bson_as_canonical_extended_json(&reply, NULL);
        MONGOC_DEBUG("<-- got reply: %s", reply_str);
        bson_free(reply_str);
    }

    /* 2. Feed the reply back with mongocrypt_ctx_mongo_feed. */
    mongocryptd_reply_bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(&reply), reply.len);
    if (!mongocrypt_ctx_mongo_feed(state_machine->ctx, mongocryptd_reply_bin)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    /* 3. Call mongocrypt_ctx_mongo_done. */
    if (!mongocrypt_ctx_mongo_done(state_machine->ctx)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    ret = true;
fail:
    bson_destroy(&reply);
    mongocrypt_binary_destroy(mongocryptd_cmd_bin);
    mongocrypt_binary_destroy(mongocryptd_reply_bin);
    return ret;
}

static bool _state_need_mongo_keys(_state_machine_t *state_machine, bson_error_t *error) {
    bool ret = false;
    mongocrypt_binary_t *filter_bin = NULL;
    bson_t filter_bson;
    bson_t opts = BSON_INITIALIZER;
    mongocrypt_binary_t *key_bin = NULL;
    const bson_t *key_bson;
    mongoc_cursor_t *cursor = NULL;
    mongoc_read_concern_t *rc = NULL;

    /* 1. Use MongoCollection.find on the MongoClient connected to the key vault
     * client (which may be the same as the encrypted client). Use the filter
     * provided by mongocrypt_ctx_mongo_op. */
    filter_bin = mongocrypt_binary_new();
    if (!mongocrypt_ctx_mongo_op(state_machine->ctx, filter_bin)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    if (!_bin_to_static_bson(filter_bin, &filter_bson, error)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    rc = mongoc_read_concern_new();
    mongoc_read_concern_set_level(rc, MONGOC_READ_CONCERN_LEVEL_MAJORITY);
    if (!mongoc_read_concern_append(rc, &opts)) {
        bson_set_error(error, MONGOC_ERROR_BSON, MONGOC_ERROR_BSON_INVALID, "%s", "could not set read concern");
        goto fail;
    }

    if (state_machine->trace) {
        char *filter_str;
        char *opts_str;

        filter_str = bson_as_canonical_extended_json(&filter_bson, NULL);
        opts_str = bson_as_canonical_extended_json(&opts, NULL);
        MONGOC_DEBUG("--> sending find to mongod with filter: %s and opts: %s", filter_str, opts_str);
        bson_free(filter_str);
        bson_free(opts_str);
    }

    cursor = mongoc_collection_find_with_opts(state_machine->keyvault_coll, &filter_bson, &opts, NULL /* read prefs */);
    /* 2. Feed all resulting documents back (if any) with repeated calls to
     * mongocrypt_ctx_mongo_feed. */
    while (mongoc_cursor_next(cursor, &key_bson)) {
        if (state_machine->trace) {
            char *key_str;

            key_str = bson_as_canonical_extended_json(key_bson, NULL);
            MONGOC_DEBUG("<-- got result key document: %s", key_str);
            bson_free(key_str);
        }
        mongocrypt_binary_destroy(key_bin);
        key_bin = mongocrypt_binary_new_from_data((uint8_t *)bson_get_data(key_bson), key_bson->len);
        if (!mongocrypt_ctx_mongo_feed(state_machine->ctx, key_bin)) {
            _test_ctx_check_error(state_machine->ctx, error, true);
            goto fail;
        }
    }
    if (mongoc_cursor_error(cursor, error)) {
        _prefix_keyvault_error(error);
        goto fail;
    }

    /* 3. Call mongocrypt_ctx_mongo_done. */
    if (!mongocrypt_ctx_mongo_done(state_machine->ctx)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    ret = true;
fail:
    mongocrypt_binary_destroy(filter_bin);
    mongoc_cursor_destroy(cursor);
    mongoc_read_concern_destroy(rc);
    bson_destroy(&opts);
    mongocrypt_binary_destroy(key_bin);
    return ret;
}

/* Create a TLS stream to a host. */
static mongoc_stream_t *connect_stream_with_tls(mongoc_ssl_opt_t *ssl_opt,
                                                const char *endpoint,
                                                int32_t connecttimeoutms,
                                                bson_error_t *error) {
    mongoc_stream_t *stream = NULL;
    mongoc_socket_t *sock = NULL;
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int64_t expire_at;
    int s;
    char *colon = NULL;
    char *host = NULL;
    char *port = NULL;
    bool success = false;

    colon = strstr(endpoint, ":");
    if (colon == NULL) {
        host = bson_strdup(endpoint);
        port = bson_strdup("443");
    } else {
        host = bson_strndup(endpoint, colon - endpoint);
        port = bson_strdup(colon + 1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;

    s = getaddrinfo(host, port, &hints, &result);
    if (s != 0) {
        MONGOC_ERROR("DNS lookup failed: %s", host);
        goto done;
    }

    for (rp = result; rp; rp = rp->ai_next) {
        if (!(sock = mongoc_socket_new(rp->ai_family, rp->ai_socktype, rp->ai_protocol))) {
            continue;
        }

        expire_at = bson_get_monotonic_time() + (connecttimeoutms * 1000L);
        if (0 != mongoc_socket_connect(sock, rp->ai_addr, (mongoc_socklen_t)rp->ai_addrlen, expire_at)) {
            mongoc_socket_destroy(sock);
            sock = NULL;
            continue;
        }

        break;
    }

    if (!sock) {
        MONGOC_ERROR("Failed to connect: %s", host);
        goto done;
    }

    stream = mongoc_stream_socket_new(sock);
    if (!stream) {
        MONGOC_ERROR("Failed to create stream: %s", host);
        goto done;
    }

    success = true;
done:
    if (result) {
        freeaddrinfo(result);
    }
    if (success) {
        stream = mongoc_stream_tls_new_with_hostname(stream, host, ssl_opt, 1);
    } else {
        mongoc_stream_destroy(stream);
        stream = NULL;
    }

    if (!mongoc_stream_tls_handshake_block(stream, host, connecttimeoutms, error)) {
        mongoc_stream_destroy(stream);
        stream = NULL;
    }

    bson_free(host);
    bson_free(port);
    return stream;
}

/* Copied from mongoc-stream.c */
bool _mongoc_stream_writev_full(mongoc_stream_t *stream,
                                mongoc_iovec_t *iov,
                                size_t iovcnt,
                                int32_t timeout_msec,
                                bson_error_t *error);

static bool _state_need_kms_credentials(_state_machine_t *state_machine, bson_error_t *error) {
    bson_t empty = BSON_INITIALIZER;
    mongocrypt_binary_t *bin = util_bson_to_bin(&empty);
    mongocrypt_ctx_provide_kms_providers(state_machine->ctx, bin);
    mongocrypt_binary_destroy(bin);
    return true;
}

static bool _state_need_kms(_state_machine_t *state_machine, bson_error_t *error) {
    mongocrypt_kms_ctx_t *kms_ctx = NULL;
    mongoc_stream_t *tls_stream = NULL;
    bool ret = false;
    mongocrypt_binary_t *http_req = NULL;
    mongocrypt_binary_t *http_reply = NULL;
    const char *endpoint;
    uint32_t sockettimeout;
    mongoc_ssl_opt_t ssl_opt;

    sockettimeout = MONGOC_DEFAULT_SOCKETTIMEOUTMS;
    kms_ctx = mongocrypt_ctx_next_kms_ctx(state_machine->ctx);
    while (kms_ctx) {
        mongoc_iovec_t iov;

        mongocrypt_binary_destroy(http_req);
        http_req = mongocrypt_binary_new();
        if (!mongocrypt_kms_ctx_message(kms_ctx, http_req)) {
            _test_kms_ctx_check_error(kms_ctx, error, true);
            goto fail;
        }

        if (!mongocrypt_kms_ctx_endpoint(kms_ctx, &endpoint)) {
            _test_kms_ctx_check_error(kms_ctx, error, true);
            goto fail;
        }

        ssl_opt = *mongoc_ssl_opt_get_default();
        ssl_opt.ca_file = state_machine->tls_ca_file;
        ssl_opt.pem_file = state_machine->tls_certificate_key_file;
        tls_stream = connect_stream_with_tls(&ssl_opt, endpoint, sockettimeout, error);
#ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL
        /* Retry once with schannel as a workaround for CDRIVER-3566. */
        if (!tls_stream) {
            tls_stream = connect_stream_with_tls(&ssl_opt, endpoint, sockettimeout, error);
        }
#endif
        if (!tls_stream) {
            goto fail;
        }

        iov.iov_base = (char *)mongocrypt_binary_data(http_req);
        iov.iov_len = mongocrypt_binary_len(http_req);
        if (state_machine->trace) {
            MONGOC_DEBUG("--> sending KMS message:");
            const char *kms_provider = mongocrypt_kms_ctx_get_kms_provider(kms_ctx, NULL);
            if (0 == strcmp(kms_provider, "kmip")) {
                // Print KMIP protocol as hex.
                uint8_t *as_u8_ptr = iov.iov_base;
                for (size_t i = 0; i < iov.iov_len; i++) {
                    printf("%02x", as_u8_ptr[i]);
                }
            } else {
                // Print HTTP protocol as text.
                char *as_char_ptr = iov.iov_base;
                BSON_ASSERT(iov.iov_len <= INT_MAX);
                printf("%.*s", (int)iov.iov_len, as_char_ptr);
            }
            printf("\n");
        }

        if (!_mongoc_stream_writev_full(tls_stream, &iov, 1, sockettimeout, error)) {
            goto fail;
        }

        /* Read and feed reply. */
        if (state_machine->trace) {
            MONGOC_DEBUG("<-- read KMS reply:");
        }
        while (mongocrypt_kms_ctx_bytes_needed(kms_ctx) > 0) {
#define BUFFER_SIZE 1024
            uint8_t buf[BUFFER_SIZE];
            uint32_t bytes_needed = mongocrypt_kms_ctx_bytes_needed(kms_ctx);
            ssize_t read_ret;

            /* Cap the bytes requested at the buffer size. */
            if (bytes_needed > BUFFER_SIZE) {
                bytes_needed = BUFFER_SIZE;
            }

            read_ret = mongoc_stream_read(tls_stream, buf, bytes_needed, 0 /* min_bytes. */, sockettimeout);
            if (read_ret == -1) {
                bson_set_error(error,
                               MONGOC_ERROR_STREAM,
                               MONGOC_ERROR_STREAM_SOCKET,
                               "failed to read from KMS stream: %d",
                               errno);
                goto fail;
            }

            if (read_ret == 0) {
                bson_set_error(error,
                               MONGOC_ERROR_STREAM,
                               MONGOC_ERROR_STREAM_SOCKET,
                               "unexpected EOF from KMS stream");
                goto fail;
            }

            if (state_machine->trace) {
                const char *kms_provider = mongocrypt_kms_ctx_get_kms_provider(kms_ctx, NULL);
                if (0 == strcmp(kms_provider, "kmip")) {
                    // Print KMIP protocol as hex.
                    for (ssize_t i = 0; i < read_ret; i++) {
                        printf("%02x", buf[i]);
                    }
                } else {
                    char *as_char_ptr = (char *)buf;
                    // Print HTTP protocol as text.
                    BSON_ASSERT(read_ret >= 0);
                    BSON_ASSERT(read_ret <= INT_MAX);
                    printf("%.*s", (int)read_ret, as_char_ptr);
                }
            }

            mongocrypt_binary_destroy(http_reply);
            http_reply = mongocrypt_binary_new_from_data(buf, (uint32_t)read_ret);
            if (!mongocrypt_kms_ctx_feed(kms_ctx, http_reply)) {
                _test_kms_ctx_check_error(kms_ctx, error, true);
                goto fail;
            }
        }
        if (state_machine->trace) {
            printf("\n");
        }
        kms_ctx = mongocrypt_ctx_next_kms_ctx(state_machine->ctx);
    }
    /* When NULL is returned by mongocrypt_ctx_next_kms_ctx, this can either be
     * an error or end-of-list. */
    if (!_test_ctx_check_error(state_machine->ctx, error, false)) {
        goto fail;
    }

    if (!mongocrypt_ctx_kms_done(state_machine->ctx)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    ret = true;
fail:
    mongoc_stream_destroy(tls_stream);
    mongocrypt_binary_destroy(http_req);
    mongocrypt_binary_destroy(http_reply);
    return ret;
#undef BUFFER_SIZE
}

static bool _state_ready(_state_machine_t *state_machine, bson_t *result, bson_error_t *error) {
    mongocrypt_binary_t *result_bin = NULL;
    bson_t tmp;
    bool ret = false;

    bson_init(result);
    result_bin = mongocrypt_binary_new();
    if (!mongocrypt_ctx_finalize(state_machine->ctx, result_bin)) {
        _test_ctx_check_error(state_machine->ctx, error, true);
        goto fail;
    }

    if (!_bin_to_static_bson(result_bin, &tmp, error)) {
        goto fail;
    }

    bson_destroy(result);
    bson_copy_to(&tmp, result);

    ret = true;
fail:
    mongocrypt_binary_destroy(result_bin);
    return ret;
}

static const char *_state_string(mongocrypt_ctx_state_t state) {
    switch (state) {
    case MONGOCRYPT_CTX_ERROR: return "MONGOCRYPT_CTX_ERROR";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB";
    case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: return "MONGOCRYPT_CTX_NEED_MONGO_COLLINFO";
    case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: return "MONGOCRYPT_CTX_NEED_MONGO_MARKINGS";
    case MONGOCRYPT_CTX_NEED_MONGO_KEYS: return "MONGOCRYPT_CTX_NEED_MONGO_KEYS";
    case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: return "MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS";
    case MONGOCRYPT_CTX_NEED_KMS: return "MONGOCRYPT_CTX_NEED_KMS";
    case MONGOCRYPT_CTX_READY: return "MONGOCRYPT_CTX_READY";
    case MONGOCRYPT_CTX_DONE: return "MONGOCRYPT_CTX_DONE";
    default: return "UNKNOWN";
    }
}

/*--------------------------------------------------------------------------
 *
 * _mongoc_cse_run_state_machine --
 *    Run the mongocrypt_ctx state machine.
 *
 * Post-conditions:
 *    *result may be set to a new bson_t, or NULL otherwise. Caller should
 *    not assume return value of true means *result is set. If false returned,
 *    @error is set.
 *
 * --------------------------------------------------------------------------
 */
bool _csfle_state_machine_run(_state_machine_t *state_machine, bson_t *result, bson_error_t *error) {
    bool ret = false;
    mongocrypt_binary_t *bin = NULL;

    bson_init(result);
    while (true) {
        if (state_machine->trace) {
            MONGOC_DEBUG("Current state = %s", _state_string(mongocrypt_ctx_state(state_machine->ctx)));
        }
        switch (mongocrypt_ctx_state(state_machine->ctx)) {
        default:
        case MONGOCRYPT_CTX_ERROR: _test_ctx_check_error(state_machine->ctx, error, true); goto fail;
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB:
            ERREXIT("Obtaining collection infos on separate databases is not yet supported");
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO:
            if (!_state_need_mongo_collinfo(state_machine, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS:
            if (!_state_need_mongo_markings(state_machine, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_NEED_MONGO_KEYS:
            if (!_state_need_mongo_keys(state_machine, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS:
            if (!_state_need_kms_credentials(state_machine, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_NEED_KMS:
            if (!_state_need_kms(state_machine, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_READY:
            bson_destroy(result);
            if (!_state_ready(state_machine, result, error)) {
                goto fail;
            }
            break;
        case MONGOCRYPT_CTX_DONE: goto success;
        }
    }

success:
    ret = true;
fail:
    if (!ret && state_machine->trace) {
        MONGOC_DEBUG("Error: %s", error->message);
    }
    mongocrypt_binary_destroy(bin);
    return ret;
}

bson_t *util_read_json_file(const char *path) {
    bson_json_reader_t *reader;
    bson_error_t error;
    bson_t *doc;

    reader = bson_json_reader_new_from_file(path, &error);
    if (!reader) {
        ERREXIT("Error opening %s: %s", path, error.message);
    }

    doc = bson_new();
    if (1 != bson_json_reader_read(reader, doc, &error)) {
        ERREXIT("Could not read BSON from %s: %s", path, error.message);
    }
    bson_json_reader_destroy(reader);
    return doc;
}

void args_parse(bson_t *args, int argc, char **argv) {
    int i;

    if (argc % 2 != 0) {
        ERREXIT("Invalid arguments, expected list of key-value pairs.");
    }

    for (i = 0; i < argc; i++) {
        for (i = 0; i < argc; i += 2) {
            if (0 != strncmp(argv[i], "--", 2)) {
                ERREXIT("Malformed option: %s", argv[i]);
            }
            bson_append_utf8(args, argv[i] + 2, -1, argv[i + 1], -1);
        }
    }
}

const char *bson_get_utf8(bson_t *bson, const char *dotkey, const char *default_value) {
    bson_iter_t iter;

    bson_iter_init(&iter, bson);
    if (bson_iter_find_descendant(&iter, dotkey, &iter) && BSON_ITER_HOLDS_UTF8(&iter)) {
        return bson_iter_utf8(&iter, NULL);
    }
    return default_value;
}

const char *bson_req_utf8(bson_t *bson, const char *dotkey) {
    const char *ret;

    ret = bson_get_utf8(bson, dotkey, NULL);
    if (!ret) {
        ERREXIT("Required field missing: '%s'", dotkey);
    }
    return ret;
}

const uint8_t *bson_get_bin(bson_t *bson, const char *dotkey, uint32_t *len) {
    bson_iter_t iter;
    bson_iter_t subiter;
    bson_subtype_t subtype;
    const uint8_t *data = NULL;

    bson_iter_init(&iter, bson);
    if (bson_iter_find_descendant(&iter, dotkey, &subiter) && BSON_ITER_HOLDS_BINARY(&subiter)) {
        bson_iter_binary(&subiter, &subtype, len, &data);
    }
    return data;
}

const uint8_t *bson_req_bin(bson_t *bson, const char *dotkey, uint32_t *len) {
    const uint8_t *ret;

    ret = bson_get_bin(bson, dotkey, len);
    if (!ret) {
        ERREXIT("Required field missing: '%s'", dotkey);
    }
    return ret;
}

bson_t *bson_get_json(bson_t *bson, const char *dotkey) {
    const char *path;

    path = bson_get_utf8(bson, dotkey, NULL);
    if (!path) {
        return NULL;
    }
    return util_read_json_file(path);
}

bson_t *bson_req_json(bson_t *bson, const char *dotkey) {
    bson_t *ret;

    ret = bson_get_json(bson, dotkey);
    if (!ret) {
        ERREXIT("Required field missing: '%s'", dotkey);
    }
    return ret;
}

bool bson_get_bool(bson_t *bson, const char *dotkey, bool default_value) {
    const char *as_str;

    as_str = bson_get_utf8(bson, dotkey, NULL);
    if (!as_str) {
        return default_value;
    }
    if (0 == bson_strcasecmp(as_str, "true") || 0 == strcmp(as_str, "1")) {
        return true;
    }
    return default_value;
}
libmongocrypt-1.19.0/test/util/util.h000066400000000000000000000054761521103432300175620ustar00rootroot00000000000000/*
 * Copyright 2020-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 

#include "mongocrypt.h"
#include 

void _errexit_mongocrypt(mongocrypt_t *crypt, int line);
#define ERREXIT_MONGOCRYPT(crypt) _errexit_mongocrypt(crypt, __LINE__)

void _errexit_ctx(mongocrypt_ctx_t *ctx, int line);
#define ERREXIT_CTX(ctx) _errexit_ctx(ctx, __LINE__)

void _errexit_bson(bson_error_t *error, int line);
#define ERREXIT_BSON(err) _errexit_bson(err, __LINE__)

#define ERREXIT(...)                                                                                                   \
    if (1) {                                                                                                           \
        MONGOC_ERROR(__VA_ARGS__);                                                                                     \
        abort();                                                                                                       \
    } else                                                                                                             \
        ((void)0)

void _log_to_stdout(mongocrypt_log_level_t level, const char *message, uint32_t message_len, void *ctx);

char *util_getenv(const char *key);

mongocrypt_binary_t *util_bson_to_bin(bson_t *bson);

typedef struct {
    mongocrypt_ctx_t *ctx;
    mongoc_collection_t *keyvault_coll;
    mongoc_client_t *mongocryptd_client;
    mongoc_client_t *collinfo_client;
    const char *db_name;
    bool trace;
    const char *tls_ca_file;
    const char *tls_certificate_key_file;
} _state_machine_t;

bool _csfle_state_machine_run(_state_machine_t *state_machine, bson_t *result, bson_error_t *error);

bson_t *util_bin_to_bson(mongocrypt_binary_t *bin);

bson_t *util_read_json_file(const char *path);

void args_parse(bson_t *args, int argc, char **argv);

const char *bson_get_utf8(bson_t *bson, const char *key, const char *default_value);

const char *bson_req_utf8(bson_t *bson, const char *key);

const uint8_t *bson_get_bin(bson_t *bson, const char *dotkey, uint32_t *len);

const uint8_t *bson_req_bin(bson_t *bson, const char *dotkey, uint32_t *len);

bson_t *bson_get_json(bson_t *bson, const char *key);

bson_t *bson_req_json(bson_t *bson, const char *key);

bool bson_get_bool(bson_t *bson, const char *key, bool default_value);
libmongocrypt-1.19.0/third-party/000077500000000000000000000000001521103432300167335ustar00rootroot00000000000000