Reports a refused GCP Decrypt as kms_refused - #122
Merged
Merged
Conversation
A Decrypt that Cloud KMS refuses with HTTP 400 or 404 (an AAD
mismatch, a key or version that is not there) now answers
{:invalid_key_descriptor, {:kms_refused, status}} from both
encryption_key/2 and decryption_keys/2, which share unwrap/3. A 403,
a 429, a 5xx, and transport, token and malformed-response failures
still answer {:key_unavailable, selector}, so a provider-level
suspension keeps the answer ADR-0005 Amendment A and ADR-0010 give
it. provision/2 is unchanged.
ADR-0007 Amendment B (proposed) records the term, ruled by the
operator, 2026-09-30, and names the reversible 400 of a disabled
version; a foot Note on ADR-0002 points at it from decision 6.
Refs: enc-hpx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs: enc-hpx
What changes
Encryptor.Provider.GcpKmsnow tells a refusedDecryptapart from an outage. ADecryptthat Cloud KMS refuses with HTTP 400 (an AAD mismatch) or 404 (a key or version that is not there) answers{:invalid_key_descriptor, {:kms_refused, status}}. Every other failure keeps{:key_unavailable, selector}: a 403, a 429, a 5xx or any other status, a transport failure, a token the server would not mint, and a malformed response. The term was ruled by the operator, 2026-09-30.lib/encryptor/provider/gcp_kms.ex:unwrap/3maps its failure through the new privatedecrypt_failure/2over the module attribute@refused_statuses [400, 404].encryption_key/2anddecryption_keys/2both reachunwrap/3, so both answer the same way.provision/2andmint/4are unchanged. A new moduledoc section, "What a failedDecryptanswers", gives the table. It names the reversible 400 of a disabled version and says where an operator finds the status, sinceEncryptor.Error's message renders only the family.lib/encryptor/provider.ex: thereasondoc's{:invalid_key_descriptor, detail}bullet gains a sentence: a provider may also answer it for a permanent refusal by its backing service.docs/adr/0007-gcp-kms-wrap-provider.md: Amendment B, with its own Status line, proposed. It answers open question 4 for 400 and 404 and leaves 403 with ADR-0005 Amendment A's open question A-2. It names the typespec-section sentences it supersedes by line (:731,:736-741).docs/adr/0002-key-providers.md: a dated foot Note on decision 6 that points at the Amendment.changelog.d/enc-hpx.md: under**Breaking**. The README's pre-1.0 banner records every change to the error vocabulary under that heading, and a caller that matched:key_unavailablefor a refused row now gets a different term.test/encryptor/provider/gcp_kms_test.exsand a new fake,Encryptor.GcpKms.DecryptStatusKms, intest/support/gcp_kms_fakes.ex.Why 403 stays
key_unavailableA revoked IAM binding on a scope's
CryptoKeyis the provider-level locus of a suspension. ADR-0005 Amendment A (accepted) fixes that both loci "surface the same reason to the caller" (its A3) and gives a suspended scope{:key_unavailable, selector}(its A4). ADR-0010 repeats it ("They compose as A3 says"). This PR edits neither record and neither guide.Tests over both classes
decryption_keys/2{:kms_refused, 400}, as ruled: "fails closed on a row whose claimed version does not match its blob", "fails closed on a wrapping moved to another scope's row" and "refuses the whole candidate list when one row will not unwrap"Sabotage, each check reverted from a copy and restored byte-equal:
@refused_statusesfails the disclosed-set test and the 403 testGate
Full
mix qualityis green on the committed tree: 676 of 676 tests, 96.5% coverage, Dialyzer, Credo, Docs and Doc links all clean. The commit was made on the tree the gate ran on, with the gate lock held.Provenance
encryption_key/2as well asdecryption_keys/2, because both shareunwrap/3. Ruled by the operator, 2026-09-30.Encryptor.Error's rendered message is unchanged.docs/adr/README.mdalready reads "accepted (2026-09-13, amended)" for ADR-0007, so it is not touched.Record check: every cite in the Amendment and the Note was re-read by anchor at
0bf205e. The appended sections sit after each file's last heading, so no cited line moved.git diff origin/main -- docs/adr/shows zero removed lines.