Full Security Whitepaper

Want the threat model, parameter rationale, and every engineering detail?

ByteGuard Security Whitepaper

Version: 1.0 Published: May 3, 2026 Applies to: ByteGuard iOS v2.3.x and later Contact: [email protected]


1. Introduction

ByteGuard is a password manager that runs entirely on the user's Apple devices (iPhone / iPad / Mac). This whitepaper is for cryptographic auditors, independent security researchers, and high-assurance users. It documents the key hierarchy, encryption algorithms, device-level protection mechanisms, and threat model in full.

We follow Kerckhoffs's Principle: a cryptographic system's security must rely solely on key secrecy, not on algorithm or architecture obscurity. Everything disclosed in this document (algorithms, parameters, key hierarchy, storage locations) does not weaken real security — on the contrary, public disclosure is a prerequisite for audit and trust.


2. Threat Model

2.1 Threats We Defend Against

Threat Defense
Server breach / data leak ByteGuard runs no backend servers; sync flows entirely through the user's own iCloud Private DB
Lost device Field-level AES-256-GCM + Keychain kSecAttrAccessibleWhenUnlockedThisDeviceOnly
Network MITM Master Password, Secret Key, and the plaintext Vault DEK never leave the device's RAM; uploads carry AES-256-GCM-encrypted field ciphertext plus the KEK-wrapped DEK (application-layer encryption, independent of CloudKit's own encryption)
Phishing logins Passkey (FIDO2) private keys never leave the device
Weak Master Password brute force Argon2id 64MB memory cost × 3 iterations (OWASP mobile recommendation)
GPU offline attack Argon2id memory hardness is hostile to GPUs
Timing attack on password verify Constant-time XOR-accumulate byte comparison
Memory residue Vault DEK is overwritten three times (zero → random → zero) on lock / suspend / vault switch; Master Key / KEK exist only as locals inside the unlock function and are released by Swift ARC

2.2 Threats We Do Not Defend Against

We make no claim about the following — any password manager that claims defense here is exaggerating:

  • Compromised OS: jailbreak, kernel exploit, malicious MDM
  • Physical coercion: user is forced to enter the Master Password
  • Keylogger: OS-level malware capturing user input
  • Screen capture: third-party screenshot apps after unlock
  • User intentionally leaks Master Password + Secret Key

These threats fall outside the security model of this whitepaper.


3. Key Hierarchy

3.1 Overview

ByteGuard uses a five-tier key hierarchy with per-entry key isolation:

Master Password + Secret Key (128-bit) ──Argon2id──→ Master Key (32B)
                                                         │
                                                         ├─HKDF "auth-v1"──→ Auth Hash (verify only)
                                                         │
                                                         └─HKDF "kek-v1"──→ KEK (32B)
                                                                              │
                                                                              ↓ AES-GCM unwrap
                                                                       Vault DEK (32B, vault-wide, randomly generated)
                                                                              │
                                                                              ↓ HKDF "type-{T}-item-{ID}-v1"
                                                                       Item Key (32B, per-entry)
                                                                              │
                                                                              ↓ AES-256-GCM
                                                                       Field ciphertext

3.2 Inputs

Input Length Source Persistence
Master Password user input user input never persisted
Secret Key 128-bit (16B) libsodium randomBytes at vault creation iOS Keychain (optional iCloud Keychain sync); also shown to the user as a 12-word BIP39 mnemonic for backup
Salt 256-bit (32B) libsodium randomBytes at vault creation SwiftData (vault metadata)

3.3 Master Key

Algorithm: Argon2id (libsodium implementation)

Input: Argon2id(password ‖ secretKey.base64, salt) → 32 bytes

Parameters:

  • Memory cost: 64 MB
  • Iterations: 3
  • Parallelism: libsodium auto-configured
  • Output length: 32 bytes

Parameter rationale: OWASP Password Storage Cheat Sheet — mobile recommendation. Typical wall-clock on iPhone 14 Pro: 100~250 ms (imperceptible to user, costly to GPU attackers).

Security properties:

  • Master Key never leaves the device, never persisted
  • Zeroed immediately on exit / lock
  • Server has zero knowledge of this key

