Wrapped Security and Keychain handling
This commit is contained in:
parent
6ed490b232
commit
c22e18b07e
|
@ -100,6 +100,8 @@
|
||||||
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
|
9E4DC6E227C4C6B700E657F4 /* SecantButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */; };
|
||||||
9E69A24D27FB002800A55317 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* Welcome.swift */; };
|
9E69A24D27FB002800A55317 /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* Welcome.swift */; };
|
||||||
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
|
9E80B47227E4B34B008FF493 /* UserPreferencesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */; };
|
||||||
|
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */; };
|
||||||
|
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */; };
|
||||||
9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; };
|
9EBEF87A27CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */; };
|
||||||
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; };
|
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */; };
|
||||||
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; };
|
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */; };
|
||||||
|
@ -252,6 +254,8 @@
|
||||||
9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = "<group>"; };
|
9E4DC6E127C4C6B700E657F4 /* SecantButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecantButtonStyles.swift; sourceTree = "<group>"; };
|
||||||
9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; };
|
9E69A24C27FB002800A55317 /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; };
|
||||||
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
|
9E80B47127E4B34B008FF493 /* UserPreferencesStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorage.swift; sourceTree = "<group>"; };
|
||||||
|
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItem.swift; sourceTree = "<group>"; };
|
||||||
|
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrappedSecItemTests.swift; sourceTree = "<group>"; };
|
||||||
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
|
9EBEF87927CE369800B4F343 /* RecoveryPhraseTestPreambleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseTestPreambleView.swift; sourceTree = "<group>"; };
|
||||||
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
|
9ECAE56727FC713C0089A0EF /* DatabaseFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFiles.swift; sourceTree = "<group>"; };
|
||||||
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
|
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletStorageTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -727,6 +731,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
9E02B56927FED43E005B809B /* WrappedFileManager.swift */,
|
9E02B56927FED43E005B809B /* WrappedFileManager.swift */,
|
||||||
|
9EAFEB83280597B700199FC9 /* WrappedSecItem.swift */,
|
||||||
);
|
);
|
||||||
path = Wrappers;
|
path = Wrappers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -761,6 +766,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */,
|
9EF8135A27ECC25E0075AF48 /* WalletStorageTests.swift */,
|
||||||
|
9EAFEB852805A23100199FC9 /* WrappedSecItemTests.swift */,
|
||||||
9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */,
|
9EF8135B27ECC25E0075AF48 /* UserPreferencesStorageTests.swift */,
|
||||||
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */,
|
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */,
|
||||||
);
|
);
|
||||||
|
@ -1193,6 +1199,7 @@
|
||||||
663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */,
|
663FAB9C271D874D00E495F8 /* ActiveButton.swift in Sources */,
|
||||||
F9C165C02740403600592F76 /* ApproveView.swift in Sources */,
|
F9C165C02740403600592F76 /* ApproveView.swift in Sources */,
|
||||||
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */,
|
0DF2DC5427235E3E00FA31E2 /* View+InnerShadow.swift in Sources */,
|
||||||
|
9EAFEB84280597B700199FC9 /* WrappedSecItem.swift in Sources */,
|
||||||
9E2AC10327DA28200042AA47 /* WalletStorage.swift in Sources */,
|
9E2AC10327DA28200042AA47 /* WalletStorage.swift in Sources */,
|
||||||
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */,
|
9ECAE56827FC713C0089A0EF /* DatabaseFiles.swift in Sources */,
|
||||||
F9971A6B27680E1000A2DB75 /* WalletInfo.swift in Sources */,
|
F9971A6B27680E1000A2DB75 /* WalletInfo.swift in Sources */,
|
||||||
|
@ -1233,6 +1240,7 @@
|
||||||
files = (
|
files = (
|
||||||
0DFE93DF272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift in Sources */,
|
0DFE93DF272C6D4B000FCCA5 /* RecoveryPhraseBackupTests.swift in Sources */,
|
||||||
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
||||||
|
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
|
||||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
||||||
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
|
0D4E7A1B26B364180058B01E /* secantTests.swift in Sources */,
|
||||||
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
|
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import MnemonicSwift
|
import MnemonicSwift
|
||||||
import Security
|
|
||||||
|
|
||||||
/// Zcash implementation of the keychain that is not universal but designed to deliver functionality needed by the wallet itself.
|
/// Zcash implementation of the keychain that is not universal but designed to deliver functionality needed by the wallet itself.
|
||||||
/// All the APIs should be thread safe according to official doc:
|
/// All the APIs should be thread safe according to official doc:
|
||||||
|
@ -35,6 +34,12 @@ struct WalletStorage {
|
||||||
case unsupportedLanguage(MnemonicLanguageType)
|
case unsupportedLanguage(MnemonicLanguageType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let secItem: WrappedSecItem
|
||||||
|
|
||||||
|
init(secItem: WrappedSecItem) {
|
||||||
|
self.secItem = secItem
|
||||||
|
}
|
||||||
|
|
||||||
func importWallet(
|
func importWallet(
|
||||||
bip39 phrase: String,
|
bip39 phrase: String,
|
||||||
birthday: BlockHeight?,
|
birthday: BlockHeight?,
|
||||||
|
@ -176,11 +181,7 @@ struct WalletStorage {
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Wallet Storage Helper Functions
|
|
||||||
|
|
||||||
private extension WalletStorage {
|
|
||||||
/// Restore data for key
|
/// Restore data for key
|
||||||
func data(
|
func data(
|
||||||
forKey: String,
|
forKey: String,
|
||||||
|
@ -189,7 +190,7 @@ private extension WalletStorage {
|
||||||
let query = restoreQuery(forAccount: account, andKey: forKey)
|
let query = restoreQuery(forAccount: account, andKey: forKey)
|
||||||
|
|
||||||
var result: AnyObject?
|
var result: AnyObject?
|
||||||
_ = SecItemCopyMatching(query as CFDictionary, &result)
|
_ = secItem.copyMatching(query as CFDictionary, &result)
|
||||||
|
|
||||||
return result as? Data
|
return result as? Data
|
||||||
}
|
}
|
||||||
|
@ -202,7 +203,7 @@ private extension WalletStorage {
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
let query = baseQuery(forAccount: account, andKey: forKey)
|
let query = baseQuery(forAccount: account, andKey: forKey)
|
||||||
|
|
||||||
let status = SecItemDelete(query as CFDictionary)
|
let status = secItem.delete(query as CFDictionary)
|
||||||
|
|
||||||
return status == noErr
|
return status == noErr
|
||||||
}
|
}
|
||||||
|
@ -216,7 +217,8 @@ private extension WalletStorage {
|
||||||
var query = baseQuery(forAccount: account, andKey: forKey)
|
var query = baseQuery(forAccount: account, andKey: forKey)
|
||||||
query[kSecValueData as String] = data as AnyObject
|
query[kSecValueData as String] = data as AnyObject
|
||||||
|
|
||||||
let status = SecItemAdd(query as CFDictionary, nil)
|
var result: AnyObject?
|
||||||
|
let status = secItem.add(query as CFDictionary, &result)
|
||||||
|
|
||||||
guard status != errSecDuplicateItem else {
|
guard status != errSecDuplicateItem else {
|
||||||
throw KeychainError.duplicate
|
throw KeychainError.duplicate
|
||||||
|
@ -239,7 +241,7 @@ private extension WalletStorage {
|
||||||
kSecValueData as String: data as AnyObject
|
kSecValueData as String: data as AnyObject
|
||||||
]
|
]
|
||||||
|
|
||||||
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
|
let status = secItem.update(query as CFDictionary, attributes as CFDictionary)
|
||||||
|
|
||||||
guard status != errSecItemNotFound else {
|
guard status != errSecItemNotFound else {
|
||||||
throw KeychainError.noDataFound
|
throw KeychainError.noDataFound
|
||||||
|
|
|
@ -76,7 +76,7 @@ struct WalletStorageInteractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WalletStorageInteractor {
|
extension WalletStorageInteractor {
|
||||||
public static func live(walletStorage: WalletStorage = WalletStorage()) -> Self {
|
public static func live(walletStorage: WalletStorage = WalletStorage(secItem: .live)) -> Self {
|
||||||
Self(
|
Self(
|
||||||
importWallet: { bip39, birthday, language, hasUserPassedPhraseBackupTest in
|
importWallet: { bip39, birthday, language, hasUserPassedPhraseBackupTest in
|
||||||
try walletStorage.importWallet(
|
try walletStorage.importWallet(
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// WrappedSecItem.swift
|
||||||
|
// secant-testnet
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 12.04.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Security
|
||||||
|
|
||||||
|
struct WrappedSecItem {
|
||||||
|
let copyMatching: (CFDictionary, inout CFTypeRef?) -> OSStatus
|
||||||
|
let add: (CFDictionary, inout CFTypeRef?) -> OSStatus
|
||||||
|
let update: (CFDictionary, CFDictionary) -> OSStatus
|
||||||
|
let delete: (CFDictionary) -> OSStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WrappedSecItem {
|
||||||
|
static let live = WrappedSecItem(
|
||||||
|
copyMatching: { query, result in
|
||||||
|
SecItemCopyMatching(query, &result)
|
||||||
|
},
|
||||||
|
add: { attributes, result in
|
||||||
|
SecItemAdd(attributes, &result)
|
||||||
|
},
|
||||||
|
update: { query, attributesToUpdate in
|
||||||
|
SecItemUpdate(query, attributesToUpdate)
|
||||||
|
},
|
||||||
|
delete: { query in
|
||||||
|
SecItemDelete(query)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ class WalletStorageTests: XCTestCase {
|
||||||
let birthday = BlockHeight(12345678)
|
let birthday = BlockHeight(12345678)
|
||||||
let seedPhrase = "one two three"
|
let seedPhrase = "one two three"
|
||||||
let language = MnemonicLanguageType.english
|
let language = MnemonicLanguageType.english
|
||||||
var storage = WalletStorage()
|
var storage = WalletStorage(secItem: .live)
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
//
|
||||||
|
// WrappedSecItemTests.swift
|
||||||
|
// secantTests
|
||||||
|
//
|
||||||
|
// Created by Lukáš Korba on 12.04.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import secant_testnet
|
||||||
|
|
||||||
|
extension WalletStorage.KeychainError {
|
||||||
|
var debugValue: String {
|
||||||
|
switch self {
|
||||||
|
case .decoding: return "decoding"
|
||||||
|
case .duplicate: return "duplicate"
|
||||||
|
case .encoding: return "encoding"
|
||||||
|
case .noDataFound: return "noDataFound"
|
||||||
|
case .unknown: return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrappedSecItemTests: XCTestCase {
|
||||||
|
func test_secItemAdd_KeychainErrorDuplicate() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecDuplicateItem },
|
||||||
|
update: { _, _ in errSecSuccess },
|
||||||
|
delete: { _ in errSecSuccess }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try walletStorage.setData(Data(), forKey: "")
|
||||||
|
|
||||||
|
XCTFail("WrappedSecItem: test_secItemAdd_KeychainErrorDuplicate expected to fail but passed.")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? WalletStorage.KeychainError else {
|
||||||
|
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
error.debugValue,
|
||||||
|
WalletStorage.KeychainError.duplicate.debugValue,
|
||||||
|
"WrappedSecItem: error must be .duplicate but it's \(error)."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_secItemAdd_KeychainErrorUnknown() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecCoreFoundationUnknown },
|
||||||
|
update: { _, _ in errSecSuccess },
|
||||||
|
delete: { _ in errSecSuccess }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try walletStorage.setData(Data(), forKey: "")
|
||||||
|
|
||||||
|
XCTFail("WrappedSecItem: test_secItemAdd_KeychainErrorUnknown expected to fail but passed.")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? WalletStorage.KeychainError else {
|
||||||
|
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
error.debugValue,
|
||||||
|
WalletStorage.KeychainError.unknown(0).debugValue,
|
||||||
|
"WrappedSecItem: error must be .unknown but it's \(error)."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_secItemUpdate_KeychainErrorNoDataFound() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecSuccess },
|
||||||
|
update: { _, _ in errSecItemNotFound },
|
||||||
|
delete: { _ in errSecSuccess }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try walletStorage.updateData(Data(), forKey: "")
|
||||||
|
|
||||||
|
XCTFail("WrappedSecItem: test_secItemUpdate_KeychainErrorNoDataFound expected to fail but passed.")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? WalletStorage.KeychainError else {
|
||||||
|
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
error.debugValue,
|
||||||
|
WalletStorage.KeychainError.noDataFound.debugValue,
|
||||||
|
"WrappedSecItem: error must be .noDataFound but it's \(error)."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_secItemUpdate_KeychainErrorUnknown() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecSuccess },
|
||||||
|
update: { _, _ in errSecCoreFoundationUnknown },
|
||||||
|
delete: { _ in errSecSuccess }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try walletStorage.updateData(Data(), forKey: "")
|
||||||
|
|
||||||
|
XCTFail("WrappedSecItem: test_secItemUpdate_KeychainErrorUnknown expected to fail but passed.")
|
||||||
|
} catch {
|
||||||
|
guard let error = error as? WalletStorage.KeychainError else {
|
||||||
|
XCTFail("WrappedSecItem: the error is expected to be WalletStorage.KeychainError but it's \(error).")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
error.debugValue,
|
||||||
|
WalletStorage.KeychainError.unknown(0).debugValue,
|
||||||
|
"WrappedSecItem: error must be .unknown but it's \(error)."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_secItemDelete_Succeeded() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecSuccess },
|
||||||
|
update: { _, _ in errSecSuccess },
|
||||||
|
delete: { _ in noErr }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
let result = walletStorage.deleteData(forKey: "")
|
||||||
|
|
||||||
|
XCTAssertTrue(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_secItemDelete_Failed() throws {
|
||||||
|
let secItemDuplicate = WrappedSecItem(
|
||||||
|
copyMatching: { _, _ in errSecSuccess },
|
||||||
|
add: { _, _ in errSecSuccess },
|
||||||
|
update: { _, _ in errSecSuccess },
|
||||||
|
delete: { _ in errSecCoreFoundationUnknown }
|
||||||
|
)
|
||||||
|
|
||||||
|
let walletStorage = WalletStorage(secItem: secItemDuplicate)
|
||||||
|
|
||||||
|
let result = walletStorage.deleteData(forKey: "")
|
||||||
|
|
||||||
|
XCTAssertFalse(result)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue