docs: correct capability-flag threat-model boundary; add detection guidance
the docs claimed 'you cannot grant yourself authority without already having it', which is false in the shared-DEK model: a DEK-holder with write access can copy a sealed True flag onto its own doc. replaced with the honest boundary (the flag is unforgeable WITHOUT the DEK, but is not a defense against a malicious DEK-holder, which is out of scope by design) and added operational guidance to detect a self-grant by auditing authorization state. no code or storage-format change. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
parent
a0824c4b1a
commit
2d01805427
45
README.md
45
README.md
@ -13,20 +13,20 @@ authorization system and the key-document schema; the crypto primitives live in
|
|||||||
## Install
|
## Install
|
||||||
|
|
||||||
```
|
```
|
||||||
envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.0
|
envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Direct:
|
Direct:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install "envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.0"
|
pip install "envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
The base install uses a local JSON file for storage (stdlib only). For shared
|
The base install uses a local JSON file for storage (stdlib only). For shared
|
||||||
dev→server storage, install the mongo extra:
|
dev→server storage, install the mongo extra:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install "envelope_authorizer[mongo] @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.0"
|
pip install "envelope_authorizer[mongo] @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
Installing pulls `envelope_crypto` (and `mongo` with the extra). After install,
|
Installing pulls `envelope_crypto` (and `mongo` with the extra). After install,
|
||||||
@ -47,12 +47,39 @@ Each key carries an **encrypted capability flag** — `meta.authorizer` =
|
|||||||
- **Servers** are authorized with `allowed: False` → they can boot and use the DEK
|
- **Servers** are authorized with `allowed: False` → they can boot and use the DEK
|
||||||
(decrypt project data) but **cannot authorize anything else**.
|
(decrypt project data) but **cannot authorize anything else**.
|
||||||
|
|
||||||
The flag is GCM-sealed under the DEK on purpose: a server **cannot flip its own
|
The flag is GCM-sealed under the DEK on purpose. What that buys, stated honestly:
|
||||||
flag in the database to escalate**, because forging a valid flag requires already
|
|
||||||
holding the DEK, and editing the stored ciphertext breaks the auth tag. The
|
- **Without the DEK, the flag is inert.** A stolen database at rest cannot be
|
||||||
guarantee is precisely: *you cannot grant yourself authority without already
|
escalated — the flags are ciphertext, unforgeable and unflippable without the key
|
||||||
having it.* (A legitimate authorizer can of course mint new flags — that is its
|
(editing the stored ciphertext breaks the GCM auth tag). This stops passive DB
|
||||||
job. This stops passive DB tampering, not an authorizer.)
|
tampering.
|
||||||
|
- **It does not defend against a party that already holds the DEK and can write
|
||||||
|
storage.** The flag is sealed under the *shared* DEK and is **not bound to key
|
||||||
|
identity**, so such a party can copy a valid sealed `True` flag from an
|
||||||
|
authorizer's doc onto its own and self-grant authority. This is **out of scope by
|
||||||
|
design**: the trust model assumes DEK-holders are trusted. A DEK-holder already
|
||||||
|
has full data access (DEK + write access is game over), so self-granting authority
|
||||||
|
exposes no additional data. We deliberately do not add per-key signing to prevent
|
||||||
|
it — the residual is handled operationally (below).
|
||||||
|
|
||||||
|
So the precise boundary is: **the capability flag is unforgeable without the DEK; it
|
||||||
|
is not a defense against a malicious DEK-holder.** (A legitimate authorizer can of
|
||||||
|
course mint new flags — that is its job.)
|
||||||
|
|
||||||
|
### Operational guidance (the residual control)
|
||||||
|
|
||||||
|
Because the DEK-holder case above is **not** prevented cryptographically in the
|
||||||
|
shared-DEK model, the intended mitigation is **detection, not crypto**. Consumers
|
||||||
|
should periodically audit the authorization state in storage:
|
||||||
|
|
||||||
|
- enumerate every key doc's capability flag (`list` shows authorizer status),
|
||||||
|
- compare the set of `allowed: True` keys against a known-good list of expected
|
||||||
|
authorizers,
|
||||||
|
- alert on any unexpected authorizer.
|
||||||
|
|
||||||
|
An unexpected `allowed: True` appearing in storage is the signal that a DEK-holder
|
||||||
|
self-granted — catch it by review, then revoke and re-authorize from a trusted
|
||||||
|
machine.
|
||||||
|
|
||||||
`revoke` is **bookkeeping, not rotation**: it deletes the record so the key can no
|
`revoke` is **bookkeeping, not rotation**: it deletes the record so the key can no
|
||||||
longer unwrap at boot, but it does not rotate the DEK or scrub it from a machine
|
longer unwrap at boot, but it does not rotate the DEK or scrub it from a machine
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user