πŸ—οΈ

SECURITY ARCHITECTURE

Technical deep dive into ArmoryHub's security implementation.
Architecture decisions, cryptographic choices, and UX tradeoffs explained.

ARCHITECTURE OVERVIEW

SecurityCoordinator (Facade Pattern)
β”œβ”€β”€ BiometricAuthManager
β”‚   β”œβ”€β”€ LAContext for Face ID/Touch ID
β”‚   β”œβ”€β”€ Lock timeout management
β”‚   └── isAuthenticated state
β”‚
β”œβ”€β”€ PINManager
β”‚   β”œβ”€β”€ PIN validation (PBKDF2-SHA256, 310K iterations)
β”‚   β”œβ”€β”€ Master key storage (encrypted in Keychain)
β”‚   β”œβ”€β”€ Lockout protection (5 attempts = 30s timeout)
β”‚   └── currentSessionMasterKey (in-memory only)
β”‚
β”œβ”€β”€ EncryptionManager
β”‚   β”œβ”€β”€ EncryptionStateManager (race prevention)
β”‚   β”œβ”€β”€ Field-level encryption (AES-256-GCM)
β”‚   β”œβ”€β”€ Batch encrypt/decrypt operations
β”‚   └── iCloud encryption flag management
β”‚
└── KeyTransferManager
    β”œβ”€β”€ QR code generation (temporary vs backup)
    β”œβ”€β”€ QR code validation (expiry, one-time use)
    β”œβ”€β”€ Rate limiting (5 attempts per QR)
    └── Import/export key data

