| Actor | Capability | Mitigation |
|---|---|---|
| Offline attacker with disk access | Reads vault.enc from stolen laptop or cloud sync |
AES-256-GCM ciphertext; useless without keychain master key |
| Accidental git commit | vault.enc pushed to repo |
Opaque ciphertext; no plaintext PII |
| Cloud sync (iCloud, Dropbox) | vault.enc synced to cloud |
Ciphertext only; redactron init warns on cloud-sync paths |
| Physical access to unlocked machine | Reads vault without user present | Touch ID required via LocalAuthentication before every vault access |
| Live malware on user's machine | Reads keychain directly (bypassing redactron) | Soft enforcement only. See limitations below. |
| Kernel-level compromise | Bypasses all user-space controls | Out of scope |
- Why GCM, not CBC: GCM is authenticated encryption (AEAD). Any bit flip in the ciphertext raises
InvalidTagbefore any plaintext is returned. CBC has no authentication tag; an attacker can flip bits and receive modified plaintext silently. - Why not ChaCha20-Poly1305: Deferred to v1.1 for cross-platform support. AES-GCM has hardware acceleration on all modern x86 and Apple Silicon.
- Key size: 256 bits (
secrets.token_bytes(32)). NIST-approved for top-secret classification. - Nonce: 96 bits (
os.urandom(12)) generated fresh for everysave()call. GCM nonce reuse with the same key is catastrophic; fresh random nonces make reuse statistically impossible.
- Master key: 32 bytes, generated once via
secrets.token_bytes(32). - Stored in the macOS login keychain via the
keyringlibrary. - Touch ID required before every vault access via
LAContext.evaluatePolicy(LocalAuthentication framework). - Never written to disk in plaintext. Never logged. Never in error messages.
redactron uses LAContext.evaluatePolicy(LAPolicyDeviceOwnerAuthenticationWithBiometrics) from the LocalAuthentication framework before every vault access. This is soft enforcement:
- The Touch ID prompt fires; if cancelled or failed, redactron refuses to proceed.
- The master key is stored in the login keychain (standard ACL, not
kSecAttrAccessControl). - This works for unsigned Python processes. No Apple Developer account or code signing required.
- A determined attacker with shell access to your unlocked machine could read the keychain directly, bypassing redactron's Touch ID gate.
Why not kSecAttrAccessControl (hardware-backed ACL)?
SecItemAdd with kSecAttrAccessControl requires the keychain-access-groups entitlement, which is only available to code-signed apps distributed through Apple's channels. An unsigned pip install package cannot use it. The LocalAuthentication soft-gate provides equivalent UX (Touch ID prompt on every access) with the same threat model as tools like sudo-touchid.
~/.redactron/vault.enc:
Offset Size Field
0 8 magic: b"REDV1\x00\x00\x00"
8 12 nonce (random per encryption)
20 16 AES-256-GCM authentication tag
36 N ciphertext (JSON payload)
The JSON payload is decrypted in-memory only and never written to disk.
- Offline attacker with disk access (stolen laptop, cloud sync leak, accidental git commit)
- Physical access to unlocked machine (Touch ID required before vault access)
vault.encis opaque ciphertext without the keychain master key
- Live malware with direct keychain access (bypasses redactron's Touch ID gate)
- Kernel-level compromise
- Side-channel attacks (timing, cache)
- A user who reads their own keychain directly
| Platform | Status |
|---|---|
| macOS (darwin) | Supported in v1. Touch ID via LocalAuthentication. |
| Linux | Planned for v1.1 (libsecret/SecretService) |
| Windows | Planned for v1.1 (DPAPI/Credential Manager) |
Email: tejinder.singh@ieee.org
Please include:
- Description of the vulnerability
- Steps to reproduce
- Affected version(s)
- Potential impact
Disclosure policy: 90-day coordinated disclosure. We will acknowledge receipt within 48 hours and provide a fix timeline within 7 days. Public disclosure after 90 days or when a fix is released, whichever comes first.
PGP key available on request.