Replace passcode identity with Ed25519 challenge-response. - Enrolment consumes a one-time bootstrap secret, after which no static secret is used on the wire again. - Nonces are single-use and consumed before the signature is judged, so a captured or bad signature cannot be retried. - Sessions are short-lived, scoped, and typed agent vs human_via_agent. - Human view access uses one-time codes with a 5 minute redemption window and a 1 hour session, deliberately different numbers: the code is a key, the session is the door. - Invariant: only an agent signature may act as an agent. Enforced and tested. - Append-only audit trail. 54 tests, covering refusals rather than only the happy path. Reference implementation, not reviewed or hardened. See README Status.
38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
"""Sigil: agent identity for the new internet.
|
|
|
|
An agent proves who it is with a signature, not a passcode. A passcode is a
|
|
credential that can be copied; a signature is evidence that cannot be forged
|
|
without the private key, which never leaves the agent.
|
|
|
|
store = Store("sigil.db")
|
|
secret = identity.seed_bootstrap(store, "bigpickle")
|
|
fingerprint = identity.enrol(store, "bigpickle", public_b64, secret)
|
|
|
|
chal = identity.challenge(store, "bigpickle")
|
|
signed = keys.sign(private, chal["nonce"].encode())
|
|
session = identity.verify(store, chal["nonce"], b64(signed))
|
|
|
|
# A human looking at the same things, through the agent
|
|
code = viewcodes.issue_code(store, "bigpickle")
|
|
view = viewcodes.redeem_code(store, code["code"])
|
|
|
|
# And the rule that holds it together
|
|
sessions.assert_may_act_as_agent(sessions.resolve(store, view["token"]))
|
|
# -> raises AgentImpersonationDenied
|
|
"""
|
|
|
|
from . import errors, identity, keys, sessions, store, viewcodes
|
|
from .store import Store
|
|
|
|
__version__ = "0.1.0"
|
|
|
|
__all__ = [
|
|
"Store",
|
|
"errors",
|
|
"identity",
|
|
"keys",
|
|
"sessions",
|
|
"store",
|
|
"viewcodes",
|
|
"__version__",
|
|
] |