This guide covers the encryption features in Fortémi for securing data using public-key encryption.
Fortémi uses Public Key Encryption (PKE) for all encryption operations. This provides wallet-style encryption where users share public key addresses (similar to cryptocurrency wallets) instead of exchanging passphrases.
| Feature | Description |
|---|---|
| Format | MMPKE01 |
| Key Exchange | X25519 (Curve25519 ECDH) |
| Encryption | AES-256-GCM |
| Address Format | mm: prefix + Base58Check |
For detailed PKE documentation, see the PKE Encryption Guide.
The encryption system provides:
- Confidentiality - AES-256-GCM encryption (256-bit key)
- Integrity - AEAD authentication tag prevents tampering
- Key Protection - Private keys encrypted with Argon2id + AES-256-GCM
- Forward Secrecy - Ephemeral keypair per encryption operation
- Secure Memory - Keys zeroized immediately after use
- Multi-Recipient - Encrypt once for multiple recipients
| Component | Algorithm | Parameters |
|---|---|---|
| Key Exchange | X25519 | Curve25519 ECDH |
| KEK Derivation | HKDF-SHA256 | With domain separation |
| Symmetric Cipher | AES-256-GCM | 256-bit key, 96-bit nonce |
| Address Hash | BLAKE3 | 20-byte truncated + 4-byte checksum |
| Private Key Storage | Argon2id + AES-256-GCM | 64 MiB, 3 iterations |
| Random Generation | ChaCha20 | OS entropy source |
# Using CLI
matric-pke keygen -p "your-secure-passphrase-123" -o ~/.matric-keys
# Output:
# {
# "address": "mm:ULnxkCj4TCc8QnFsar8Be4DVV4TkWivXE",
# "private_key_path": "/home/user/.matric-keys/private.key.enc",
# "public_key_path": "/home/user/.matric-keys/public.key"
# }Share your public address (mm:...) with anyone who needs to send you encrypted data. Your address is safe to share publicly - it cannot be used to decrypt anything.
# Encrypt for one or more recipients
matric-pke encrypt \
-i document.pdf \
-o document.pdf.mmpke \
-r /path/to/alice.pub \
-r /path/to/bob.pub# Decrypt using your private key
matric-pke decrypt \
-i document.pdf.mmpke \
-o document.pdf \
-k ~/.matric-keys/private.key.enc \
-p "your-secure-passphrase-123"┌──────────────────────────────────────┐
│ Magic: "MMPKE01\n" │ 8 bytes
├──────────────────────────────────────┤
│ Header Length │ 4 bytes (little-endian u32)
├──────────────────────────────────────┤
│ Header (JSON) │ Variable
│ { │
│ "version": 1, │
│ "ephemeral_pubkey": "<base64>", │
│ "recipients": [ │
│ { │
│ "address": "mm:...", │
│ "encrypted_dek": "<base64>", │
│ "dek_nonce": "<base64>" │
│ }, │
│ ... │
│ ], │
│ "data_nonce": "<base64>", │
│ "created_at": "2026-01-22T...", │
│ "original_filename": "doc.pdf" │
│ } │
├──────────────────────────────────────┤
│ Encrypted Data │ Variable
│ (ciphertext + 16-byte auth tag) │
└──────────────────────────────────────┘
Generate a new X25519 keypair.
pke_generate_keypair({
passphrase: "secure-passphrase-123",
output_dir: "/path/to/keys",
label: "My Key" // optional
})Encrypt data for recipients.
pke_encrypt({
input: "/path/to/file.pdf",
output: "/path/to/file.pdf.mmpke",
recipients: ["/path/to/alice.pub", "/path/to/bob.pub"]
})Decrypt data with your private key.
pke_decrypt({
input: "/path/to/file.pdf.mmpke",
output: "/path/to/file.pdf",
private_key: "/path/to/private.key.enc",
passphrase: "your-passphrase"
})List recipient addresses without decrypting.
pke_list_recipients({
input: "/path/to/file.mmpke"
})
// Returns: { recipients: ["mm:abc...", "mm:xyz..."], count: 2 }Get the address for a public key.
pke_get_address({
public_key: "/path/to/public.key"
})
// Returns: { address: "mm:..." }Verify an address checksum.
pke_verify_address({
address: "mm:ULnxkCj4TCc8QnFsar8Be4DVV4TkWivXE"
})
// Returns: { valid: true, version: 1 }- Protect your private key - Use a strong passphrase (20+ characters)
- Back up your keys - Store copies in secure, separate locations
- Share only addresses - Never share private keys or passphrases
- Use strong passphrases - At least 12 characters (20+ recommended)
- Use a password manager - Store passphrases securely
- Don't reuse passphrases - Use unique passphrases for each key
- Verify addresses - Confirm addresses through a trusted channel
- Check recipients before encrypting - Use
pke_list_recipientsto verify - Limit recipients - Maximum 100 recipients per file
- Test decryption - Verify you can decrypt before deleting originals
- Multiple backups - Follow the 3-2-1 backup rule
- Rotate keys periodically - Generate new keypairs for long-term security
Passphrases must be at least 12 characters. Use a longer, more secure passphrase.
- Your address is not in the recipient list
- Use
pke_list_recipientsto see valid recipient addresses - Ask the sender to re-encrypt with your public key
- Wrong private key
- File was corrupted during transfer
- Check that your key matches one of the recipients
The file is not in MMPKE01 format. It may be:
- A different encryption format
- Not encrypted at all
- Corrupted
Argon2id uses 64 MiB of memory by default for private key encryption. This is intentional to resist GPU/ASIC attacks.
- File contents (confidentiality)
- File integrity (tampering detection)
- Key material in memory (zeroized after use)
- Forward secrecy (ephemeral keys per encryption)
- File existence (encrypted files are visible)
- File size (length is observable)
- Recipient addresses (visible in header without decryption)
- Encryption metadata (algorithm, timestamp visible in header)
The encryption protects against:
- Unauthorized access to files
- Data theft from storage devices
- Cloud storage provider access
- Passive network attackers
It does NOT protect against:
- Compromised endpoints (malware on your system)
- Private key compromise
- Coerced disclosure (legal/physical threats)
- PKE Encryption Guide - Detailed PKE documentation
- Shard Exchange Primer - Practical workflows for sharing and recovering knowledge
- Backup Guide - Backup and restore procedures
- Architecture - System design overview