Harden Sigil: sigil.py
This commit is contained in:
parent
57c07758ce
commit
e894d838a5
34
sigil.py
34
sigil.py
@ -2,12 +2,13 @@
|
|||||||
"""Command line for Sigil.
|
"""Command line for Sigil.
|
||||||
|
|
||||||
sigil.py seed <agent> one-time enrolment secret
|
sigil.py seed <agent> one-time enrolment secret
|
||||||
sigil.py keygen <path> write an Ed25519 private key (0600)
|
sigil.py keygen <path> [pass] write an Ed25519 key (0600, encrypted if given)
|
||||||
sigil.py enrol <agent> <pubkey.b64> <secret>
|
sigil.py enrol <agent> <pubkey.b64> <secret>
|
||||||
sigil.py challenge <agent> nonce to sign
|
sigil.py challenge <agent> nonce to sign
|
||||||
sigil.py sign <keypath> <nonce> signature, base64
|
sigil.py verify-chain check the audit log for tampering
|
||||||
|
sigil.py sign <keypath> <nonce> [pass] signature, base64
|
||||||
sigil.py verify <nonce> <sig.b64> agent session token
|
sigil.py verify <nonce> <sig.b64> agent session token
|
||||||
sigil.py code <agent> one-time code for a human to view
|
sigil.py code <agent> <keypath> [pass] one-time code, proving the key
|
||||||
sigil.py redeem <code> human_via_agent session token
|
sigil.py redeem <code> human_via_agent session token
|
||||||
sigil.py whoami <token> describe a session
|
sigil.py whoami <token> describe a session
|
||||||
sigil.py events [actor] audit trail
|
sigil.py events [actor] audit trail
|
||||||
@ -48,7 +49,8 @@ def main(argv) -> int:
|
|||||||
elif cmd == "keygen":
|
elif cmd == "keygen":
|
||||||
path = argv[2] if len(argv) > 2 else KEY
|
path = argv[2] if len(argv) > 2 else KEY
|
||||||
private, public = keys.generate()
|
private, public = keys.generate()
|
||||||
keys.save_private(private, path)
|
passphrase = argv[3] if len(argv) > 3 else None
|
||||||
|
keys.save_private(private, path, passphrase)
|
||||||
out({
|
out({
|
||||||
"private_key": path,
|
"private_key": path,
|
||||||
"fingerprint": keys.fingerprint(public),
|
"fingerprint": keys.fingerprint(public),
|
||||||
@ -62,7 +64,7 @@ def main(argv) -> int:
|
|||||||
out(identity.challenge(store, argv[2]))
|
out(identity.challenge(store, argv[2]))
|
||||||
|
|
||||||
elif cmd == "sign":
|
elif cmd == "sign":
|
||||||
private = keys.load_private(argv[2])
|
private = keys.load_private(argv[2], argv[4] if len(argv) > 4 else None)
|
||||||
nonce = argv[3]
|
nonce = argv[3]
|
||||||
out({"nonce": nonce, "signature": base64.b64encode(
|
out({"nonce": nonce, "signature": base64.b64encode(
|
||||||
keys.sign(private, nonce.encode("utf-8"))).decode("ascii")})
|
keys.sign(private, nonce.encode("utf-8"))).decode("ascii")})
|
||||||
@ -71,7 +73,17 @@ def main(argv) -> int:
|
|||||||
out(identity.verify(store, argv[2], argv[3]))
|
out(identity.verify(store, argv[2], argv[3]))
|
||||||
|
|
||||||
elif cmd == "code":
|
elif cmd == "code":
|
||||||
out(viewcodes.issue_code(store, argv[2]))
|
# Mints durable authority, so it proves key possession rather than
|
||||||
|
# trusting a name on the command line.
|
||||||
|
if len(argv) < 4:
|
||||||
|
print("usage: sigil.py code <agent> <keypath> [passphrase]")
|
||||||
|
return 2
|
||||||
|
private = keys.load_private(argv[3], argv[4] if len(argv) > 4 else None)
|
||||||
|
chal = identity.challenge(store, argv[2])
|
||||||
|
signed = keys.sign(private, chal["nonce"].encode("utf-8"))
|
||||||
|
sig = base64.b64encode(signed).decode("ascii")
|
||||||
|
token = identity.verify(store, chal["nonce"], sig)["token"]
|
||||||
|
out(viewcodes.issue_code(store, token))
|
||||||
|
|
||||||
elif cmd == "redeem":
|
elif cmd == "redeem":
|
||||||
out(viewcodes.redeem_code(store, argv[2]))
|
out(viewcodes.redeem_code(store, argv[2]))
|
||||||
@ -86,6 +98,11 @@ def main(argv) -> int:
|
|||||||
session["may_act_as_agent"] = may_act
|
session["may_act_as_agent"] = may_act
|
||||||
out(session)
|
out(session)
|
||||||
|
|
||||||
|
elif cmd == "verify-chain":
|
||||||
|
result = store.verify_chain()
|
||||||
|
out(result)
|
||||||
|
return 0 if result["ok"] else 1
|
||||||
|
|
||||||
elif cmd == "events":
|
elif cmd == "events":
|
||||||
out(store.events(argv[2] if len(argv) > 2 else None,
|
out(store.events(argv[2] if len(argv) > 2 else None,
|
||||||
int(argv[3]) if len(argv) > 3 else 50))
|
int(argv[3]) if len(argv) > 3 else 50))
|
||||||
@ -97,6 +114,11 @@ def main(argv) -> int:
|
|||||||
except errors.SigilError as exc:
|
except errors.SigilError as exc:
|
||||||
print(json.dumps({"error": type(exc).__name__, "message": str(exc)}))
|
print(json.dumps({"error": type(exc).__name__, "message": str(exc)}))
|
||||||
return 1
|
return 1
|
||||||
|
except (ValueError, OSError) as exc:
|
||||||
|
# Wrong passphrase, unreadable key file. Report it as a plain result
|
||||||
|
# rather than a traceback: it is an ordinary operator mistake.
|
||||||
|
print(json.dumps({"error": type(exc).__name__, "message": str(exc)[:200]}))
|
||||||
|
return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user