libertaria-stack/sdk/janus-sdk/libertaria/identity.jan

99 lines
2.8 KiB
Plaintext

-- libertaria/identity.jan
-- Cryptographic identity for sovereign agents
-- Exit is Voice: Identity can be rotated, expired, or burned
module Identity exposing
( Identity
, create, rotate, burn
, is_valid, is_expired
, public_key, fingerprint
, sign, verify
)
import crypto.{ed25519, hash}
import time.{timestamp, duration}
-- Core identity type with cryptographic material and metadata
type Identity =
{ public_key: ed25519.PublicKey
, secret_key: ed25519.SecretKey -- Encrypted at rest
, created_at: timestamp.Timestamp
, expires_at: ?timestamp.Timestamp -- Optional expiry
, rotated_from: ?fingerprint.Fingerprint -- Chain of custody
, revoked: bool
}
-- Create new sovereign identity
-- Fresh keypair, no history, self-sovereign
fn create() -> Identity
let (pk, sk) = ed25519.generate_keypair()
let now = timestamp.now()
{ public_key = pk
, secret_key = sk
, created_at = now
, expires_at = null
, rotated_from = null
, revoked = false
}
-- Rotate identity: New keys, linked provenance
-- Old identity becomes invalid after grace period
fn rotate(old: Identity) -> (Identity, Identity)
assert not old.revoked "Cannot rotate revoked identity"
let (new_pk, new_sk) = ed25519.generate_keypair()
let now = timestamp.now()
let old_fp = fingerprint.of_identity(old)
let new_id =
{ public_key = new_pk
, secret_key = new_sk
, created_at = now
, expires_at = null
, rotated_from = some(old_fp)
, revoked = false
}
-- Old identity gets short grace period then auto-expires
let grace_period = duration.hours(24)
let expired_old = { old with expires_at = some(now + grace_period) }
(new_id, expired_old)
-- Burn identity: Cryptographic deletion
-- After burn, no messages can be signed, verification still works for history
fn burn(id: Identity) -> Identity
{ id with
secret_key = ed25519.zero_secret(id.secret_key)
, revoked = true
, expires_at = some(timestamp.now())
}
-- Check if identity is currently valid
fn is_valid(id: Identity) -> bool
not id.revoked and not is_expired(id)
-- Check if identity has expired
fn is_expired(id: Identity) -> bool
match id.expires_at with
| null -> false
| some(t) -> timestamp.now() > t
-- Get public key for sharing/verification
fn public_key(id: Identity) -> ed25519.PublicKey
id.public_key
-- Get fingerprint (short, unique identifier)
fn fingerprint(id: Identity) -> fingerprint.Fingerprint
fingerprint.of_key(id.public_key)
-- Sign message with this identity
fn sign(id: Identity, message: bytes.Bytes) -> signature.Signature
assert is_valid(id) "Cannot sign with invalid identity"
ed25519.sign(id.secret_key, message)
-- Verify signature against this identity's public key
fn verify(id: Identity, message: bytes.Bytes, sig: signature.Signature) -> bool
ed25519.verify(id.public_key, message, sig)