3.4 Auth Hash and KEK

Both are derived from Master Key via HKDF-SHA256 (RFC 5869):

Derived key Purpose HKDF info string Output length
Auth Hash Verify Master Password correctness "vault-auth-v1" 32B
KEK Unwrap Vault DEK "vault-kek-v1" 32B

Strict separation of duty:

  • Auth Hash does not participate in any data decryption — verification only
  • KEK never directly touches user data — unwraps DEK only

Password verification uses constant-time XOR-accumulate comparison, eliminating timing attacks.

3.5 Vault DEK (vault-wide Data Encryption Key)

Generation: At vault creation, libsodium randomBytes produces a 32-byte random key. It is then wrapped with KEK using AES-256-GCM (the Wrapped DEK) and stored in SwiftData.

Why one DEK per vault, not per item? Persistence stores only one wrapped DEK (instead of N), so storage and sync overhead grows linearly but slowly with entry count. Per-entry isolation is delivered by the next layer (Item Key).

Master Password change is cheap:

  1. Re-derive Master Key / Auth Hash / KEK
  2. DEK stays the same — only re-wrap DEK with new KEK
  3. No vault-wide re-encryption needed — this is the core advantage of envelope encryption.

3.6 Item Key (per-entry)

Algorithm: HKDF-SHA256(Vault DEK, info="type-{T}-item-{ID}-v1") → 32B

  • T: data type (login / card / note / passkey / identity, ...)
  • ID: entry's unique ID

Security:

  • Each record uses an independent key — leaking one Item Key does not affect others
  • Deterministic derivation → no need to persist a key per entry
  • Type + ID concatenation → even if two entries share the same ID across types, they derive distinct Item Keys

3.7 Field Ciphertext

Algorithm: AES-256-GCM (CryptoKit implementation)

Encrypted per field: username, password, URL, custom fields, card number, CVV (session-memory only), notes, attachments — all sensitive fields.

Unique IV: CryptoKit auto-generates a unique 96-bit IV per encryption — identical plaintext never yields identical ciphertext.

Never persisted:

  • CVV / CVC (PCI DSS hard line — not persisted at any stage)
  • Master Password (never persisted)
  • Raw biometric data (handled by iOS — the app never touches it)

4. Device-Level Protection

4.1 iOS Keychain ACL

To support Face ID / Touch ID quick unlock, a copy of the Vault DEK is stored in iOS Keychain when the user enables biometric:

Access control (ACL):
  kSecAttrAccessible       = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  kSecAccessControlFlags   = .biometryCurrentSet

Meaning:

  • WhenUnlockedThisDeviceOnly: Unreadable when device is locked; not synced via iCloud Keychain
  • biometryCurrentSet: Requires Face ID / Touch ID verification; if the user adds/removes/re-enrolls biometric data in system settings, iOS automatically deletes this entry

4.2 Secure Enclave Takes Over Transparently

iOS Keychain entries are encrypted by a device key inside Secure Enclave. When the app calls SecItemCopyMatching:

  1. iOS triggers a Face ID / Touch ID prompt
  2. On success, SE decrypts the Keychain entry using the device key
  3. Returns the plaintext Vault DEK to the app

Key facts:

  • The application layer does not hold any BiometricKey / SE key
  • The trust boundary is delegated entirely to iOS Keychain ACL
  • SE operations are completely handled by iOS system isolation; the app cannot reach into SE

4.3 Face ID Re-enrollment Auto-Invalidation

iOS's biometryCurrentSet semantic guarantees: the moment a user re-enrolls Face ID (even just adding an additional face), iOS automatically deletes the Keychain entry.

ByteGuard detects this change at launch via LAContext.evaluatedPolicyDomainState:

  • Detects biometric domain change → automatically disables quick unlock
  • Clears all in-memory cached DEK and decrypted data
  • Forces user to re-enter Master Password

4.4 Strict Mode (No Fallback)

If biometryCurrentSet is unavailable (simulator, no enrolled biometrics), the DEK cache simply throws — and does not fall back to a plain Keychain entry without biometric protection.

