2019-09-08 23:00:46 +02:00
|
|
|
//
|
|
|
|
|
// PGPAgent.swift
|
|
|
|
|
// passKitTests
|
|
|
|
|
//
|
|
|
|
|
// Created by Yishi Lin on 2019/7/17.
|
|
|
|
|
// Copyright © 2019 Bob Sun. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import XCTest
|
2019-09-30 00:12:54 +08:00
|
|
|
import SwiftyUserDefaults
|
2019-09-08 23:00:46 +02:00
|
|
|
|
|
|
|
|
@testable import passKit
|
|
|
|
|
|
|
|
|
|
class PGPAgentTest: XCTestCase {
|
|
|
|
|
private var keychain: KeyStore!
|
|
|
|
|
private var pgpAgent: PGPAgent!
|
|
|
|
|
|
|
|
|
|
private let testData = "Hello World!".data(using: .utf8)!
|
|
|
|
|
|
|
|
|
|
override func setUp() {
|
|
|
|
|
super.setUp()
|
|
|
|
|
keychain = DictBasedKeychain()
|
|
|
|
|
pgpAgent = PGPAgent(keyStore: keychain)
|
2019-09-30 00:12:54 +08:00
|
|
|
UserDefaults().removePersistentDomain(forName: "SharedDefaultsForPGPAgentTest")
|
2020-01-02 00:48:00 +01:00
|
|
|
passKit.Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: "SharedDefaultsForPGPAgentTest")!, keyStore: DefaultsKeys())
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override func tearDown() {
|
|
|
|
|
keychain.removeAllContent()
|
2019-09-30 00:12:54 +08:00
|
|
|
UserDefaults().removePersistentDomain(forName: "SharedDefaultsForPGPAgentTest")
|
2019-09-08 23:00:46 +02:00
|
|
|
super.tearDown()
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-13 10:25:01 -07:00
|
|
|
func basicEncryptDecrypt(using pgpAgent: PGPAgent, keyID: String, encryptKeyID: String? = nil, requestPassphrase: () -> String = requestPGPKeyPassphrase, encryptInArmored: Bool = true, encryptInArmoredNow: Bool = true) throws -> Data? {
|
2020-01-02 00:48:00 +01:00
|
|
|
passKit.Defaults.encryptInArmored = encryptInArmored
|
2020-04-13 01:30:00 -07:00
|
|
|
let encryptedData = try pgpAgent.encrypt(plainData: testData, keyID: keyID)
|
2020-01-02 00:48:00 +01:00
|
|
|
passKit.Defaults.encryptInArmored = encryptInArmoredNow
|
2020-04-13 10:25:01 -07:00
|
|
|
return try pgpAgent.decrypt(encryptedData: encryptedData, keyID: encryptKeyID ?? keyID, requestPGPKeyPassphrase: requestPassphrase)
|
2020-04-13 01:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testMultiKeys() throws {
|
|
|
|
|
try [
|
|
|
|
|
RSA2048_RSA4096
|
|
|
|
|
].forEach { keyTriple in
|
|
|
|
|
let keychain = DictBasedKeychain()
|
|
|
|
|
let pgpAgent = PGPAgent(keyStore: keychain)
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: keyTriple.publicKey)
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: keyTriple.privateKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
|
|
|
|
try pgpAgent.initKeys()
|
|
|
|
|
try [
|
|
|
|
|
(true, true), (true, false), (false, true), (false, false)
|
|
|
|
|
].forEach{ a, b in
|
|
|
|
|
for id in keyTriple.fingerprint {
|
|
|
|
|
XCTAssertEqual(try basicEncryptDecrypt(using: pgpAgent, keyID: id, encryptInArmored: a, encryptInArmoredNow: b), testData)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testBasicEncryptDecrypt() throws {
|
|
|
|
|
try [
|
|
|
|
|
RSA2048,
|
2020-04-12 18:24:03 -07:00
|
|
|
RSA2048_SUB,
|
2020-04-12 19:32:58 -07:00
|
|
|
RSA4096,
|
|
|
|
|
RSA4096_SUB,
|
2019-09-08 23:00:46 +02:00
|
|
|
ED25519,
|
2020-04-12 18:24:03 -07:00
|
|
|
ED25519_SUB,
|
2019-09-08 23:00:46 +02:00
|
|
|
].forEach { keyTriple in
|
|
|
|
|
let keychain = DictBasedKeychain()
|
|
|
|
|
let pgpAgent = PGPAgent(keyStore: keychain)
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: keyTriple.publicKey)
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: keyTriple.privateKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
|
|
|
|
try pgpAgent.initKeys()
|
2019-10-03 14:49:03 +08:00
|
|
|
XCTAssert(try pgpAgent.getKeyId()!.lowercased().hasSuffix(keyTriple.fingerprint))
|
2019-09-30 00:12:54 +08:00
|
|
|
try [
|
|
|
|
|
(true, true), (true, false), (false, true), (false, false)
|
|
|
|
|
].forEach{ a, b in
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertEqual(try basicEncryptDecrypt(using: pgpAgent, keyID: keyTriple.fingerprint, encryptInArmored: a, encryptInArmoredNow: b), testData)
|
2019-09-30 00:12:54 +08:00
|
|
|
}
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testNoPrivateKey() throws {
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: RSA2048.publicKey)
|
|
|
|
|
XCTAssertFalse(pgpAgent.isPrepared)
|
|
|
|
|
XCTAssertThrowsError(try pgpAgent.initKeys()) {
|
|
|
|
|
XCTAssertEqual($0 as! AppError, AppError.KeyImport)
|
|
|
|
|
}
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint)) {
|
2019-09-08 23:00:46 +02:00
|
|
|
XCTAssertEqual($0 as! AppError, AppError.KeyImport)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testInterchangePublicAndPrivateKey() throws {
|
|
|
|
|
try importKeys(RSA2048.privateKey, RSA2048.publicKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint)) {
|
2020-04-11 23:23:38 -07:00
|
|
|
XCTAssert($0.localizedDescription.contains("gopenpgp: unable to add locked key to a keyring"))
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testIncompatibleKeyTypes() throws {
|
|
|
|
|
try importKeys(ED25519.publicKey, RSA2048.privateKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
2020-04-13 10:25:01 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: ED25519.fingerprint, encryptKeyID: RSA2048.fingerprint)) {
|
2019-10-20 12:14:51 +02:00
|
|
|
XCTAssertEqual($0 as! AppError, AppError.KeyExpiredOrIncompatible)
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testCorruptedKey() throws {
|
|
|
|
|
try importKeys(RSA2048.publicKey.replacingOccurrences(of: "1", with: ""), RSA2048.privateKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint)) {
|
2019-09-08 23:00:46 +02:00
|
|
|
XCTAssert($0.localizedDescription.contains("Can't read keys. Invalid input."))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testUnsettKeys() throws {
|
|
|
|
|
try importKeys(ED25519.publicKey, ED25519.privateKey)
|
|
|
|
|
XCTAssert(pgpAgent.isPrepared)
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertEqual(try basicEncryptDecrypt(using: pgpAgent, keyID: ED25519.fingerprint), testData)
|
2019-09-08 23:00:46 +02:00
|
|
|
keychain.removeContent(for: PgpKey.PUBLIC.getKeychainKey())
|
|
|
|
|
keychain.removeContent(for: PgpKey.PRIVATE.getKeychainKey())
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: ED25519.fingerprint)) {
|
2019-09-08 23:00:46 +02:00
|
|
|
XCTAssertEqual($0 as! AppError, AppError.KeyImport)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-16 21:35:07 +02:00
|
|
|
func testNoDecryptionWithIncorrectPassphrase() throws {
|
|
|
|
|
try importKeys(RSA2048.publicKey, RSA2048.privateKey)
|
|
|
|
|
|
2019-09-30 02:05:01 +08:00
|
|
|
var passphraseRequestCalledCount = 0
|
2019-09-16 21:35:07 +02:00
|
|
|
let provideCorrectPassphrase: () -> String = {
|
2019-09-30 02:05:01 +08:00
|
|
|
passphraseRequestCalledCount = passphraseRequestCalledCount + 1
|
2019-09-16 21:35:07 +02:00
|
|
|
return requestPGPKeyPassphrase()
|
|
|
|
|
}
|
|
|
|
|
let provideIncorrectPassphrase: () -> String = {
|
2019-09-30 02:05:01 +08:00
|
|
|
passphraseRequestCalledCount = passphraseRequestCalledCount + 1
|
2019-09-16 21:35:07 +02:00
|
|
|
return "incorrect passphrase"
|
|
|
|
|
}
|
2019-09-30 02:05:01 +08:00
|
|
|
|
|
|
|
|
// Provide the correct passphrase.
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertEqual(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint, requestPassphrase: provideCorrectPassphrase), testData)
|
2019-09-30 02:05:01 +08:00
|
|
|
XCTAssertEqual(passphraseRequestCalledCount, 1)
|
|
|
|
|
|
|
|
|
|
// Provide the wrong passphrase.
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertThrowsError(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint, requestPassphrase: provideIncorrectPassphrase)) {
|
2019-10-20 12:14:51 +02:00
|
|
|
XCTAssertEqual($0 as! AppError, AppError.WrongPassphrase)
|
2019-09-16 21:35:07 +02:00
|
|
|
}
|
2019-09-30 02:05:01 +08:00
|
|
|
XCTAssertEqual(passphraseRequestCalledCount, 2)
|
|
|
|
|
|
|
|
|
|
// Ask for the passphrase because the previous decryption has failed.
|
2020-04-13 01:30:00 -07:00
|
|
|
XCTAssertEqual(try basicEncryptDecrypt(using: pgpAgent, keyID: RSA2048.fingerprint, requestPassphrase: provideCorrectPassphrase), testData)
|
2019-09-30 02:05:01 +08:00
|
|
|
XCTAssertEqual(passphraseRequestCalledCount, 3)
|
2019-09-16 21:35:07 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-08 23:00:46 +02:00
|
|
|
private func importKeys(_ publicKey: String, _ privateKey: String) throws {
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PUBLIC, keyPath: "", keyHandler: keychain.add).importKey(from: publicKey)
|
|
|
|
|
try KeyFileManager(keyType: PgpKey.PRIVATE, keyPath: "", keyHandler: keychain.add).importKey(from: privateKey)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|