Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
e13027356d
commit
d2bf6004e1
@ -6,5 +6,6 @@ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES
|
|||||||
INFOPLIST_KEY_BaseBundleID = $(BASE_BUNDLE_ID)
|
INFOPLIST_KEY_BaseBundleID = $(BASE_BUNDLE_ID)
|
||||||
INFOPLIST_KEY_TeamID = $(TEAM_ID)
|
INFOPLIST_KEY_TeamID = $(TEAM_ID)
|
||||||
INFOPLIST_KEY_AppGroupID = $(APP_GROUP_ID)
|
INFOPLIST_KEY_AppGroupID = $(APP_GROUP_ID)
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = SecureStorage uses Face ID to unlock protected Keychain items.
|
||||||
|
|
||||||
CODE_SIGN_ENTITLEMENTS = SecureStorageSample/SecureStorageSample.entitlements
|
CODE_SIGN_ENTITLEMENTS = SecureStorageSample/SecureStorageSample.entitlements
|
||||||
|
|||||||
@ -42,7 +42,7 @@ SharedPackage/
|
|||||||
└── Models/
|
└── Models/
|
||||||
└── UserProfile.swift
|
└── UserProfile.swift
|
||||||
SecureStorageSample/
|
SecureStorageSample/
|
||||||
├── ContentView.swift # Tabbed navigation
|
├── ContentView.swift # List-based navigation
|
||||||
├── Models/
|
├── Models/
|
||||||
│ ├── Credential.swift
|
│ ├── Credential.swift
|
||||||
│ └── SampleLocationData.swift
|
│ └── SampleLocationData.swift
|
||||||
@ -112,6 +112,7 @@ The app demonstrates various storage configurations:
|
|||||||
### Encrypted Storage
|
### Encrypted Storage
|
||||||
- AES-256-GCM or ChaCha20-Poly1305 encryption
|
- AES-256-GCM or ChaCha20-Poly1305 encryption
|
||||||
- PBKDF2 or HKDF key derivation
|
- PBKDF2 or HKDF key derivation
|
||||||
|
- PBKDF2 iteration count must remain consistent or existing data will not decrypt
|
||||||
- Complete file protection
|
- Complete file protection
|
||||||
- External key material example via `KeyMaterialProviding`
|
- External key material example via `KeyMaterialProviding`
|
||||||
- Global encryption configuration (Keychain service/account) in app `init`
|
- Global encryption configuration (Keychain service/account) in app `init`
|
||||||
|
|||||||
@ -17,6 +17,7 @@ extension StorageKeys {
|
|||||||
let syncPolicy: SyncPolicy = .never
|
let syncPolicy: SyncPolicy = .never
|
||||||
|
|
||||||
init(iterations: Int = 10_000) {
|
init(iterations: Int = 10_000) {
|
||||||
|
// NOTE: PBKDF2 iterations must remain stable for existing data; changing this breaks decryption.
|
||||||
self.security = .encrypted(.aes256(keyDerivation: .pbkdf2(iterations: iterations)))
|
self.security = .encrypted(.aes256(keyDerivation: .pbkdf2(iterations: iterations)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,9 @@ struct EncryptedStorageDemo: View {
|
|||||||
Stepper("PBKDF2 Iterations: \(iterations)", value: $iterations, in: 1000...100000, step: 1000)
|
Stepper("PBKDF2 Iterations: \(iterations)", value: $iterations, in: 1000...100000, step: 1000)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
|
|
||||||
|
IterationWarningView()
|
||||||
|
.padding(.top, Design.Spacing.xSmall)
|
||||||
|
|
||||||
Text("Higher iterations = more secure but slower")
|
Text("Higher iterations = more secure but slower")
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
@ -211,3 +214,11 @@ struct EncryptedStorageDemo: View {
|
|||||||
EncryptedStorageDemo()
|
EncryptedStorageDemo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct IterationWarningView: View {
|
||||||
|
var body: some View {
|
||||||
|
Text("PBKDF2 iterations must match the value used during encryption. Changing this after saving will prevent decryption.")
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundStyle(Color.Status.warning)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user