# ByteGuard Security Whitepaper

**Version**: 1.0
**Published**: May 3, 2026
**Applies to**: ByteGuard iOS v2.3.x and later
**Contact**: hellobyteguard@gmail.com

---

## 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](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id) — 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:

```swift
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 `hellobyteguard@gmail.com`. 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)
