KiiWORKS / Documentation
Security
SOURCE · docs/security.md · synced from the engineering repo
Security
Cryptographic Primitives
KiiWORKS uses battle-tested cryptographic libraries from the Rust ecosystem. No custom cryptography is implemented.
Key Algorithms
| Algorithm | Library | Usage |
|---|---|---|
| Schnorr (secp256k1) | secp256k1 0.29 | Transaction signing, DID authentication |
| X25519 | x25519-dalek 2 | Wire messaging key exchange (ECDH) |
| AES-256-GCM | aes-gcm 0.10 | Wire message encryption, timelock encryption |
| ChaCha20-Poly1305 | chacha20poly1305 0.10 | Alternative authenticated encryption |
| SHA-256 | sha2 0.10 | Content hashing, Merkle trees, API key hashing, commitments |
| BLAKE2b-256 | blake2 0.10 | Kaspa transaction sighash |
| Argon2id | argon2 0.5 | Password hashing (auth service) |
| PBKDF2-HMAC-SHA256 | pbkdf2 0.12 | Key derivation from passwords |
| HKDF-SHA256 | hkdf 0.12 | Key derivation from shared secrets |
| HMAC-SHA256 | hmac 0.12 | Webhook payload signing |
| Ed25519 | ed25519-dalek 2 | Alternative signature scheme |
| BIP-39 | bip39 2 | Mnemonic seed phrase generation (12/24 words) |
| BIP-44 | Custom (kiiworks-anvil) | HD key derivation path m/44'/111111'/0'/0/{index} |
Signature Scheme (Kaspa)
Kaspa uses Schnorr signatures over secp256k1 with a specific sighash protocol:
- Transaction inputs/outputs are serialised
- BLAKE2b-256 is computed with key
b"TransactionSigningHash"(NOT personalization) - The 32-byte hash is signed directly with
Keypair::sign_schnorr(msg) - Signature script format:
[0x41][64-byte-sig][0x01](OP_DATA_65 + sig + SIGHASH_ALL)
Key Exchange (Wire Messaging)
Wire channels use the Noise-NK-like pattern:
- Both parties generate X25519 keypairs via
generate_x25519_key_pair() - Public keys are exchanged via REST handshake endpoint
- Shared secret derived via
derive_shared_secret(our_private, their_public) - HKDF-SHA256 stretches the shared secret into AES-256-GCM keys
- Each message gets a unique 96-bit nonce via
generate_nonce() - Ciphertext + nonce + tag stored together in
WireEnvelope
Key Management
Wallet Keys
- Generation: BIP-39 mnemonic (12 or 24 words) → seed → BIP-44 HD derivation
- Storage: Private keys and mnemonics held in-memory only via
WalletServiceState - Derivation path:
m/44'/111111'/0'/0/{index}(coin type 111111 = Kaspa) - Display: Mnemonic shown exactly once at wallet creation, then never again
- Cleanup:
zeroizecrate ensures key material is zeroed on drop
API Keys (Gateway)
- Generation:
kw_prefix + 32 bytes ofrand::OsRng→ base64url encoding - Storage: Only the SHA-256 hash of the key is stored; raw key shown once at creation
- Identification: First 8 characters stored as
key_prefixfor human identification - Rotation: Old key is revoked, new key generated — no grace period
JWT Tokens
- Algorithm: HS256 (HMAC-SHA256)
- Signing key:
KIIWORKS_JWT_SECRETenvironment variable - Expiry: 3600 seconds (1 hour) by default
- Claims:
sub(user ID),exp(expiry),iat(issued at)
Wire Channel Keys
- X25519 private keys: Generated per-channel, held in
ChannelState, never serialised to JSON - Shared secrets: Derived via ECDH, held in memory, zeroed on channel close
- Nonces: 96-bit random nonces, unique per message
Threat Model
Trust Boundaries
┌─────────────────────────────────────────────────────┐
│ KiiWORKS Node │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ Auth │ │ Platform │ │ Blockchain │ │
│ │ Service │ │ Services │ │ Interface │ │
│ │ │ │ (39 services)│ │ (Kaspa RPC) │ │
│ └────┬─────┘ └──────┬───────┘ └──────┬────────┘ │
│ │ │ │ │
├───────┼───────────────┼─────────────────┼────────────┤
│ ▼ ▼ ▼ │
│ JWT Validation Business Logic TX Signing │
│ │
└─────────┬──────────────┬───────────────┬─────────────┘
│ │ │
┌────┴────┐ ┌─────┴─────┐ ┌────┴──────┐
│ Clients │ │ PostgreSQL│ │ Kaspa │
│ (HTTP) │ │ (optional)│ │ BlockDAG │
└─────────┘ └───────────┘ └───────────┘
Threats and Mitigations
| Threat | Mitigation |
|---|---|
| Stolen JWT | Short expiry (1h), HTTPS in production, dev_mode=false enforces JWT |
| Stolen API key | SHA-256 hashed storage — breach of DB doesn’t expose raw keys. Rate limiting bounds damage. Immediate revocation via gateway. |
| Data tampering | Blockchain anchoring provides tamper evidence. SHA-256 hashes anchored on Kaspa BlockDAG are immutable. |
| MITM on Wire channels | X25519 ECDH + AES-256-GCM provides authenticated encryption. Public key exchange happens over HTTPS. |
| Key compromise (wallet) | Keys held in-memory only, zeroed on drop. BIP-39 mnemonics shown once. No key export API. |
| DPP forgery | Each passport state is hashed and anchored on-chain. Merkle batch anchoring provides individual inclusion proofs. |
| Replay attacks | DID authentication uses time-limited challenge/response. Nonces are single-use. |
| DoS | Sliding-window rate limiting (per minute/hour/day) via gateway service. Axum’s backpressure handles connection floods. |
| SQL injection | SQLx with parameterised queries (no string interpolation). Most services use in-memory storage. |
| Secret leakage | Private keys excluded from Serialize derives. #[serde(skip)] on sensitive fields. Structured logging never logs key material. |
Blockchain Anchoring as Tamper Evidence
KiiWORKS does not store data on the blockchain. It stores hashes of data on the blockchain:
- DPP state → SHA-256 → 32 bytes
- Embed hash in P2PK script:
[0x20][32-byte-hash][0xac](sends to unspendable address derived from hash) - Submit transaction to Kaspa
- Transaction ID serves as proof-of-existence at a specific time
- Anyone can verify: query TX → extract script → compare hash
This provides:
- Immutability: Once confirmed, the hash is permanent on the BlockDAG
- Timestamping: Block header provides a trusted timestamp
- Non-repudiation: The signing key proves who anchored
- Efficiency: O(1) cost for N items when using Merkle batch anchoring
Security Checklist for Production
- Set
KIIWORKS_DEV_MODE=false - Set a strong
KIIWORKS_JWT_SECRET(minimum 32 bytes, random) - Enable TLS (
KIIWORKS_TLS_CERT+KIIWORKS_TLS_KEY) - Use PostgreSQL (
KIIWORKS_DATABASE_URL) — not in-memory storage - Set
KIIWORKS_LOG_FORMAT=jsonfor structured logging - Run behind a reverse proxy (nginx/Caddy) with rate limiting
- Create API keys for all external consumers via the gateway service
- Rotate JWT secrets periodically
- Monitor sentinel alerts for anomaly detection
- Back up PostgreSQL database regularly
- Keep Rust toolchain and dependencies updated
- Review webhook HMAC secrets and rotate periodically