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

AlgorithmLibraryUsage
Schnorr (secp256k1)secp256k1 0.29Transaction signing, DID authentication
X25519x25519-dalek 2Wire messaging key exchange (ECDH)
AES-256-GCMaes-gcm 0.10Wire message encryption, timelock encryption
ChaCha20-Poly1305chacha20poly1305 0.10Alternative authenticated encryption
SHA-256sha2 0.10Content hashing, Merkle trees, API key hashing, commitments
BLAKE2b-256blake2 0.10Kaspa transaction sighash
Argon2idargon2 0.5Password hashing (auth service)
PBKDF2-HMAC-SHA256pbkdf2 0.12Key derivation from passwords
HKDF-SHA256hkdf 0.12Key derivation from shared secrets
HMAC-SHA256hmac 0.12Webhook payload signing
Ed25519ed25519-dalek 2Alternative signature scheme
BIP-39bip39 2Mnemonic seed phrase generation (12/24 words)
BIP-44Custom (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:

  1. Transaction inputs/outputs are serialised
  2. BLAKE2b-256 is computed with key b"TransactionSigningHash" (NOT personalization)
  3. The 32-byte hash is signed directly with Keypair::sign_schnorr(msg)
  4. 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:

  1. Both parties generate X25519 keypairs via generate_x25519_key_pair()
  2. Public keys are exchanged via REST handshake endpoint
  3. Shared secret derived via derive_shared_secret(our_private, their_public)
  4. HKDF-SHA256 stretches the shared secret into AES-256-GCM keys
  5. Each message gets a unique 96-bit nonce via generate_nonce()
  6. 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: zeroize crate ensures key material is zeroed on drop

API Keys (Gateway)

  • Generation: kw_ prefix + 32 bytes of rand::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_prefix for human identification
  • Rotation: Old key is revoked, new key generated — no grace period

JWT Tokens

  • Algorithm: HS256 (HMAC-SHA256)
  • Signing key: KIIWORKS_JWT_SECRET environment 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

ThreatMitigation
Stolen JWTShort expiry (1h), HTTPS in production, dev_mode=false enforces JWT
Stolen API keySHA-256 hashed storage — breach of DB doesn’t expose raw keys. Rate limiting bounds damage. Immediate revocation via gateway.
Data tamperingBlockchain anchoring provides tamper evidence. SHA-256 hashes anchored on Kaspa BlockDAG are immutable.
MITM on Wire channelsX25519 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 forgeryEach passport state is hashed and anchored on-chain. Merkle batch anchoring provides individual inclusion proofs.
Replay attacksDID authentication uses time-limited challenge/response. Nonces are single-use.
DoSSliding-window rate limiting (per minute/hour/day) via gateway service. Axum’s backpressure handles connection floods.
SQL injectionSQLx with parameterised queries (no string interpolation). Most services use in-memory storage.
Secret leakagePrivate 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:

  1. DPP state → SHA-256 → 32 bytes
  2. Embed hash in P2PK script: [0x20][32-byte-hash][0xac] (sends to unspendable address derived from hash)
  3. Submit transaction to Kaspa
  4. Transaction ID serves as proof-of-existence at a specific time
  5. 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=json for 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