diff --git a/README.md b/README.md index 1298811..06befa2 100644 --- a/README.md +++ b/README.md @@ -13,26 +13,26 @@ authorization system and the key-document schema; the crypto primitives live in ## Install ``` -envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.2 +envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.3 ``` Direct: ```bash -pip install "envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.2" +pip install "envelope_authorizer @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.3" ``` The base install uses a local JSON file for storage (stdlib only). For shared dev→server storage, install the mongo extra: ```bash -pip install "envelope_authorizer[mongo] @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.2" +pip install "envelope_authorizer[mongo] @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_authorizer.git@v0.1.3" ``` Installing pulls `envelope_crypto` (and `mongo` with the extra). After install, the `authorizer` command is on your PATH; `python -m envelope_authorizer` also works. -Drop the `@v0.1.2` suffix from the line above to install the latest unpinned. +Drop the `@v0.1.3` suffix from the line above to install the latest unpinned. ## Trust model (read this) @@ -129,7 +129,11 @@ initialized or the friendly name is taken. Boots the local DEK, verifies the local key is itself an authorizer, then wraps the same DEK to the target public key and stores a new key doc. Omit `--can-authorize` for servers (`allowed: False`); pass it only for trusted -dev/home machines. +dev/home machines. Refuses a target key whose fingerprint already has a record +(most importantly the local key itself) — `save` upserts by `_id`, so +authorizing an already-known key would silently replace its existing doc +(capability flag and friendly name) under a success banner instead of adding a +new key. Revoke the existing record first if you intend to re-authorize it. ``` [✔] Authorized Jy7k2ey7... | friendly: server1 [can_authorize=False] diff --git a/pyproject.toml b/pyproject.toml index 24f7c87..b363f81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "envelope_authorizer" -version = "0.1.2" +version = "0.1.3" description = "CLI key-authorization manager for envelope_crypto" requires-python = ">=3.10" dependencies = [ diff --git a/src/envelope_authorizer/__init__.py b/src/envelope_authorizer/__init__.py index b3f4756..ae73625 100644 --- a/src/envelope_authorizer/__init__.py +++ b/src/envelope_authorizer/__init__.py @@ -1 +1 @@ -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/src/envelope_authorizer/commands/authorize.py b/src/envelope_authorizer/commands/authorize.py index 461dc9e..2f67a43 100644 --- a/src/envelope_authorizer/commands/authorize.py +++ b/src/envelope_authorizer/commands/authorize.py @@ -2,7 +2,11 @@ boots the local DEK, verifies the local key is itself an authorizer, then wraps the same DEK to the target public key and stores a new key doc. `--can-authorize` -decides whether the new key may authorize others (omit it for servers). +decides whether the new key may authorize others (omit it for servers). Refuses +a target key that fingerprints to an existing `_id` — `save` upserts by `_id`, so +authorizing a key that is already on record (most dangerously the local key +itself) would silently replace that doc's capability flag and friendly name +under a success banner instead of adding a new key. """ from . import ( @@ -10,6 +14,7 @@ from . import ( boot_local, build_doc, find_by_friendly, + local_fingerprint, make_flag, read_flag, ) @@ -25,9 +30,23 @@ def run(config, storage, args) -> None: if not read_flag(crypto, local_doc["meta"]["authorizer"]): raise CommandError("this key is not permitted to authorize others") - new_fp, new_wrapped = crypto.encrypt_aes_key_with_rsa( - crypto.master_key, args.key - ) + new_fp = crypto.get_rsa_key_fingerprint(args.key) + if new_fp == local_fingerprint(crypto, config): + raise CommandError( + "target key is the local key; authorize would silently replace the " + "local authorizer record — use a different keypair, or `authorizer " + "list` if you meant to check its status" + ) + existing = storage.get(new_fp) + if existing: + existing_friendly = existing.get("meta", {}).get("friendly", "?") + raise CommandError( + f"target key is already authorized as '{existing_friendly}'; " + f"authorize would silently replace that record — revoke it first " + f"if you intend to re-authorize it" + ) + + _, new_wrapped = crypto.encrypt_aes_key_with_rsa(crypto.master_key, args.key) flag = make_flag(crypto, args.can_authorize) doc = build_doc(new_fp, new_wrapped, flag, config.identity, args.friendly) storage.save(doc)