aws-kms-decrypt-via-reencrypt

ReEncryptFrom proves KMS can decrypt the source ciphertext; with an attacker-decryptable destination key, data is recoverable.

derived aws emits CanDecrypt

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanRead {'key': None}

where

node_type(?key) == EncryptionKey ?principal has EFFECTIVE kms:ReEncryptFrom on ?key, permitted by identity + key policy

optional

Present → upgrade the emitted state; absent → downgrade (not a hard requirement).

{'principal': None} CanDecrypt {'destkey': None}

emit

target typeEncryptionKey
source?principal
target?key
permissionskms:ReEncryptFrom kms:ReEncryptTo
conditionskey_permission resource_policy condition_expression
state logicif matched(optional[0]) (attacker controls a decryptable destination key) AND policies permit: ACTIVE; else CONDITIONAL(key_permission) — ReEncryptFrom alone never returns plaintext

Narrative

{principal.name} can ReEncrypt ciphertext from {key.name} (kms:ReEncryptFrom); paired with a key it can decrypt, this recovers the data.

Raw rule rules/derived/aws/kms.yaml

id: aws-kms-decrypt-via-reencrypt
emits: CanDecrypt
description: ReEncryptFrom proves KMS can decrypt the source ciphertext; with an attacker-decryptable
  destination key, data is recoverable.
match:
- - principal: null
  - CanRead
  - key: null
where:
- node_type(?key) == EncryptionKey
- ?principal has EFFECTIVE kms:ReEncryptFrom on ?key, permitted by identity + key policy
optional:
- - principal: null
  - CanDecrypt
  - destkey: null
emit:
  target_type: EncryptionKey
  source: ?principal
  target: ?key
  permissions:
  - kms:ReEncryptFrom
  - kms:ReEncryptTo
  conditions:
  - key_permission
  - resource_policy
  - condition_expression
  state_logic: "if matched(optional[0]) (attacker controls a decryptable destination key) AND policies\
    \ permit: ACTIVE; else CONDITIONAL(key_permission) \u2014 ReEncryptFrom alone never returns plaintext"
  confidence: min(contributing_confidences) * 0.6
  derived_from:
  - <effective kms:ReEncryptFrom permission>
  - <CanDecrypt destkey?>
  false_positive_note: "kms:ReEncryptFrom NEVER returns plaintext \u2014 it only re-wraps ciphertext under\
    \ another key. Data is recovered only if the attacker can decrypt the DESTINATION key (ReEncryptTo\
    \ target). Without that, this is a data-movement/persistence primitive, not exfil -> keep CONDITIONAL\
    \ and low confidence (0.6)."
  narrative: '{principal.name} can ReEncrypt ciphertext from {key.name} (kms:ReEncryptFrom); paired with
    a key it can decrypt, this recovers the data.'
move · open · esc close