Design rationale:

  • The user's intent in enabling Face ID is "stronger protection"; silently downgrading to weaker storage violates that mental model
  • Fallback would actually be weaker than not enabling Face ID at all — without Face ID, nothing is cached; with fallback, something is cached
  • Callers receive the exception, log it, and continue: the next unlock simply prompts for Master Password

5. Sync and Local Storage

5.1 SwiftData + Field Encryption

All user data lives in iOS SwiftData. ByteGuard does not rely on SwiftData's storage encryption — even if an attacker obtains the raw SwiftData file bytes, every sensitive field is already AES-256-GCM ciphertext.

5.2 iCloud Sync (Optional)

iCloud sync is disabled by default; the user must explicitly enable it in Settings. Once enabled, SwiftData connects via ModelConfiguration(cloudKitDatabase: .private(...)) to CloudKit Private Database.

Plain facts about CloudKit encryption (to dispel a common misconception):

  • CloudKit Private DB by default is stored on Apple servers; transit uses HTTPS and data at rest is encrypted, but the decryption key is held by Apple
  • Only when the user additionally enables iCloud "Advanced Data Protection" (iOS 16.2+) does CloudKit Private DB enter end-to-end encryption mode
  • Therefore ByteGuard does not assume Apple cannot read CloudKit; instead it adds its own AES-256-GCM field encryption layer

Key properties:

  • iCloud uploads ByteGuard-encrypted field ciphertext (ciphertext + 96-bit IV + 128-bit GCM tag)
  • Even if Apple complies with a legal request or CloudKit is breached, what's exposed is AES ciphertext only — without the Vault DEK it cannot be reconstructed
  • The wrapped Vault DEK syncs via iCloud Keychain (Apple's own end-to-end encrypted product — E2EE regardless of ADP; Apple does not hold the decryption key)
  • Cross-device recovery requires: iCloud account + Secret Key + Master Password — all three.

5.3 FAQ: Is uploading the wrapped DEK to CloudKit really safe?

Seeing "the wrapped Vault DEK is uploaded to iCloud" naturally raises a concern: isn't putting the key in the cloud counter-intuitive? This section addresses that head-on.

A. Why the wrapped DEK must be uploaded

Cross-device sync demands cryptographically: a user signing into a brand-new iPhone / iPad must be able to decrypt all previously stored fields. Without syncing the wrapped DEK, the new device can never obtain the DEK, and all ciphertext is effectively lost.

Envelope encryption (KEK wraps DEK) is the standard solution to this dilemma — it decouples "the user's master password" from "the data encryption key," letting the former live only in the user's head while the latter, once wrapped by a key derived from it, can sit anywhere (including a public server) without weakening security.

B. What an attacker has to do after stealing the wrapped DEK

To recover the plaintext DEK from the wrapped DEK, an attacker must break through, in order:

wrapped DEK = AES-256-GCM(KEK, plaintext_DEK, IV)
                ↑
          KEK required to decrypt
                ↑
          KEK = HKDF-SHA256(Master Key, "vault-kek-v1")
                ↑
          Master Key required
                ↑
          Master Key = Argon2id(Master Password ‖ Secret Key, salt) · 64MB × 3 iter
                ↑
          Three barriers must be cleared simultaneously:
          ① The user's Master Password (attacker doesn't know it)
          ② 128-bit Secret Key (user-specific; Apple cannot obtain it; never leaves the device in plaintext form)
          ③ Each guess requires a 64MB × 3 iter Argon2id computation

C. Concrete attack-cost estimate

Assume a Master Password with only 60 bits of entropy (decent but not extreme), combined with the 128-bit Secret Key = 188 bits total.

  • Each guess requires a 64MB-memory Argon2id computation — GPUs offer no real help (memory-hardness by design)
  • Assume all human GPUs combined ≈ 10^10 Argon2id-64MB ops / second (extremely optimistic)
  • Brute-forcing 2^188 combinations takes ~10^46 years

Even if quantum computing matures and Argon2id is sped up by 10^15×, the search would still take 10^31 years — 10^21× the age of the universe.

D. Comparison with industry leaders

Every mainstream password manager stores the wrapped master key (or equivalent) in the cloud:

