Derives the fixed-width cache partition id a vault hands the caching CMM.
One cache process serves every partition within a vault (ADR-0001 decision 3), so the thing that keeps one scope's data key out of another scope's cache lookup is the partition id, not a second process. Decision 7 fixes the derivation:
partition_id = binary_part(sha256(vault_namespace, 0, encoded_selector), 0, 16)and this module is the only place it is computed. The result is what the
vault hands the engine's caching CMM as :partition_id.
The write side also carries the key
The engine's encryption cache id hashes the partition id, the suite and the
context, and never the key that wrapped the entry's data key. With the
formula above alone, a write after a new key version is minted finds the
warm entry from before the mint and wraps its data key under the old
version. So the partition a vault hands the caching CMM on the write side
(an encrypt, and a rekey's write half) also carries the resolved key's
identity - the namespace and name of an Encryptor.Key.Aes, the key id of
an Encryptor.Key.Kms - in a length-prefixed pre-image, and a new version
is a new partition, cold at once (ADR-0001 Amendment B). The read side is
unchanged: the decryption cache id already hashes the message's encrypted
data keys, which name the key that wrapped them.
Why the width is fixed, and why it is 16
The engine's cache-id computation (compute_encryption_cache_id/3 in the
aws_encryption_sdk caching CMM) concatenates
the partition id into the cache id pre-image with no length prefix. A
variable-width partition id therefore makes the pre-image ambiguous, and two
different partitions could in principle hash to one cache id - which is two
scopes sharing a data key. Sixteen bytes is the width of the UUID the
engine generates when no partition id is given, so matching it removes the
ambiguity by construction rather than by argument.
Nothing here may be relaxed into "any binary": the width is load-bearing.
What a partition id is not
It is a cache-key input only. It is not key material, it is not secret, and it never reaches a message. Deriving it by hash rather than using the raw selector keeps scope identifiers out of a structure this package does not control the lifetime of, and buys the uniform width for free.
Records: ADR-0001 decisions 3 and 7 and Amendment B; the selector type is ADR-0004 decision 3.
Summary
Functions
The width every partition id has, in bytes.
The 16-byte partition id for a vault and a key selector.
Functions
@spec bytes() :: pos_integer()
The width every partition id has, in bytes.
iex> Encryptor.Vault.Partition.bytes()
16
@spec id(module(), Encryptor.Error.selector()) :: binary()
The 16-byte partition id for a vault and a key selector.
Pure, total over the selector types ADR-0004 decision 3 admits, and allocating nothing that outlives the call.
iex> id = Encryptor.Vault.Partition.id(MyApp.Vault, "scope-42")
iex> byte_size(id)
16
iex> Encryptor.Vault.Partition.id(MyApp.Vault, "scope-42") ==
...> Encryptor.Vault.Partition.id(MyApp.Vault, "scope-43")
false
iex> Encryptor.Vault.Partition.id(MyApp.Vault, :default) ==
...> Encryptor.Vault.Partition.id(MyApp.OtherVault, :default)
false