gcp-kms-can-decrypt

cloudkms.cryptoKeyVersions.useToDecrypt lets a principal decrypt with the key.

derived gcp emits CanDecrypt

match (effective permission)

{ "action": "cloudkms.cryptoKeyVersions.useToDecrypt", "resource_type": "google.cloud.kms.CryptoKey" }

where

CryptoKey.purpose is a useToDecrypt purpose: ENCRYPT_DECRYPT, RAW_ENCRYPT_DECRYPT, or ASYMMETRIC_DECRYPT (AsymmetricDecrypt API; the IAM permission is still useToDecrypt) purpose is NOT a KEM key — key encapsulation uses the DISTINCT permission cloudkms.cryptoKeyVersions.useToDecapsulate, matched by gcp-kms-can-decapsulate, not this rule at least one CryptoKeyVersion is ENABLED action resolves at the key scope (per-key allow policy OR inherited keyRing/project/folder/org binding)

emit

target typeEncryptionKey
source<principal>
target<CryptoKey (EncryptionKey subtype)>
permissionscloudkms.cryptoKeyVersions.useToDecrypt
conditionsiam_permission resource_policy key_permission service_state
state logicACTIVE if useToDecrypt resolves AND >=1 version ENABLED; CONDITIONAL(service_state) if all versions DISABLED (re-enableable by a cryptoKeyVersions.update holder); BLOCKED if all versions DESTROYED, or a VPC-SC perimeter / IAM deny policy denies cloudkms.cryptoKeyVersions.useToDecrypt at the effective scope (denied_by=<perimeter/deny>).

Narrative

{principal.name} can decrypt with key {key.name} (cloudkms.cryptoKeyVersions.useToDecrypt) - unlocking any data encrypted under this key.

Raw rule rules/derived/gcp/kms.yaml

id: gcp-kms-can-decrypt
emits: CanDecrypt
description: cloudkms.cryptoKeyVersions.useToDecrypt lets a principal decrypt with the key.
match_effective_permission:
  action: cloudkms.cryptoKeyVersions.useToDecrypt
  resource_type: google.cloud.kms.CryptoKey
where:
- 'CryptoKey.purpose is a useToDecrypt purpose: ENCRYPT_DECRYPT, RAW_ENCRYPT_DECRYPT, or ASYMMETRIC_DECRYPT
  (AsymmetricDecrypt API; the IAM permission is still useToDecrypt)'
- "purpose is NOT a KEM key \u2014 key encapsulation uses the DISTINCT permission cloudkms.cryptoKeyVersions.useToDecapsulate,\
  \ matched by gcp-kms-can-decapsulate, not this rule"
- at least one CryptoKeyVersion is ENABLED
- action resolves at the key scope (per-key allow policy OR inherited keyRing/project/folder/org binding)
emit:
  target_type: EncryptionKey
  source: <principal>
  target: <CryptoKey (EncryptionKey subtype)>
  permissions:
  - cloudkms.cryptoKeyVersions.useToDecrypt
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  - service_state
  state_logic: ACTIVE if useToDecrypt resolves AND >=1 version ENABLED; CONDITIONAL(service_state) if
    all versions DISABLED (re-enableable by a cryptoKeyVersions.update holder); BLOCKED if all versions
    DESTROYED, or a VPC-SC perimeter / IAM deny policy denies cloudkms.cryptoKeyVersions.useToDecrypt
    at the effective scope (denied_by=<perimeter/deny>).
  confidence: min(contributing_confidences) * 0.97
  derived_from:
  - <effective cloudkms.cryptoKeyVersions.useToDecrypt grant>
  false_positive_note: "Gate on CryptoKey.purpose \u2014 useToDecrypt is meaningless on an ASYMMETRIC_SIGN\
    \ or MAC key; do not emit CanDecrypt there. roles/cloudkms.admin does NOT grant useToDecrypt (separation\
    \ of duties) \u2014 admin reaches decrypt only via setIamPolicy self-grant (see gcp-kms-selfgrant-to-decrypt).\
    \ IAM inheritance: a keyRing/project/folder/org binding covers ALL keys in scope; keep the target\
    \ key precise. Requires >=1 ENABLED version \u2014 an all-disabled key is CONDITIONAL/BLOCKED even\
    \ with the permission."
  narrative: "{principal.name} can decrypt with key {key.name} (cloudkms.cryptoKeyVersions.useToDecrypt)\
    \ \u2014 unlocking any data encrypted under this key."
move · open · esc close