Files
6e841ec293 gh-2828 Lock / unlock store (#2891)
* gh-2828 Lock/unlock data store

* gh-2828 Add required credential checks to security center

* gh-2828 Add screen for faceID unlock

* gh-2828 Separate layout for faceID unlock screen

* gh-2828 Fix optional variable binding; cleanup

* gh-2828 Wire faceID unlock

* gh-2828 WIP: passcode check

* gh-2828 Add handling for initial passcode

* gh-2828 delete v1

* gh-2828 Do not create a new sensitive key when changing the password

* gh-2828 Enable passcode check

* gh-2828 enable conditional unlock

* gh-2828 Rename feature toggle

* gh-2828 Test sensitive key is kept across password changes.

* gh-2828 Unlock datastore with derived user passcode

* gh-2828 Change passcode: password check & unlock

* gh-2828 Prevent double faceID check

* gh-2828 Add separate callback for faceID check

* gh-2828 Automatically ask for TouchId/FaceId

* gh-2828 Can't use protocol as type if you want default values :-(

---------

Co-authored-by: Dirk Jäckel <[email protected]>
2023-02-13 13:28:17 +01:00

201 lines
8.3 KiB
Swift

//
// Created by Dirk Jäckel on 14.11.22.
// Copyright (c) 2022 Gnosis Ltd. All rights reserved.
//
import XCTest
@testable import Multisig
import LocalAuthentication
public class ProtectedKeyStoreTests: XCTestCase {
var sensitiveKeyStore: ProtectedKeyStore! = nil
var dataKeyStore: ProtectedKeyStore! = nil
var keychainItemStore: KeychainItemStore! = nil
public override func setUp() {
super.setUp()
keychainItemStore = KeychainItemStore(KeychainStore())
sensitiveKeyStore = ProtectedKeyStore(protectionClass: .sensitive, keychainItemStore)
dataKeyStore = ProtectedKeyStore(protectionClass: .data, keychainItemStore)
}
public override func tearDown() {
super.tearDown()
do {
try sensitiveKeyStore.deleteAllKeys()
} catch {}
do {
try! dataKeyStore.deleteAllKeys()
} catch {}
}
func testInitialSetup() {
XCTAssertEqual(sensitiveKeyStore.isInitialized(), false)
do {
try sensitiveKeyStore.initializeKeyStore()
} catch {
XCTFail() // do not throw
}
XCTAssertEqual(sensitiveKeyStore.isInitialized(), true)
}
func testImport() throws {
let randomKey = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c322") as Data
try sensitiveKeyStore.initializeKeyStore()
try sensitiveKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomKey)
let result = try sensitiveKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result?.toHexString(), randomKey.toHexString())
}
func testImportOverride() throws {
let randomKey1 = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c322") as Data
let randomKey2 = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c323") as Data
try sensitiveKeyStore.initializeKeyStore()
try sensitiveKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomKey1)
var result = try sensitiveKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result?.toHexString(), randomKey1.toHexString())
try sensitiveKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomKey2)
result = try sensitiveKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result?.toHexString(), randomKey2.toHexString())
}
func testEthereumKeyNotFound() throws {
try sensitiveKeyStore.initializeKeyStore()
let ethPrivateKey = try sensitiveKeyStore.find(dataID: DataID(id: "0xfb1ca734579C3F2dC6DC8cD64A4f5D91891387C6"), password: nil)
XCTAssertEqual(ethPrivateKey, nil)
}
func testChangePassword() {
XCTAssertEqual(sensitiveKeyStore.isInitialized(), false)
do {
try sensitiveKeyStore.initializeKeyStore()
try sensitiveKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "test123", to: "random", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "random", to: nil, useBiometry: false)
} catch {
XCTFail() // should not throw
}
}
func testAfterChangePasswordKeysAreStillAvailable() {
XCTAssertEqual(dataKeyStore.isInitialized(), false)
let randomKey = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c322") as Data
do {
try dataKeyStore.initializeKeyStore()
// Store something
try dataKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomKey)
// Change password
try dataKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
} catch {
XCTFail()
}
do {
// Check imported key can be decrypted
let result = try dataKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: "test123")
XCTAssertEqual(result?.toHexString(), randomKey.toHexString())
} catch {
XCTFail("Imported data could not be found/decrypted")
}
}
func testChangePasswordGivenWrongPasswordShouldFail() throws {
guard simulatorCheck() else {
throw XCTSkip("Test not supported on simulator")
}
XCTAssertEqual(sensitiveKeyStore.isInitialized(), false)
do {
try sensitiveKeyStore.initializeKeyStore()
try sensitiveKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
} catch {
XCTFail() // should not throw
}
do {
try sensitiveKeyStore.changePassword(from: "wrong", to: "random", useBiometry: false)
XCTFail() // should've thrown
} catch {
// check for correct exception?
}
}
func testBiometryOnly() throws {
guard simulatorCheck() else {
throw XCTSkip("Test not supported on simulator")
}
XCTAssertEqual(sensitiveKeyStore.isInitialized(), false)
do {
try sensitiveKeyStore.initializeKeyStore()
try sensitiveKeyStore.changePassword(from: nil, to: nil, useBiometry: true)
// check ?
try sensitiveKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "test123", to: "random", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "random", to: nil, useBiometry: false)
} catch {
XCTFail() // should not throw
}
}
func testImportDataKey() throws {
let randomKey = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c322") as Data
try dataKeyStore.initializeKeyStore()
try dataKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomKey)
let result = try dataKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result?.toHexString(), randomKey.toHexString())
}
func testImportDataAndSensitiveKeyStore() throws {
let randomSensitiveKey = Data(ethHex: "da18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c322") as Data
try sensitiveKeyStore.initializeKeyStore()
let randomDataKey = Data(ethHex: "cb18066dda40499e6ef67a392eda0fd90acf804448a765db9fa9b6e7dd15c321") as Data
try dataKeyStore.initializeKeyStore()
try sensitiveKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomSensitiveKey)
try dataKeyStore.import(id: DataID(id:"0xE86935943315293154c7AD63296b4e1adAc76364"), data: randomDataKey)
let result = try sensitiveKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result?.toHexString(), randomSensitiveKey.toHexString())
let result2 = try dataKeyStore.find(dataID: DataID(id: "0xE86935943315293154c7AD63296b4e1adAc76364"), password: nil)
XCTAssertEqual(result2?.toHexString(), randomDataKey.toHexString())
}
func testChangePasswordDataAndSensitiveStore() {
XCTAssertEqual(sensitiveKeyStore.isInitialized(), false)
do {
try sensitiveKeyStore.initializeKeyStore()
try dataKeyStore.initializeKeyStore()
try sensitiveKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
try dataKeyStore.changePassword(from: nil, to: "test123", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "test123", to: "random", useBiometry: false)
try dataKeyStore.changePassword(from: "test123", to: "random", useBiometry: false)
try sensitiveKeyStore.changePassword(from: "random", to: nil, useBiometry: false)
try dataKeyStore.changePassword(from: "random", to: nil, useBiometry: false)
} catch {
XCTFail() // should not throw
}
}
private func simulatorCheck() -> Bool {
LAContext().setCredential("anyPassword".data(using: .utf8), type: .applicationPassword)
}
}