Vendor Sync Wrapped master key location KDF
1Password Own cloud Uploaded PBKDF2 / protected by Account Password + Secret Key
Bitwarden Own cloud Uploaded PBKDF2 / Argon2id (user-selectable)
Dashlane Own cloud Uploaded Argon2id
Apple Keychain iCloud Keychain E2EE Uploaded (Apple ADP layer) Apple-internal
ByteGuard iCloud Private DB Uploaded Argon2id 64MB × 3 + 128-bit Secret Key

ByteGuard's cryptographic strength is in the top tier of the industry, with key differentiators:

  • 128-bit Secret Key as a second factor (same approach as 1Password) — even if the Master Password is weak, even if Apple complies with a legal request and hands over the wrapped DEK, the attacker still has nowhere to start
  • Argon2id 64MB mobile-recommended parameters (on par with Dashlane)

E. Conclusion

Uploading the wrapped DEK to iCloud does not weaken security at all — it is the core security premise of envelope encryption.

Security depends entirely on: ① Master Password strength ② the Secret Key never being leaked ③ Argon2id parameters. Whether CloudKit is breached, whether Apple can decrypt CloudKit, whether a network MITM intercepts ciphertext — all become irrelevant under this design.


5.4 AutoFill Extension (v2 Derived Key Isolation)

iOS AutoFill runs in a separate process context that cannot directly access the main App's in-memory DEK. ByteGuard v2's design:

  • When the main App enables AutoFill, it derives an independent Derived AutoFill Key from the Vault DEK (HKDF + vault id)
  • This key is written to App Group shared Keychain with biometryCurrentSet protection
  • The AutoFill Extension can read this key after Face ID succeeds, decrypting only the credential the user is currently filling

Benefit: AutoFill never touches the master Vault DEK — principle of least privilege.


6. Security Engineering Practices

6.1 Memory Zeroing

SecureMemory.zero(&buffer) performs a three-pass overwrite + memory barrier on sensitive bytes:

  1. Set to zero (memset)
  2. Fill with random bytes (arc4random_buf)
  3. Set to zero again (memset)
  4. After each step, load(as: UInt8) prevents the compiler from optimizing the zeroing away

Scope of zeroing (precise facts):

  • ✅ Vault DEK (the _currentDEK instance variable): explicitly overwritten via SecureMemory.zero in lockVault / clearAllKeychainData
  • ⚠️ Master Key / KEK: exist only as local variables inside unlockVault / performUnlockOperations. After the function returns, Swift ARC releases them. ARC release only zeros the reference count, not the bytes — bytes may linger in the heap until reallocated. This is a current implementation limitation.
  • ⚠️ Argon2 internal buffers: managed by libsodium; libsodium calls sodium_memzero internally after key derivation completes

Defends against: compiler optimization, heap dumps (jailbreak/debugger scenarios), memory pages swapped to disk after app suspension. Does not defend against: live memory reads of Master Key / KEK during the brief window (millisecond~second range) before ARC releases them.

6.2 Constant-Time Comparison

Password verification uses byte-wise XOR accumulation:

var result: UInt8 = 0
for i in 0..<computedHash.count {
    result |= computedHash[i] ^ storedHash[i]
}
guard result == 0 else { throw .invalidPassword }

Ensures comparison time is independent of match position/count, eliminating timing side channels.

6.3 CSPRNG Sources

All randomness (Salt, Vault DEK, Secret Key, IV) comes from:

  • libsodium randomBytes.buf() (calls macOS / iOS arc4random_buf, ultimately backed by kernel CSPRNG)
  • CryptoKit's AES.GCM.seal() auto-generates IVs internally

We never use Math.random(), Date(), PID, or other low-entropy sources.

6.4 Password Generator

The built-in password generator:

  • Random mode: SecRandomCopyBytes (kernel CSPRNG)
  • Memorable passphrase: EFF Long Word List (7,776 words)
  • Numeric PIN: CSPRNG sampling

6.5 Automated Testing

  • ViewModel / Manager / Crypto Service all have unit tests
  • Crypto tests cover: derivation correctness, wrap/unwrap round-trips, password change preserves old ciphertext readability (re-wrapped DEK still decrypts)
  • UI tests cover: unlock flow, biometric invalidation detection

