fix: EA-1 refuse authorize of an already-recorded key (v0.1.3)

authorize never checked the target fingerprint against existing docs before
save()'s upsert-by-_id, so authorizing the local machine's own public key
under a new friendly name silently replaced the local authorizer record
(can_authorize demoted to False) while printing a success banner. With a
sole authorizer this bricks the CLI: authorize refuses (not permitted),
init refuses (already initialized), and revoke of the local key refuses
(refusing to revoke the local key) -- no in-CLI recovery. Mirror revoke's
local-key guard and extend it to any existing _id, so a duplicate target
is refused with a clear message instead of silently replacing the record.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-02 16:41:09 -04:00
parent f2e9e5fe35
commit bfeee80712
4 changed files with 34 additions and 11 deletions
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.1.2"
__version__ = "0.1.3"
+23 -4
View File
@@ -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)