#!/usr/bin/env bats # KMS tests setup() { load 'test_helper/common-setup' KEY_ID="" } teardown() { if [ -n "$KEY_ID" ]; then aws_cmd kms schedule-key-deletion --key-id "$KEY_ID" --pending-window-in-days 7 >/dev/null 2>&1 || true fi } @test "KMS: create key" { run aws_cmd kms create-key --description "bats-test-key" assert_success KEY_ID=$(json_get "$output" '.KeyMetadata.KeyId') [ -n "$KEY_ID" ] } @test "KMS: describe key" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms describe-key --key-id "$KEY_ID" assert_success enabled=$(json_get "$output" '.KeyMetadata.Enabled') [ "$enabled" = "true" ] } @test "KMS: list keys" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms list-keys assert_success found=$(echo "$output" | jq --arg id "$KEY_ID" '.Keys | any(.KeyId == $id)') [ "$found" = "true" ] } @test "KMS: list grants" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') [ -n "$KEY_ID" ] run aws_cmd kms list-grants --key-id "$KEY_ID" assert_success has_grants=$(echo "$output" | jq 'has("Grants") and (.Grants | type == "array")') [ "$has_grants" = "true" ] } @test "KMS: encrypt and decrypt" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') # Create a temp file with plaintext local plaintext_file ciphertext_file decrypted_file plaintext_file=$(mktemp) ciphertext_file=$(mktemp) decrypted_file=$(mktemp) echo -n "hello-kms-bats" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$KEY_ID" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --output text \ --query Plaintext assert_success echo "$output" | base64 -d > "$decrypted_file" decrypted=$(cat "$decrypted_file") [ "$decrypted" = "hello-kms-bats" ] rm -f "$plaintext_file" "$ciphertext_file" "$decrypted_file" } @test "KMS: generate data key" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms generate-data-key --key-id "$KEY_ID" --key-spec AES_256 assert_success plaintext=$(json_get "$output" '.Plaintext') [ -n "$plaintext" ] ciphertext=$(json_get "$output" '.CiphertextBlob') [ -n "$ciphertext" ] } # --- KMS Alias Tests --- @test "KMS: create alias" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') alias_name="alias/bats-test-$(unique_name)" run aws_cmd kms create-alias --alias-name "$alias_name" --target-key-id "$KEY_ID" assert_success # Cleanup alias aws_cmd kms delete-alias --alias-name "$alias_name" >/dev/null 2>&1 || true } @test "KMS: list aliases" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') alias_name="alias/bats-test-$(unique_name)" aws_cmd kms create-alias --alias-name "$alias_name" --target-key-id "$KEY_ID" >/dev/null run aws_cmd kms list-aliases assert_success found=$(echo "$output" | jq --arg name "$alias_name" '.Aliases | any(.AliasName == $name)') [ "$found" = "true" ] # Cleanup alias aws_cmd kms delete-alias --alias-name "$alias_name" >/dev/null 2>&1 || true } @test "KMS: create HMAC key and describe returns MacAlgorithms" { out=$(aws_cmd kms create-key \ --description "bats-hmac-$(unique_name)" \ --key-spec HMAC_256 \ --key-usage GENERATE_VERIFY_MAC) KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') [ -n "$KEY_ID" ] spec=$(json_get "$out" '.KeyMetadata.KeySpec') [ "$spec" = "HMAC_256" ] run aws_cmd kms describe-key --key-id "$KEY_ID" assert_success macs=$(echo "$output" | jq -r '.KeyMetadata.MacAlgorithms[0]') [ "$macs" = "HMAC_SHA_256" ] } @test "KMS: delete alias" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') alias_name="alias/bats-test-$(unique_name)" aws_cmd kms create-alias --alias-name "$alias_name" --target-key-id "$KEY_ID" >/dev/null run aws_cmd kms delete-alias --alias-name "$alias_name" assert_success # Verify alias is deleted run aws_cmd kms list-aliases found=$(echo "$output" | jq --arg name "$alias_name" '.Aliases | any(.AliasName == $name)') [ "$found" = "false" ] } @test "KMS: create key with reserved override tag uses pinned ID and strips reserved tag" { run aws_cmd kms create-key \ --description "override-key" \ --tags TagKey=floci:override-id,TagValue=bats-pinned-key TagKey=env,TagValue=test assert_success KEY_ID=$(json_get "$output" '.KeyMetadata.KeyId') [ "$KEY_ID" = "bats-pinned-key" ] run aws_cmd kms list-resource-tags --key-id "$KEY_ID" assert_success has_reserved=$(echo "$output" | jq 'any(.Tags[]?; .TagKey == "floci:override-id")') [ "$has_reserved" = "false" ] env_value=$(echo "$output" | jq -r '.Tags[] | select(.TagKey == "env") | .TagValue') [ "$env_value" = "test" ] } @test "KMS: duplicate reserved override tag fails with AlreadyExistsException" { out=$(aws_cmd kms create-key \ --description "override-key" \ --tags TagKey=floci:override-id,TagValue=bats-duplicate-key) KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms create-key \ --description "override-key-2" \ --tags TagKey=floci:override-id,TagValue=bats-duplicate-key assert_failure [[ "$output" == *"AlreadyExistsException"* ]] } @test "KMS: tag-resource rejects reserved override tag after creation" { out=$(aws_cmd kms create-key --description "bats-test-key") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms tag-resource \ --key-id "$KEY_ID" \ --tags TagKey=floci:override-id,TagValue=late-id assert_failure [[ "$output" == *"ValidationException"* ]] } # Issue #1844: Decrypt must enforce KeyId against wrapping key @test "KMS: decrypt with matching KeyId returns plaintext" { out=$(aws_cmd kms create-key --description "bats-1844-match") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$KEY_ID" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --key-id "$KEY_ID" \ --output text \ --query Plaintext assert_success [ "$(echo "$output" | base64 -d)" = "secret data" ] rm -f "$plaintext_file" "$ciphertext_file" } @test "KMS: decrypt with mismatched KeyId returns IncorrectKeyException" { out_a=$(aws_cmd kms create-key --description "bats-1844-a") key_a=$(json_get "$out_a" '.KeyMetadata.KeyId') out_b=$(aws_cmd kms create-key --description "bats-1844-b") key_b=$(json_get "$out_b" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$key_a" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --key-id "$key_b" assert_failure [[ "$output" == *"IncorrectKeyException"* ]] rm -f "$plaintext_file" "$ciphertext_file" aws_cmd kms schedule-key-deletion --key-id "$key_a" --pending-window-in-days 7 >/dev/null 2>&1 || true aws_cmd kms schedule-key-deletion --key-id "$key_b" --pending-window-in-days 7 >/dev/null 2>&1 || true } @test "KMS: reencrypt with matching SourceKeyId re-wraps ciphertext" { out_a=$(aws_cmd kms create-key --description "bats-reencrypt-match-a") key_a=$(json_get "$out_a" '.KeyMetadata.KeyId') out_b=$(aws_cmd kms create-key --description "bats-reencrypt-match-b") key_b=$(json_get "$out_b" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) reencrypted_file=$(mktemp) decrypted_file=$(mktemp) echo -n "reencrypt test data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$key_a" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms re-encrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --source-key-id "$key_a" \ --destination-key-id "$key_b" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$reencrypted_file" run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$reencrypted_file" \ --output text \ --query Plaintext assert_success echo "$output" | base64 -d > "$decrypted_file" [ "$(cat "$decrypted_file")" = "reencrypt test data" ] rm -f "$plaintext_file" "$ciphertext_file" "$reencrypted_file" "$decrypted_file" aws_cmd kms schedule-key-deletion --key-id "$key_a" --pending-window-in-days 7 >/dev/null 2>&1 || true aws_cmd kms schedule-key-deletion --key-id "$key_b" --pending-window-in-days 7 >/dev/null 2>&1 || true } @test "KMS: reencrypt with mismatched SourceKeyId returns IncorrectKeyException" { out_a=$(aws_cmd kms create-key --description "bats-reencrypt-mismatch-a") key_a=$(json_get "$out_a" '.KeyMetadata.KeyId') out_b=$(aws_cmd kms create-key --description "bats-reencrypt-mismatch-b") key_b=$(json_get "$out_b" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) echo -n "reencrypt test data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$key_a" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms re-encrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --source-key-id "$key_b" \ --destination-key-id "$key_a" assert_failure [[ "$output" == *"IncorrectKeyException"* ]] rm -f "$plaintext_file" "$ciphertext_file" aws_cmd kms schedule-key-deletion --key-id "$key_a" --pending-window-in-days 7 >/dev/null 2>&1 || true aws_cmd kms schedule-key-deletion --key-id "$key_b" --pending-window-in-days 7 >/dev/null 2>&1 || true } @test "KMS: decrypt with disabled key returns DisabledException" { out=$(aws_cmd kms create-key --description "bats-disabled-decrypt") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$KEY_ID" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms disable-key --key-id "$KEY_ID" assert_success run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --key-id "$KEY_ID" assert_failure [[ "$output" == *"DisabledException"* ]] rm -f "$plaintext_file" "$ciphertext_file" } @test "KMS: encrypt with disabled key returns DisabledException" { out=$(aws_cmd kms create-key --description "bats-disabled-encrypt") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms disable-key --key-id "$KEY_ID" assert_success plaintext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt --key-id "$KEY_ID" --plaintext "fileb://$plaintext_file" assert_failure [[ "$output" == *"DisabledException"* ]] rm -f "$plaintext_file" } @test "KMS: decrypt with pending deletion key returns KmsInvalidStateException" { out=$(aws_cmd kms create-key --description "bats-pending-decrypt") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') plaintext_file=$(mktemp) ciphertext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt \ --key-id "$KEY_ID" \ --plaintext "fileb://$plaintext_file" \ --output text \ --query CiphertextBlob assert_success echo "$output" | base64 -d > "$ciphertext_file" run aws_cmd kms schedule-key-deletion --key-id "$KEY_ID" --pending-window-in-days 7 assert_success run aws_cmd kms decrypt \ --ciphertext-blob "fileb://$ciphertext_file" \ --key-id "$KEY_ID" assert_failure [[ "$output" == *"KMSInvalidStateException"* ]] rm -f "$plaintext_file" "$ciphertext_file" } @test "KMS: encrypt with pending deletion key returns KmsInvalidStateException" { out=$(aws_cmd kms create-key --description "bats-pending-encrypt") KEY_ID=$(json_get "$out" '.KeyMetadata.KeyId') run aws_cmd kms schedule-key-deletion --key-id "$KEY_ID" --pending-window-in-days 7 assert_success plaintext_file=$(mktemp) echo -n "secret data" > "$plaintext_file" run aws_cmd kms encrypt --key-id "$KEY_ID" --plaintext "fileb://$plaintext_file" assert_failure [[ "$output" == *"KMSInvalidStateException"* ]] rm -f "$plaintext_file" }