Supporting Components:
β”œβ”€β”€ KeychainWrapper (kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
β”œβ”€β”€ PBKDF2 (CommonCrypto wrapper)
β”œβ”€β”€ EncryptionState (state machine enum)
└── SecurityTypes (PINLength, LockTimeout, AuthenticationError)

KEY DESIGN DECISIONS

1. Master Key Generation: On Encryption Enable (Not PIN Setup)

Context

Originally, the master key was generated when users set up their PINβ€”even if they never enabled encryption. This meant PIN-only users had unnecessary cryptographic material in their Keychain.

Decision

We moved master key generation to happen ONLY when encryption is first enabled. PIN setup now just creates a PIN hash for authentication.

βœ“ Benefits

  • +Reduced attack surface: PIN-only users have NO master key to compromise
  • +Clearer separation: PIN = authentication, Encryption = data protection
  • +Follows principle of least privilege (don't create sensitive data until needed)
  • +Better UX: Users understand that 'Enable Encryption' generates the key

⚠ Tradeoffs

  • -Slightly more complex code (conditional key generation)
  • -Backward compatibility needed for users with old architecture

2. Data at Rest vs. Data in Use: Plaintext While Unlocked

Context

We could decrypt data only in memory (never write plaintext to disk), but this would severely impact performance and UX. Core Data expects persistent storage.

Decision

Data is decrypted to plaintext in Core Data while app is unlocked, then re-encrypted when backgrounded. This is the same approach used by Signal, 1Password, and most secure apps.

βœ“ Benefits

  • +Fast app performance (no on-demand decryption delays)
  • +Standard Core Data integration (no custom persistence layer needed)
  • +Better UX: instant data access, smooth scrolling
  • +Auto-lock timeout provides safety net (forces re-encryption)

⚠ Tradeoffs

  • -If device is seized while app is unlocked, data is exposed
  • -File system snapshots during unlocked state capture plaintext
  • -Mitigation: Auto-lock forces periodic encryption, 'Lock Now' button available

3. State Machine for Race Condition Prevention

Context

Background/foreground transitions are asynchronous. Without coordination, the app could start decrypting data while encryption is still in progress, causing data corruption.

Decision

Implemented EncryptionStateManager with atomic state transitions (.decrypted, .encrypting, .encrypted, .decrypting). Only one transition allowed at a time, enforced by NSLock.

βœ“ Benefits

  • +Prevents race conditions (can't encrypt + decrypt simultaneously)
  • +Atomic operations (all-or-nothing with rollback on failure)
  • +Crash recovery (app checks disk state on launch)
  • +UI blocking during transitions (user can't trigger conflicting operations)

⚠ Tradeoffs

  • -Added complexity (~150 lines of state machine code)
  • -Slight delay if user rapidly backgrounds/foregrounds (waits for completion)
  • -Mitigation: Wait timeout is only 5 seconds, users rarely notice

4. 'Never' Timeout Disabled When Encryption Enabled

Context

With encryption on, the master key stays in memory while app is unlocked. The 'Never' timeout would keep data decrypted and key in memory indefinitelyβ€”a security risk.

Decision

Automatically hide 'Never' option in auto-lock picker when encryption is enabled. Programmatic attempts to set 'Never' are silently changed to 'Immediately'.

βœ“ Benefits

  • +Forces periodic master key clearing from memory
  • +Reduces window of exposure for memory dump attacks
  • +Ensures data re-encrypts regularly

⚠ Tradeoffs

  • -Less convenience for users who want unlimited unlocked time
  • -Mitigation: 5 minute option available (reasonable compromise)

5. PBKDF2-SHA256 with 310,000 Iterations

Context

Key derivation function choice impacts both security (brute-force resistance) and UX (unlock speed). We follow OWASP 2023 recommendations.

Decision

PBKDF2-HMAC-SHA256 with 310,000 iterations. Takes ~100ms on iPhone 15 Pro, ~200ms on iPhone 12. Bcrypt and Argon2 considered but require third-party dependencies.

βœ“ Benefits

  • +Meets OWASP 2023 minimum recommendation for PBKDF2-SHA256
  • +Native CommonCrypto implementation (no dependencies)
  • +Well-tested, FIPS-approved algorithm
  • +~100ms unlock time is imperceptible to users

⚠ Tradeoffs

  • -Argon2id would be more resistant to GPU attacks (but requires library)
  • -Scrypt would be more memory-hard (but also requires library)
  • -310K iterations is minimumβ€”600K would be more secure but slower

6. Field-Level Encryption vs. Full Database Encryption

Context

We could encrypt the entire SQLite database file with native Core Data encryption, but this is all-or-nothing (either everything is encrypted or nothing is).

Decision

Field-level encryption using AES-256-GCM. Each string/binary field encrypted individually. Dates and numeric values remain plaintext due to Core Data type constraints.

βœ“ Benefits

  • +Granular control (encrypt only sensitive fields)
  • +Backward compatible (can toggle encryption on/off without database migration)
  • +Metadata remains searchable (dates, counts) for UI features
  • +Easier debugging (can see structure even when data is encrypted)

⚠ Tradeoffs

  • -More complex than full-database encryption
  • -Dates/numbers not encrypted (but these aren't sensitiveβ€”serial numbers ARE encrypted)
  • -Each field has AES-GCM overhead (~28 bytes minimum)

CRYPTOGRAPHIC CHOICES

πŸ”

AES-256-GCM

Field Encryption

Why:

Authenticated encryption with automatic nonce generation via Apple's CryptoKit. GCM mode provides both confidentiality and authenticity (prevents tampering). Approved by NSA for TOP SECRET data.

Alternatives Considered:

ChaCha20-Poly1305 (similar security, better on devices without AES hardware), AES-CBC (no authentication)

Why We Chose AES-256-GCM:

Native CryptoKit support, hardware acceleration on all Apple devices, authenticated encryption, battle-tested

πŸ”

PBKDF2-HMAC-SHA256

PIN-to-Key Derivation

Why:

Stretches 6-digit PIN (20 bits entropy) into 256-bit key (makes brute force computationally infeasible). 310,000 iterations takes ~100ms, making each PIN attempt expensive.

Alternatives Considered:

Argon2id (more resistant to GPU/ASIC attacks), bcrypt (memory-hard), scrypt (memory-hard)

Why We Chose PBKDF2-SHA256:

Native CommonCrypto support (no dependencies), FIPS-approved, OWASP 2023 compliant, fast enough for good UX

πŸ”

SecRandomCopyBytes

Salt Generation

Why:

Cryptographically secure random number generation backed by /dev/random. Generates unique 32-byte salts for each PIN and master key.

Alternatives Considered:

CryptoKit.SymmetricKey (similar), Swift's random APIs (NOT cryptographically secure)

Why We Chose SecRandomCopyBytes:

Industry standard for iOS, guaranteed entropy, hardware-backed on modern devices

πŸ”

iOS Keychain

Sensitive Data Storage

Why:

Hardware-backed secure storage with Secure Enclave protection. Data encrypted with device-unique keys. kSecAttrAccessibleWhenUnlockedThisDeviceOnly prevents iCloud Keychain sync.

Alternatives Considered:

UserDefaults (INSECURE), File encryption (complex), CloudKit (keys would syncβ€”bad)

Why We Chose iOS Keychain:

Purpose-built for this, hardware protection, well-documented, industry standard

UX TRADEOFFS EXPLAINED

Separate PIN and Encryption Features

UX Impact

Users must understand two concepts: PIN (locks app) vs Encryption (protects data)

Reasoning

Gives users flexibility to choose security level. Free users get PIN for app lock. Pro users can add encryption for data protection. Many users want app lock without the complexity/risk of encryption.

Alternative Considered

Force encryption when PIN is enabled (simpler, but removes choice)

Chosen Approach

Separate features with clear UI explanations

Result

More initial complexity, but users appreciate the choice and flexibility

Plaintext in Database While App Unlocked

UX Impact

No performance impactβ€”app is fast and responsive

Reasoning

Decrypting on-demand would add latency to every data access. Users expect instant scrolling and search. Banking apps (Chase, BofA) use same approach.

Alternative Considered

In-memory-only decryption (slower, more secure)

Chosen Approach

Plaintext while unlocked, encrypted when backgrounded

Result

Fast app with forced encryption on background (best of both worlds)

1-2 Second Delay on Unlock (Decryption)

UX Impact

Users wait ~1-2 seconds after PIN entry for large inventories

Reasoning

Decrypting 1000s of text fields takes time. We show 'Unlocking Your Data...' blocker during this time so users understand what's happening.

Alternative Considered

Lazy decryption (decrypt each field on accessβ€”even slower)

Chosen Approach

Batch decrypt on unlock with progress indicator

Result

Slight delay on unlock, but instant access once decrypted

6-Digit PIN Only (No Longer PIN Lengths)

UX Impact

Users cannot choose 4-digit or 8-digit PINs

Reasoning

4 digits = 10,000 combinations (too weak). 6 digits = 1 million combinations (industry standard). 8+ digits = annoying to enter. We standardized on 6 for security/UX balance.

Alternative Considered

Configurable length (4-12 digits)

Chosen Approach

Fixed 6-digit PIN

Result

Simpler UX, industry-standard security, muscle memory consistency

QR Code Key Transfer (Not iCloud Keychain Sync)

UX Impact

Users must manually scan QR codes between devices (less convenient)

Reasoning

Syncing encryption keys via iCloud Keychain would be easier BUT: (1) Keys would exist in Apple's servers (zero-knowledge broken), (2) Government could subpoena iCloud backups and get keys, (3) iCloud breach would expose keys. QR transfer keeps keys offline.

Alternative Considered

iCloud Keychain sync for encryption keys (more convenient, less secure)

Chosen Approach

Offline QR code transfer only

Result

True zero-knowledge architecture, more setup friction, better security

THREAT MODEL

What we protect against and what we don't. Understanding the threat model helps you choose the right security level.

Device Theft (App Locked)

MAXIMUMβœ…

With PIN or biometric enabled, thief cannot open app. With encryption enabled, even if they bypass app lock (jailbreak), data is encrypted garbage without your PIN.

Device Seizure by Authorities

HIGH (cryptographic), LOW (legal compulsion)βœ…

With encryption enabled, AES-256-GCM encrypted data cannot be decrypted without your PIN. Forensic tools cannot break modern encryption. You may be legally compelled to provide PIN (5th Amendment protections vary by jurisdiction).

iCloud Breach (Encryption Enabled)

MAXIMUMβœ…

Your encryption keys are stored in device Keychain onlyβ€”never synced to iCloud. Even if Apple's iCloud is breached, attackers get encrypted blobs without keys. Data is useless.

Memory Dump While App Unlocked

MEDIUM (time-limited exposure)❌

While app is unlocked, master key is in memory and data is decrypted in database. Debugger attachment or memory dump would expose both. Auto-lock timeout provides mitigation by forcing periodic encryption.

Backup Extraction (Encrypted Backups)

MAXIMUMβœ…

iOS encrypted backups (and iCloud backups if encryption enabled) contain AES-256-GCM encrypted data. Without your PIN, backups are useless. Keys are in Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly (survives encrypted backup restore).

Social Engineering / Phishing

DEPENDS ON USER❌

If attacker tricks you into giving them your PIN, they can decrypt your data. We cannot prevent user error. Education (in-app warnings) is our only defense.

Compromised Backup QR Code + PIN

MEDIUM (user can revoke by changing PIN)❌

Backup QR codes contain your encrypted master key. If attacker gets both the QR AND your PIN, they can decrypt all data. Solution: Change PIN if QR is compromised (regenerates key encryption).

Jailbroken Device

LOW❌

Jailbreak gives root access, potentially allowing Keychain extraction. Secure Enclave still provides hardware protection, but sophisticated attackers may succeed. Recommendation: Don't use encryption on jailbroken devices.

IMPLEMENTATION DETAILS

PINManager

PIN validation, master key management, lockout protection

Key Methods

  • β€’ setupPIN(): Generates PIN hash + salt, stores in Keychain (NO key generation)
  • β€’ validatePINAndUnlock(): Validates PIN, loads master key if exists
  • β€’ saveMasterKeyToKeychain(): Encrypts master key with PIN-derived key
  • β€’ changePIN(): Re-encrypts master key with new PIN (invalidates old QRs)

Security Features

  • βœ“ PBKDF2-SHA256 with 310,000 iterations
  • βœ“ 32-byte cryptographically secure random salts (SecRandomCopyBytes)
  • βœ“ 5-attempt lockout with 30-second timeout
  • βœ“ Weak PIN detection (rejects 123456, 111111, sequential, repeating)

EncryptionManager

Data encryption/decryption, state management, batch operations

Key Methods

  • β€’ enableEncryption(): Generates master key, saves to Keychain, encrypts all data
  • β€’ encryptDataOnLock(): Encrypts all fields when app backgrounds
  • β€’ decryptDataInPlace(): Decrypts all fields after authentication
  • β€’ encryptField() / decryptField(): Per-field encryption/decryption

Security Features

  • βœ“ AES-256-GCM for all field encryption
  • βœ“ State machine prevents race conditions
  • βœ“ Atomic batch operations with rollback on failure
  • βœ“ Automatic nonce generation (CryptoKit handles this)
  • βœ“ Master key cleared from memory after encryption

KeyTransferManager

QR code generation/import, multi-device key synchronization

Key Methods

  • β€’ generateKeyExportData(): Creates temporary QR (5 min expiry, one-time use)
  • β€’ generateBackupKeyExportData(): Creates backup QR (non-expiring, reusable)
  • β€’ importKeyFromQRData(): Validates and imports key from QR
  • β€’ Rate limiting: 5 failed import attempts per QR = lockout

Security Features

  • βœ“ Temporary QR codes expire after 5 minutes
  • βœ“ One-time use tracking (exportID prevents replay attacks)
  • βœ“ Backup QR codes revoked by PIN change
  • βœ“ Import attempt limiting (5 attempts = QR locked)

HONEST LIMITATIONS

We believe in transparency. Here are the limitations of our security implementation:

6-Digit PIN Has Limited Entropy

6 digits = 1 million combinations = ~20 bits of entropy. This is industry-standard but not high-entropy. PBKDF2's 310,000 iterations slow down brute force, but a determined attacker with GPU cluster could try millions of PINs offline (if they steal backup QR code).

Mitigation:

Use random 6 digits (not birth dates). Generate backup QR and store securely. Consider changing PIN periodically.

Plaintext Exposure While App Is Unlocked

Data is decrypted to SQLite database while app is unlocked. Memory dump or debugger attachment during this window would expose plaintext. File system snapshots capture plaintext.

Mitigation:

Auto-lock with short timeout (1-5 minutes). 'Lock Now' button for instant encryption. Don't leave app unlocked unattended.

No Hardware-Backed Master Key Storage

Master key is stored encrypted in Keychain, but not in Secure Enclave. Secure Enclave can only store 256-bit keys for signing, not general-purpose encryption keys. We'd need to re-architect to use Secure Enclave for key wrapping.

Mitigation:

Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly is still hardware-protected on modern devices. Future enhancement: Secure Enclave key wrapping.

Dates and Numeric Values Not Encrypted

Due to Core Data type constraints, dates (purchase dates, birth dates) and numeric values (prices, quantities) are stored in plaintext. Encrypting these would require converting to string types, breaking Core Data queries and sorting.

Mitigation:

Sensitive data (serial numbers, names, locations, notes) IS encrypted. Dates/numbers are less sensitive. Alternative: Use full-database encryption (different tradeoffs).

No Server-Side Key Escrow

If you lose all devices and don't have a backup QR, your data is gone forever. True zero-knowledge architecture means we can't help you recover. This is by design, but it's also a support burden.

Mitigation:

Strong user education, mandatory backup QR recommendations, testing recovery flow before you need it.

FUTURE ENHANCEMENTS

Secure Enclave Key Wrapping

HIGH

Use Secure Enclave to wrap the master encryption key (iPhone XS+ with A12 chip or later). Prevents Keychain extraction even on jailbroken devices.

Timeline: 6-12 months

Post-Quantum Cryptography

MEDIUM

Add support for quantum-resistant algorithms (Kyber, Dilithium) alongside current AES-256. Future-proofs against quantum computing attacks.

Timeline: 12-24 months

Hardware Security Key Support

MEDIUM

Allow YubiKey or other FIDO2 hardware keys for authentication. Physical key required for unlock (something you have + something you know).

Timeline: 12-18 months

Optional Cloud Key Escrow

LOW

Opt-in server-side key backup with account recovery (email verification, security questions). Would break zero-knowledge architecture but give recovery option.

Timeline: Not planned (conflicts with zero-knowledge promise)

Biometric Re-Authentication for Sensitive Ops

MEDIUM

Require Face ID/Touch ID again for: export data, disable encryption, remove PIN, generate QR codes. Prevents unauthorized actions if device is unlocked and left unattended.

Timeline: 3-6 months

Security Audit Logging

HIGH

Write-only log of security events (PIN changes, encryption toggles, QR generations) using os_log. Helps forensic analysis if breach occurs.

Timeline: 3-6 months

FOR SECURITY RESEARCHERS

Note: ArmoryHub is a single-developer project built by an independent developer passionate about firearms and security. While we strive for professional-grade security implementation, we don't have the resources of large security teams. We welcome community input and security research to help us improve.

We believe in security through transparency. If you're a security researcher interested in auditing ArmoryHub:

  • β€’Source Code: Available upon request for security audit purposes. Contact support@armoryhub.app with credentials.
  • β€’Cryptographic Stack: Apple CryptoKit (AES-256-GCM), CommonCrypto (PBKDF2), iOS Keychain, SecRandomCopyBytes. All native iOS APIsβ€”no custom crypto.
  • β€’What We Don't Do: No custom crypto implementations, no rolling our own algorithms, no encryption backdoors, no server-side key storage.
  • β€’Report Security Issues: support@armoryhub.app . We aim to respond to critical vulnerabilities within 48-72 hours (single developer, best effort).

TECHNICAL SPECIFICATIONS

ComponentSpecificationStandard
Encryption AlgorithmAES-256-GCMNIST FIPS 197, NSA Suite B
Key Derivation FunctionPBKDF2-HMAC-SHA256NIST SP 800-132
KDF Iterations310,000OWASP 2023 Minimum
Master Key Size256 bitsNSA TOP SECRET approved
Salt GenerationSecRandomCopyBytes (32 bytes)FIPS 140-2 Level 1
PIN Length6 digits (1 million combinations)Industry standard
Keychain ProtectionkSecAttrAccessibleWhenUnlockedThisDeviceOnlyiOS Security Guide
Keychain SyncDisabled (kSecAttrSynchronizable = false)Zero-knowledge architecture
Biometric AuthLAContext.deviceOwnerAuthenticationiOS LocalAuthentication
Nonce GenerationAutomatic (CryptoKit AES.GCM)NIST SP 800-38D
Authentication Tag128 bits (GCM default)NIST SP 800-38D