CONFIDENTIALITY: LEVEL 0 (PUBLIC)
TECHNICAL SPECIFICATIONS
Security through obscurity is not security.
We rely on mathematics, open standards, and hardware isolation.
| Component | Implementation | Specification |
|---|---|---|
| Cipher | ChaCha20-Poly1305 (IETF RFC 8439) | 256-bit Key, AEAD |
| Key Storage | iOS Secure Enclave (Keychain API) | Hardware Backed, On-Device Only |
| PIN Hashing | Argon2id (libsodium) | Memory-Hard KDF, Interactive Params |
| Secure Deletion | NIST 800-88 Rev. 1 | Single-Pass Random Overwrite (Pro) |
| Backup Encryption | AES-256-GCM (Argon2id derived key) | Password-Derived Key, Moderate Params |
| Network Access | NULL (Air-Gapped) | Zero Network Permissions |
| Cloud Sync | NULL (Disabled) | iCloud Backup Excluded |
| Analytics | NULL (No Telemetry) | Zero Data Collection |
import CryptoKit
// Encrypt data with AEAD
func encrypt(data: Data, key: SymmetricKey) throws -> Data {
// Generates random nonce internally
let sealedBox = try ChaChaPoly.seal(
data,
using: key
)
// Returns: Nonce + Ciphertext + Tag
return sealedBox.combined
}
// 256-bit key never leaves Secure Enclave
let key = SymmetricKey(size: .bits256)
// Dead Man's Switch: Irreversible Wipe
func nukeFileSystem() {
let secureWipe = UserDefaults
.standard
.bool(forKey: "secureWipeEnabled")
if secureWipe {
// NIST 800-88 (single-pass random)
secureWipeDirectory(vaultURL)
} else {
// Fast wipe (instant)
FileManager.removeItem(vaultURL)
}
// Destroy keys (cryptographic erasure)
Keychain.delete("vaultKey")
}
Nu11VLT uses Argon2id via libsodium for all password and PIN hashing. Argon2id is the winner of the Password Hashing Competition (2015) and is specifically designed to resist GPU/ASIC cracking attacks.
Why Argon2id?
- Memory-hard algorithm (forces attackers to use expensive RAM)
- Resistant to GPU/ASIC/FPGA cracking farms
- Recommended by OWASP, Signal, 1Password, Bitwarden
- Configurable parameters for different use cases
// PIN hashing with interactive parameters (fast)
func hashPIN(_ pin: String) -> String? {
guard let pinData = pin.data(using: .utf8) else { return nil }
let sodium = Sodium()
return sodium.pwHash.str(
passwd: Bytes(pinData),
opsLimit: sodium.pwHash.OpsLimitInteractive,
memLimit: sodium.pwHash.MemLimitInteractive
)
}
// Archive password hashing with moderate parameters (stronger)
func deriveArchiveKey(password: String, salt: Data) -> SymmetricKey? {
guard let passwordData = password.data(using: .utf8) else { return nil }
let sodium = Sodium()
guard let keyBytes = sodium.pwHash.hash(
outputLength: 32,
passwd: Bytes(passwordData),
salt: Bytes(salt),
opsLimit: sodium.pwHash.OpsLimitModerate,
memLimit: sodium.pwHash.MemLimitModerate,
alg: .argon2id13
) else { return nil }
return SymmetricKey(data: Data(keyBytes))
}
Argon2id stores the salt, algorithm, and parameters in a PHC string format. No separate salt storage needed for PINs.
Nu11VLT implements NIST 800-88 Rev. 1 "Clear/Purge" standard for secure file deletion on modern SSDs. This Pro feature performs a single-pass random data overwrite before deletion.
Why Single-Pass?
Modern SSDs use wear-leveling and internal remapping. Multi-pass overwrites (like DoD 5220.22-M) were designed for magnetic hard drives and provide no additional security on flash storage. NIST 800-88 Rev. 1 recommends:
- Clear: Logical overwrite (single pass) - Sufficient for SSDs
- Purge: Cryptographic erasure (key destruction) - Our primary method
- Destroy: Physical destruction - Not applicable to apps
// NIST 800-88 Single-Pass Random Overwrite
func secureWipeFile(at url: URL) {
guard FileManager.default.fileExists(atPath: url.path) else { return }
do {
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
guard let fileSize = attributes[.size] as? UInt64, fileSize > 0 else {
try FileManager.default.removeItem(at: url)
return
}
let fileHandle = try FileHandle(forWritingTo: url)
defer { try? fileHandle.close() }
// Single-pass cryptographically secure random overwrite
var randomData = Data(count: Int(fileSize))
_ = randomData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, Int(fileSize), $0.baseAddress!)
}
try fileHandle.seek(toOffset: 0)
fileHandle.write(randomData)
fileHandle.synchronizeFile()
// Finally delete the file
try FileManager.default.removeItem(at: url)
} catch {
// Fallback to standard deletion
try? FileManager.default.removeItem(at: url)
}
}
Optional Pro feature. Slower and more battery-intensive than standard deletion. Primary security comes from cryptographic erasure (key destruction).
THREAT MODEL ASSESSMENT
// PROTECTED AGAINST
Data encrypted at rest with ChaCha20-Poly1305. Mathematically unreadable without Face ID/PIN. Keys stored in Secure Enclave (hardware isolated).
Backup PIN loads alternative vault with innocent photos. Zero visual indicators. Indistinguishable from primary vault.
Impossible. App has zero network code. No cloud sync. No servers. Data never transmitted.
Primary defense: Cryptographic erasure (key destruction). Optional: NIST 800-88 single-pass random overwrite for additional protection against forensic tools.
Skeleton Switch (dead man's switch) auto-wipes vault if device not unlocked within 7/14/30/60 days. Optional NIST 800-88 secure wipe.
Argon2id memory-hard hashing resists GPU/ASIC farms. Even weak PINs have significant protection against brute force.
// NOT PROTECTED AGAINST
If device is snatched while vault is open, decrypted data is visible in RAM. Mitigate by using Face ID authentication on app launch.
If iOS itself has kernel-level exploit, memory hooks could theoretically read decryption keys. Keep iOS updated.
Jailbreaking compromises iOS security model. Keychain access can be bypassed. Do not use Nu11VLT on jailbroken devices.
Encrypted backup files (.nu11vlt) are protected by Argon2id, but still only as strong as the user's password. Use 16+ character passwords with high entropy.
Nation-state actors with iOS zero-day exploits may bypass security. No app can protect against unknown vulnerabilities in the OS.
Nu11VLT stores encryption keys in iOS Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly attribute. This ensures keys are:
- Hardware-backed (stored in Secure Enclave, not software)
- Only accessible when device is unlocked
- Never backed up to iCloud
- Never transferred to other devices
- Destroyed if device is reset or app is deleted
// Key storage with maximum security
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: "vaultKey",
kSecValueData: keyData,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
Dive Deeper
Explore detailed documentation on GitHub including architecture diagrams, threat model analysis, and implementation guides.
GitHub Repository