7. What We Cannot Do

A zero-knowledge architecture means even we cannot do the following — by design, not as a limitation:

Scenario Status
Reset the Master Password ❌ Cannot — there is no second decryption path
Recover a lost Secret Key ❌ Cannot — we never held any Secret Key data
Decrypt user data for law enforcement ❌ Cannot — server has no keys, no ciphertext
Insert backdoors in the encryption ❌ Cannot — algorithms and parameters are fully public, independently auditable
Collect user passwords for risk control / training ❌ We don't, and technically can't
Push ads or third-party SDKs ❌ The app integrates no analytics, ad, or crash-reporting SDKs

If a user loses both Master Password and Secret Key → data is permanently undecryptable. This is a hard design constraint, not a bug.


8. Algorithms and Versioning

8.1 Current Algorithm Selection

Purpose Algorithm Implementation Standard
Master Password KDF Argon2id 64MB / 3 iter libsodium RFC 9106
Key derivation HKDF-SHA256 CryptoKit RFC 5869
Symmetric encryption AES-256-GCM CryptoKit NIST SP 800-38D
Password strength zxcvbn port internal —
Breach detection k-anonymity HIBP API internal HIBP API v3
Mnemonic BIP39 12 words (128-bit entropy) internal BIP-0039
Passkey WebAuthn / FIDO2 ECDSA P-256 iOS AuthenticationServices FIDO2 / CTAP 2.2

8.2 Versioning and Compatibility

All HKDF info strings carry a -v1 suffix, enabling seamless future migration to v2 algorithms (e.g. SHA-3, AES-256-GCM-SIV). Every database migration path retains decryption capability for old ciphertext.


9. Known Limitations and Future Work

Known limitations

  1. AutoFill v1 users: Older users not yet migrated to v2 Derived Key still use the legacy biometric key path. New installs default to v2.
  2. iCloud Keychain invalidation: After signing out of iCloud, Secret Key must be manually restored on a new device (enter the 12-word mnemonic)
  3. Password history: Old password copies are retained (user-controlled toggle), encrypted at field level; cleared when the entry is deleted

Future plans

  • Optional end-to-end encrypted "family share" (Diffie-Hellman key agreement; master keys never leave devices)
  • Adaptive Argon2id parameters (device RAM detection)
  • WebAuthn cross-device sync (CTAP 2.2 hybrid transport)

Appendix A: Parameter Reference

Parameter Value
Argon2id memory 64 MB
Argon2id iterations 3
Argon2id output 32 bytes
Salt length 32 bytes
Secret Key length 16 bytes (128-bit)
Master Key length 32 bytes (256-bit)
KEK length 32 bytes (256-bit)
Vault DEK length 32 bytes (256-bit)
Item Key length 32 bytes (256-bit)
AES-GCM IV length 12 bytes (96-bit)
AES-GCM tag length 16 bytes (128-bit)
Auth Hash length 32 bytes
BIP39 mnemonic 12 words

Appendix B: HKDF info string registry

info Purpose
vault-auth-v1 Auth Hash derivation
vault-kek-v1 KEK derivation
type-{T}-item-{ID}-v1 Item Key derivation (by data type + entry ID)
vault-dek-v1 (deprecated) legacy DEK derivation

Appendix C: References

  • RFC 9106 — Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications
  • RFC 5869 — HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
  • NIST SP 800-38D — Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM)
  • BIP-0039 — Mnemonic code for generating deterministic keys
  • OWASP Password Storage Cheat Sheet
  • Apple Platform Security Guide — Keychain Data Protection / Secure Enclave
  • Have I Been Pwned API v3 — k-anonymity password range query

Version history

Version Date Changes
1.0 2026-05-03 Whitepaper v1.0

Feedback and vulnerability disclosure

Found a security issue? Email [email protected]. We commit to:

  • First reply within 24 hours
  • Coordinated disclosure window with the reporter
  • Public credit on fix release for significant issues (with reporter's consent)

Download .md (English) · 下载 .md(中文)