-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ios: Banana Spit backup flow (#2355)
* feat: ios: Assets, localizable, models, rename and moving seeds related classes * feat: ios: UI elements fixes and cleanup * feat: ios: Banana split main modal * feat: ios: Banana split action modal * feat: ios: banana split / passphrase keychain classes * feat: ios: QR code modal, passphrase modal * feat: ios: initial key details integration * feat: ios: Unit tests for BS keychain * feat: ios: banana split unit tests --------- Co-authored-by: Pavel Rybalko <[email protected]>
- Loading branch information
Showing
45 changed files
with
1,956 additions
and
153 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// | ||
// IconButton.swift | ||
// PolkadotVault | ||
// | ||
// Created by Krzysztof Rodak on 15/02/2024. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct IconButtonStyle: ButtonStyle { | ||
func makeBody(configuration: Self.Configuration) -> some View { | ||
configuration.label | ||
.padding(Spacing.medium) | ||
.foregroundColor(.textAndIconsSecondary) | ||
.frame( | ||
height: Heights.iconButton, | ||
alignment: .center | ||
) | ||
} | ||
} | ||
|
||
struct IconButton: View { | ||
private let action: () -> Void | ||
private let icon: ImageResource | ||
|
||
init( | ||
action: @escaping () -> Void, | ||
icon: ImageResource | ||
) { | ||
self.action = action | ||
self.icon = icon | ||
} | ||
|
||
var body: some View { | ||
Button(action: action) { | ||
HStack { | ||
Image(icon) | ||
} | ||
} | ||
.buttonStyle(IconButtonStyle()) | ||
} | ||
} | ||
|
||
#if DEBUG | ||
struct IconButton_Previews: PreviewProvider { | ||
static var previews: some View { | ||
VStack(alignment: .leading, spacing: 10) { | ||
IconButton( | ||
action: {}, | ||
icon: .refreshPassphrase | ||
) | ||
} | ||
.padding() | ||
.preferredColorScheme(.dark) | ||
.previewLayout(.sizeThatFits) | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
ios/PolkadotVault/Core/Keychain/BananaSplit/KeychainBananaSplitQueryProvider.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// | ||
// KeychainBananaSplitQueryProvider.swift | ||
// Polkadot Vault | ||
// | ||
// Created by Krzysztof Rodak on 26/02/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
struct BananaSplitPassphrase: Codable, Equatable { | ||
let passphrase: String | ||
} | ||
|
||
enum KeychainBananaSplitQuery { | ||
case fetch(seedName: String) | ||
case check(seedName: String) | ||
case delete(seedName: String) | ||
case save(seedName: String, bananaSplit: BananaSplitBackup) | ||
} | ||
|
||
enum KeychainBananaSplitPassphraseQuery { | ||
case fetch(seedName: String) | ||
case delete(seedName: String) | ||
case save(seedName: String, passphrase: BananaSplitPassphrase, accessControl: SecAccessControl) | ||
} | ||
|
||
// sourcery: AutoMockable | ||
protocol KeychainBananaSplitQueryProviding: AnyObject { | ||
func query(for queryType: KeychainBananaSplitQuery) -> CFDictionary | ||
func passhpraseQuery(for queryType: KeychainBananaSplitPassphraseQuery) -> CFDictionary | ||
} | ||
|
||
final class KeychainBananaSplitQueryProvider: KeychainBananaSplitQueryProviding { | ||
enum Constants { | ||
static let bananaSplitSuffix = "_bananaSplit" | ||
static let passphraseSuffix = "_passphrase" | ||
} | ||
|
||
private let jsonEncoder: JSONEncoder | ||
|
||
init(jsonEncoder: JSONEncoder = JSONEncoder()) { | ||
self.jsonEncoder = jsonEncoder | ||
} | ||
|
||
func query(for queryType: KeychainBananaSplitQuery) -> CFDictionary { | ||
var dictionary: [CFString: Any] = [ | ||
kSecClass: kSecClassGenericPassword | ||
] | ||
switch queryType { | ||
case let .fetch(seedName): | ||
dictionary[kSecMatchLimit] = kSecMatchLimitOne | ||
dictionary[kSecAttrAccount] = backupName(seedName) | ||
dictionary[kSecReturnAttributes] = false | ||
dictionary[kSecReturnData] = true | ||
case let .check(seedName): | ||
dictionary[kSecMatchLimit] = kSecMatchLimitOne | ||
dictionary[kSecAttrAccount] = backupName(seedName) | ||
dictionary[kSecReturnData] = false | ||
case let .delete(seedName): | ||
dictionary[kSecAttrAccount] = backupName(seedName) | ||
case let .save(seedName, bananaSplit): | ||
dictionary[kSecAttrAccount] = backupName(seedName) | ||
if let data = try? jsonEncoder.encode(bananaSplit) { | ||
dictionary[kSecValueData] = data | ||
} | ||
dictionary[kSecReturnData] = false | ||
} | ||
return dictionary as CFDictionary | ||
} | ||
|
||
func passhpraseQuery(for queryType: KeychainBananaSplitPassphraseQuery) -> CFDictionary { | ||
var dictionary: [CFString: Any] = [ | ||
kSecClass: kSecClassGenericPassword | ||
] | ||
switch queryType { | ||
case let .fetch(seedName): | ||
dictionary[kSecMatchLimit] = kSecMatchLimitOne | ||
dictionary[kSecAttrAccount] = passphraseName(seedName) | ||
dictionary[kSecReturnAttributes] = false | ||
dictionary[kSecReturnData] = true | ||
case let .delete(seedName): | ||
dictionary[kSecAttrAccount] = passphraseName(seedName) | ||
case let .save(seedName, passphrase, accessControl): | ||
dictionary[kSecAttrAccessControl] = accessControl | ||
dictionary[kSecAttrAccount] = passphraseName(seedName) | ||
if let data = try? jsonEncoder.encode(passphrase) { | ||
dictionary[kSecValueData] = data | ||
} | ||
|
||
dictionary[kSecReturnData] = false | ||
} | ||
return dictionary as CFDictionary | ||
} | ||
|
||
private func backupName(_ seedName: String) -> String { | ||
seedName + Constants.bananaSplitSuffix | ||
} | ||
|
||
private func passphraseName(_ seedName: String) -> String { | ||
seedName + Constants.passphraseSuffix | ||
} | ||
} |
Oops, something went wrong.