LOCK YOUR
PRIVATE PHOTOS
FOR GOOD.

An encrypted photo vault that lives entirely on your iPhone. No cloud. No servers. No way in except by you.

ChaCha20-Poly1305 encryption with 256-bit keys, protected by FaceID and stored in iOS Keychain.

iOS 18+ · ChaCha20-Poly1305 · FaceID

Layer 1

Secure Enclave

Hardware-level key storage, never exposed to software

Layer 2

Biometric Auth

FaceID gating — no biometric match, no access

Layer 3

ChaCha20-Poly1305 Vault

256-bit encryption. Same standard as Signal and modern VPNs

Keys

256-bit

Cloud

None

Storage

Device

Encryption Pipeline

How Vault Works

┌────────────────────────────────────────────────────────────┐ │ VAULT ENCRYPTION FLOW │ └────────────────────────────────────────────────────────────┘ SENSITIVE PHOTO │ ├─> [User: Move/Copy to Vault] │ ▼ BIOMETRIC AUTH │ ├─> FaceID / Touch ID / Passcode │ ▼ RETRIEVE KEY │ ├─> iOS Keychain (Secure Enclave) │ └─ Device-bound 256-bit key │ ▼ CHACHA20-POLY1305 │ ├─> AEAD Cipher │ ├─ Confidentiality │ ├─ Authenticity │ └─ Integrity │ ▼ ENCRYPTED FILE (.n11) │ ├─> App Sandbox │ ├─ .completeFileProtection │ └─ Excluded from backups │ ▼ ORIGINAL DELETED (optional)

Source Code

Encryption Implementation

> /Vault/VaultCrypto.swift ChaCha20-Poly1305 AEAD
import CryptoKit

final class VaultCrypto {

 /// Encrypts photo data with ChaCha20-Poly1305 AEAD
 static func encryptData(_ data: Data, key: SymmetricKey) throws -> Data {
  // ChaCha20-Poly1305 provides:
  // - Confidentiality (encryption)
  // - Authenticity (HMAC)
  // - Integrity (tampering detection)
  let sealedBox = try ChaChaPoly.seal(data, using: key)

  return sealedBox.combined // Nonce + Ciphertext + Tag
 }

 /// Decrypts vault photo
 static func decryptData(_ encryptedData: Data, key: SymmetricKey) throws -> Data {
  let sealedBox = try ChaChaPoly.SealedBox(combined: encryptedData)

  // Authentication tag verified automatically
  // Throws error if data has been tampered with
  let decryptedData = try ChaChaPoly.open(sealedBox, using: key)

  return decryptedData
 }

 /// Generates vault encryption key with entropy validation
 static func generateVaultKey() throws -> SymmetricKey {
  let key = SymmetricKey(size: .bits256)

  // Validate entropy meets NIST SP 800-90B standards
  let keyData = key.withUnsafeBytes { Data($0) }
  let entropy = calculateEntropy(keyData)

  guard entropy >= 7.5 else {
   throw CryptoError.insufficientEntropy
  }

  return key
 }
}

Key Features

AEAD Cipher

ChaCha20-Poly1305 provides authenticated encryption. Same standard used in WireGuard, TLS 1.3, and Signal Protocol.

Biometric Lock

Vault requires FaceID/Touch ID every time you open it. Even with unlocked phone, vault stays locked.

Security Model

What Vault Protects Against

PROTECTED

  • Device seizure (encrypted at rest)
  • Unauthorized physical access
  • Cloud breaches (no cloud storage)
  • Backup extraction (keys device-bound)
  • Network interception (no uploads)

OUT OF SCOPE

  • Forced biometric unlock (physical coercion)
  • Jailbroken devices (iOS security compromised)
  • OS zero-day exploits (affects all apps)
  • Device loss (keys cannot be recovered)

CRITICAL: Device-Locked Security

**Vault data cannot be recovered if you lose your device, switch phones, or reinstall NuDefndr.** This is intentional—it's the only way to guarantee true security.

Encryption keys are hardware-bound to your device's Secure Enclave. Without the physical device, vault contents are permanently inaccessible—even to us.

Learn More

Want to see the full architecture?

View Technical Specs