2019-09-08 23:00:46 +02:00
|
|
|
//
|
|
|
|
|
// PGPAgent.swift
|
|
|
|
|
// passKit
|
|
|
|
|
//
|
|
|
|
|
// Created by Yishi Lin on 2019/7/17.
|
|
|
|
|
// Copyright © 2019 Bob Sun. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
public class PGPAgent {
|
2020-07-05 22:59:03 +02:00
|
|
|
public static let shared = PGPAgent()
|
2019-09-08 23:00:46 +02:00
|
|
|
|
|
|
|
|
private let keyStore: KeyStore
|
2020-04-19 15:41:30 +02:00
|
|
|
private var pgpInterface: PGPInterface?
|
2021-03-06 15:26:42 +01:00
|
|
|
private var latestDecryptStatus = true
|
2019-09-08 23:00:46 +02:00
|
|
|
|
|
|
|
|
public init(keyStore: KeyStore = AppKeychain.shared) {
|
|
|
|
|
self.keyStore = keyStore
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 11:36:36 +01:00
|
|
|
init(keyStore: KeyStore, pgpInterface: PGPInterface) {
|
|
|
|
|
self.keyStore = keyStore
|
|
|
|
|
self.pgpInterface = pgpInterface
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-08 23:00:46 +02:00
|
|
|
public func initKeys() throws {
|
2021-12-28 02:57:11 +01:00
|
|
|
guard let publicKey: String = keyStore.get(for: PGPKey.PUBLIC.getKeychainKey()),
|
|
|
|
|
let privateKey: String = keyStore.get(for: PGPKey.PRIVATE.getKeychainKey()) else {
|
2019-10-01 00:40:33 +08:00
|
|
|
pgpInterface = nil
|
2020-09-20 15:07:18 +02:00
|
|
|
throw AppError.keyImport
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
do {
|
2020-04-19 15:41:30 +02:00
|
|
|
pgpInterface = try GopenPGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
|
2019-09-08 23:00:46 +02:00
|
|
|
} catch {
|
2020-04-19 15:41:30 +02:00
|
|
|
pgpInterface = try ObjectivePGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func uninitKeys() {
|
|
|
|
|
pgpInterface = nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 00:34:34 +01:00
|
|
|
public func isInitialized() -> Bool {
|
|
|
|
|
pgpInterface != nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-13 19:15:52 -07:00
|
|
|
public func getKeyID() throws -> [String] {
|
2019-10-01 01:19:41 +08:00
|
|
|
try checkAndInit()
|
2020-04-13 19:15:52 -07:00
|
|
|
return pgpInterface?.keyID ?? []
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-13 19:15:52 -07:00
|
|
|
public func getShortKeyID() throws -> [String] {
|
2020-04-11 23:23:38 -07:00
|
|
|
try checkAndInit()
|
2020-04-17 23:56:14 -07:00
|
|
|
return pgpInterface?.shortKeyID.sorted() ?? []
|
2020-04-11 23:23:38 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-18 22:35:17 -07:00
|
|
|
public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? {
|
2019-09-30 02:05:01 +08:00
|
|
|
// Init keys.
|
2019-09-08 23:00:46 +02:00
|
|
|
try checkAndInit()
|
2023-04-23 22:01:37 +02:00
|
|
|
guard let pgpInterface else {
|
2020-09-20 15:07:18 +02:00
|
|
|
throw AppError.decryption
|
2020-04-14 20:20:16 -07:00
|
|
|
}
|
|
|
|
|
|
2020-06-28 21:25:40 +02:00
|
|
|
var keyID = keyID
|
2020-04-14 20:20:16 -07:00
|
|
|
if !pgpInterface.containsPrivateKey(with: keyID) {
|
2020-04-18 22:35:17 -07:00
|
|
|
if pgpInterface.keyID.count == 1 {
|
|
|
|
|
keyID = pgpInterface.keyID.first!
|
|
|
|
|
} else {
|
2020-09-20 15:07:18 +02:00
|
|
|
throw AppError.pgpPrivateKeyNotFound(keyID: keyID)
|
2020-04-18 22:35:17 -07:00
|
|
|
}
|
2020-04-14 20:20:16 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-17 23:56:14 -07:00
|
|
|
// Remember the previous status and set the current status
|
2020-06-28 21:25:40 +02:00
|
|
|
let previousDecryptStatus = latestDecryptStatus
|
|
|
|
|
latestDecryptStatus = false
|
2020-04-17 23:56:14 -07:00
|
|
|
|
2019-09-30 02:05:01 +08:00
|
|
|
// Get the PGP key passphrase.
|
2026-03-10 17:14:11 +01:00
|
|
|
let providePassPhraseForKey = { (selectedKeyID: String) -> String in
|
|
|
|
|
if previousDecryptStatus == false {
|
|
|
|
|
return requestPGPKeyPassphrase(selectedKeyID)
|
|
|
|
|
}
|
|
|
|
|
return self.keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: selectedKeyID)) ?? requestPGPKeyPassphrase(selectedKeyID)
|
2019-09-30 02:05:01 +08:00
|
|
|
}
|
|
|
|
|
// Decrypt.
|
2026-03-10 22:16:42 +01:00
|
|
|
guard let result = try pgpInterface.decrypt(encryptedData: encryptedData, keyIDHint: keyID, passPhraseForKey: providePassPhraseForKey) else {
|
2019-09-30 02:05:01 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
// The decryption step has succeed.
|
2020-06-28 21:25:40 +02:00
|
|
|
latestDecryptStatus = true
|
2019-09-30 02:05:01 +08:00
|
|
|
return result
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-13 01:30:00 -07:00
|
|
|
public func encrypt(plainData: Data, keyID: String) throws -> Data {
|
2019-09-08 23:00:46 +02:00
|
|
|
try checkAndInit()
|
2023-04-23 22:01:37 +02:00
|
|
|
guard let pgpInterface else {
|
2020-09-20 15:07:18 +02:00
|
|
|
throw AppError.encryption
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
2020-04-18 23:21:50 -07:00
|
|
|
var keyID = keyID
|
2020-04-14 20:20:16 -07:00
|
|
|
if !pgpInterface.containsPublicKey(with: keyID) {
|
2020-04-18 23:21:50 -07:00
|
|
|
if pgpInterface.keyID.count == 1 {
|
|
|
|
|
keyID = pgpInterface.keyID.first!
|
|
|
|
|
} else {
|
2020-09-20 15:07:18 +02:00
|
|
|
throw AppError.pgpPublicKeyNotFound(keyID: keyID)
|
2020-04-18 23:21:50 -07:00
|
|
|
}
|
2020-04-14 20:20:16 -07:00
|
|
|
}
|
2020-04-13 01:30:00 -07:00
|
|
|
return try pgpInterface.encrypt(plainData: plainData, keyID: keyID)
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-10 17:14:11 +01:00
|
|
|
public func decrypt(encryptedData: Data, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? {
|
2021-01-07 21:58:38 -08:00
|
|
|
// Remember the previous status and set the current status
|
2021-01-31 13:17:37 +01:00
|
|
|
let previousDecryptStatus = latestDecryptStatus
|
|
|
|
|
latestDecryptStatus = false
|
2021-01-07 21:58:38 -08:00
|
|
|
// Init keys.
|
|
|
|
|
try checkAndInit()
|
|
|
|
|
// Get the PGP key passphrase.
|
2026-03-10 17:14:11 +01:00
|
|
|
let providePassPhraseForKey = { (selectedKeyID: String) -> String in
|
|
|
|
|
if previousDecryptStatus == false {
|
|
|
|
|
return requestPGPKeyPassphrase(selectedKeyID)
|
|
|
|
|
}
|
|
|
|
|
return self.keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: selectedKeyID)) ?? requestPGPKeyPassphrase(selectedKeyID)
|
2021-01-07 21:58:38 -08:00
|
|
|
}
|
|
|
|
|
// Decrypt.
|
2026-03-10 22:16:42 +01:00
|
|
|
guard let result = try pgpInterface!.decrypt(encryptedData: encryptedData, keyIDHint: nil, passPhraseForKey: providePassPhraseForKey) else {
|
2021-01-07 21:58:38 -08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
// The decryption step has succeed.
|
2021-01-31 13:17:37 +01:00
|
|
|
latestDecryptStatus = true
|
2021-01-07 21:58:38 -08:00
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func encrypt(plainData: Data) throws -> Data {
|
|
|
|
|
try checkAndInit()
|
2023-04-23 22:01:37 +02:00
|
|
|
guard let pgpInterface else {
|
2021-01-07 21:58:38 -08:00
|
|
|
throw AppError.encryption
|
|
|
|
|
}
|
|
|
|
|
return try pgpInterface.encrypt(plainData: plainData, keyID: nil)
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-08 23:00:46 +02:00
|
|
|
public var isPrepared: Bool {
|
2021-12-28 02:57:11 +01:00
|
|
|
keyStore.contains(key: PGPKey.PUBLIC.getKeychainKey())
|
|
|
|
|
&& keyStore.contains(key: PGPKey.PRIVATE.getKeychainKey())
|
2019-09-08 23:00:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func checkAndInit() throws {
|
|
|
|
|
if pgpInterface == nil || !keyStore.contains(key: Globals.pgpKeyPassphrase) {
|
|
|
|
|
try initKeys()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|