add encrypt-save-decrypt roundtrip test
This commit is contained in:
parent
60999c7eab
commit
e5650ec756
21 changed files with 75 additions and 18 deletions
1
passKitTests/Fixtures/password-store-empty.git/HEAD
Normal file
1
passKitTests/Fixtures/password-store-empty.git/HEAD
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/heads/main
|
||||||
6
passKitTests/Fixtures/password-store-empty.git/config
Normal file
6
passKitTests/Fixtures/password-store-empty.git/config
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = true
|
||||||
|
ignorecase = true
|
||||||
|
precomposeunicode = true
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
||||||
|
x•ÍM
|
||||||
|
Â0@a×9Åì™ü<E284A2>¤ â\z<>i:Å@›BœzzKÕ¸}‹ï¥ež³€îôN*3Sôv¤48J=ÙÞEoØŽÎDÒˆÈk"EMnK…ËóN¥ÀµRy,<2C>_pœ¶r–_9p;<3B>Á¢‹ˆìqETÚ¾Âÿ
|
||||||
|
&ú¯ rÉ’i‚¥ÞÃ@í
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# pack-refs with: peeled fully-peeled sorted
|
||||||
|
f095bb4897e4cd58faadfe4d4f678fb697be3ffd refs/heads/main
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Example password store repository for passforios tests with .gpg-id files.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Example password store repository for passforios tests.
|
|
||||||
|
|
@ -13,7 +13,6 @@ import XCTest
|
||||||
@testable import passKit
|
@testable import passKit
|
||||||
|
|
||||||
final class PasswordStoreTest: XCTestCase {
|
final class PasswordStoreTest: XCTestCase {
|
||||||
private lazy var remoteRepoURL: URL = Bundle(for: type(of: self)).resourceURL!.appendingPathComponent("Fixtures/password-store.git")
|
|
||||||
private let localRepoURL: URL = Globals.sharedContainerURL.appendingPathComponent("Library/password-store-test/")
|
private let localRepoURL: URL = Globals.sharedContainerURL.appendingPathComponent("Library/password-store-test/")
|
||||||
|
|
||||||
private var passwordStore: PasswordStore! = nil
|
private var passwordStore: PasswordStore! = nil
|
||||||
|
|
@ -25,10 +24,12 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
passwordStore.erase()
|
passwordStore.erase()
|
||||||
passwordStore = nil
|
passwordStore = nil
|
||||||
|
|
||||||
|
Defaults.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitPasswordEntityCoreData() throws {
|
func testInitPasswordEntityCoreData() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
|
|
||||||
XCTAssertEqual(passwordStore.numberOfPasswords, 4)
|
XCTAssertEqual(passwordStore.numberOfPasswords, 4)
|
||||||
|
|
||||||
|
|
@ -50,7 +51,7 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEraseStoreData() throws {
|
func testEraseStoreData() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
XCTAssertTrue(FileManager.default.fileExists(atPath: localRepoURL.path))
|
XCTAssertTrue(FileManager.default.fileExists(atPath: localRepoURL.path))
|
||||||
XCTAssertGreaterThan(passwordStore.numberOfPasswords, 0)
|
XCTAssertGreaterThan(passwordStore.numberOfPasswords, 0)
|
||||||
XCTAssertNotNil(passwordStore.gitRepository)
|
XCTAssertNotNil(passwordStore.gitRepository)
|
||||||
|
|
@ -63,8 +64,8 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testErase() throws {
|
func testErase() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
try importPGPKeys()
|
try importSinglePGPKey()
|
||||||
Defaults.gitSignatureName = "Test User"
|
Defaults.gitSignatureName = "Test User"
|
||||||
PasscodeLock.shared.save(passcode: "1234")
|
PasscodeLock.shared.save(passcode: "1234")
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFetchPasswordEntityCoreDataByParent() throws {
|
func testFetchPasswordEntityCoreDataByParent() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
|
|
||||||
let rootChildren = passwordStore.fetchPasswordEntityCoreData(parent: nil)
|
let rootChildren = passwordStore.fetchPasswordEntityCoreData(parent: nil)
|
||||||
XCTAssertGreaterThan(rootChildren.count, 0)
|
XCTAssertGreaterThan(rootChildren.count, 0)
|
||||||
|
|
@ -99,7 +100,7 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testFetchPasswordEntityCoreDataWithDir() throws {
|
func testFetchPasswordEntityCoreDataWithDir() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
|
|
||||||
let allPasswords = passwordStore.fetchPasswordEntityCoreData(withDir: false)
|
let allPasswords = passwordStore.fetchPasswordEntityCoreData(withDir: false)
|
||||||
XCTAssertEqual(allPasswords.count, 4)
|
XCTAssertEqual(allPasswords.count, 4)
|
||||||
|
|
@ -108,14 +109,21 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testEncryptSaveDecryptMultiline() throws {
|
||||||
|
try cloneRepository(.empty)
|
||||||
|
try importSinglePGPKey()
|
||||||
|
|
||||||
|
let password = Password(name: "test", path: "test.gpg", plainText: "foobar\nwith\nmultiple\nlines")
|
||||||
|
_ = try passwordStore.add(password: password)
|
||||||
|
let decryptedPassword = try decrypt(path: "test.gpg")
|
||||||
|
XCTAssertEqual(decryptedPassword.plainText, "foobar\nwith\nmultiple\nlines")
|
||||||
|
}
|
||||||
|
|
||||||
func testCloneAndDecryptMultiKeys() throws {
|
func testCloneAndDecryptMultiKeys() throws {
|
||||||
try cloneRepository()
|
try cloneRepository(.withGPGID)
|
||||||
try importPGPKeys()
|
try importMultiplePGPKeys()
|
||||||
|
|
||||||
Defaults.isEnableGPGIDOn = true
|
Defaults.isEnableGPGIDOn = true
|
||||||
defer {
|
|
||||||
Defaults.isEnableGPGIDOn = false
|
|
||||||
}
|
|
||||||
|
|
||||||
[
|
[
|
||||||
("work/github.com", "4712286271220DB299883EA7062E678DA1024DAE"),
|
("work/github.com", "4712286271220DB299883EA7062E678DA1024DAE"),
|
||||||
|
|
@ -139,21 +147,51 @@ final class PasswordStoreTest: XCTestCase {
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func cloneRepository() throws {
|
private enum RemoteRepo {
|
||||||
try passwordStore.cloneRepository(remoteRepoURL: remoteRepoURL, branchName: "master")
|
case empty
|
||||||
|
case withGPGID
|
||||||
|
|
||||||
|
var url: URL {
|
||||||
|
switch self {
|
||||||
|
case .empty:
|
||||||
|
Bundle(for: PasswordStoreTest.self).resourceURL!.appendingPathComponent("Fixtures/password-store-empty.git")
|
||||||
|
case .withGPGID:
|
||||||
|
Bundle(for: PasswordStoreTest.self).resourceURL!.appendingPathComponent("Fixtures/password-store-with-gpgid.git")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var branchName: String {
|
||||||
|
switch self {
|
||||||
|
case .empty:
|
||||||
|
"main"
|
||||||
|
case .withGPGID:
|
||||||
|
"master"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cloneRepository(_ remote: RemoteRepo) throws {
|
||||||
|
try passwordStore.cloneRepository(remoteRepoURL: remote.url, branchName: remote.branchName)
|
||||||
expectation(for: NSPredicate { _, _ in FileManager.default.fileExists(atPath: self.localRepoURL.path) }, evaluatedWith: nil)
|
expectation(for: NSPredicate { _, _ in FileManager.default.fileExists(atPath: self.localRepoURL.path) }, evaluatedWith: nil)
|
||||||
waitForExpectations(timeout: 3, handler: nil)
|
waitForExpectations(timeout: 3, handler: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func importPGPKeys() throws {
|
private func importSinglePGPKey() throws {
|
||||||
|
let keychain = AppKeychain.shared
|
||||||
|
try KeyFileManager(keyType: PGPKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: RSA4096.publicKey)
|
||||||
|
try KeyFileManager(keyType: PGPKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: RSA4096.privateKey)
|
||||||
|
try PGPAgent.shared.initKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func importMultiplePGPKeys() throws {
|
||||||
let keychain = AppKeychain.shared
|
let keychain = AppKeychain.shared
|
||||||
try KeyFileManager(keyType: PGPKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: RSA2048_RSA4096.publicKeys)
|
try KeyFileManager(keyType: PGPKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: RSA2048_RSA4096.publicKeys)
|
||||||
try KeyFileManager(keyType: PGPKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: RSA2048_RSA4096.privateKeys)
|
try KeyFileManager(keyType: PGPKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: RSA2048_RSA4096.privateKeys)
|
||||||
try PGPAgent.shared.initKeys()
|
try PGPAgent.shared.initKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func decrypt(path: String) throws -> Password {
|
private func decrypt(path: String, keyID: String? = nil) throws -> Password {
|
||||||
let entity = passwordStore.fetchPasswordEntity(with: path)!
|
let entity = passwordStore.fetchPasswordEntity(with: path)!
|
||||||
return try passwordStore.decrypt(passwordEntity: entity, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
|
return try passwordStore.decrypt(passwordEntity: entity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue