Move Crypto functionality back to passKit
This commit is contained in:
parent
6ae506d7e6
commit
56b7b24fce
5 changed files with 1 additions and 1 deletions
|
|
@ -1,142 +0,0 @@
|
|||
//
|
||||
// GopenPGPInterface.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 08.09.19.
|
||||
// Copyright © 2019 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import Crypto
|
||||
|
||||
struct GopenPGPInterface: PGPInterface {
|
||||
private static let errorMapping: [String: Error] = [
|
||||
"gopenpgp: error in unlocking key: openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
|
||||
"openpgp: incorrect key": AppError.KeyExpiredOrIncompatible,
|
||||
]
|
||||
|
||||
private var publicKeys: [String: CryptoKey] = [:]
|
||||
private var privateKeys: [String: CryptoKey] = [:]
|
||||
|
||||
init(publicArmoredKey: String, privateArmoredKey: String) throws {
|
||||
let pubKeys = extractKeysFromArmored(str: publicArmoredKey)
|
||||
let prvKeys = extractKeysFromArmored(str: privateArmoredKey)
|
||||
|
||||
for key in pubKeys {
|
||||
var error: NSError?
|
||||
guard let k = CryptoNewKeyFromArmored(key, &error) else {
|
||||
guard error == nil else {
|
||||
throw error!
|
||||
}
|
||||
throw AppError.KeyImport
|
||||
}
|
||||
publicKeys[k.getFingerprint().lowercased()] = k
|
||||
}
|
||||
|
||||
for key in prvKeys {
|
||||
var error: NSError?
|
||||
guard let k = CryptoNewKeyFromArmored(key, &error) else {
|
||||
guard error == nil else {
|
||||
throw error!
|
||||
}
|
||||
throw AppError.KeyImport
|
||||
}
|
||||
privateKeys[k.getFingerprint().lowercased()] = k
|
||||
}
|
||||
}
|
||||
|
||||
func extractKeysFromArmored(str: String) -> [String] {
|
||||
var keys: [String] = []
|
||||
var key: String = ""
|
||||
for line in str.splitByNewline() {
|
||||
if line.trimmed.uppercased().hasPrefix("-----BEGIN PGP") {
|
||||
key = ""
|
||||
key += line
|
||||
} else if line.trimmed.uppercased().hasPrefix("-----END PGP") {
|
||||
key += line
|
||||
keys.append(key)
|
||||
} else {
|
||||
key += line
|
||||
}
|
||||
key += "\n"
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func containsPublicKey(with keyID: String) -> Bool {
|
||||
publicKeys.keys.contains { key in key.hasSuffix(keyID.lowercased()) }
|
||||
}
|
||||
|
||||
func containsPrivateKey(with keyID: String) -> Bool {
|
||||
privateKeys.keys.contains { key in key.hasSuffix(keyID.lowercased()) }
|
||||
}
|
||||
|
||||
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
|
||||
guard let e = privateKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) }),
|
||||
let privateKey = privateKeys[e.key] else {
|
||||
throw AppError.Decryption
|
||||
}
|
||||
|
||||
do {
|
||||
let unlockedKey = try privateKey.unlock(passphrase.data(using: .utf8))
|
||||
var error: NSError?
|
||||
|
||||
guard let keyRing = CryptoNewKeyRing(unlockedKey, &error) else {
|
||||
guard error == nil else {
|
||||
throw error!
|
||||
}
|
||||
throw AppError.Decryption
|
||||
}
|
||||
|
||||
let message = createPgpMessage(from: encryptedData)
|
||||
return try keyRing.decrypt(message, verifyKey: nil, verifyTime: 0).data
|
||||
} catch {
|
||||
throw Self.errorMapping[error.localizedDescription, default: error]
|
||||
}
|
||||
}
|
||||
|
||||
func encrypt(plainData: Data, keyID: String) throws -> Data {
|
||||
guard let e = publicKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) }),
|
||||
let publicKey = publicKeys[e.key] else {
|
||||
throw AppError.Encryption
|
||||
}
|
||||
|
||||
var error: NSError?
|
||||
|
||||
guard let keyRing = CryptoNewKeyRing(publicKey, &error) else {
|
||||
guard error == nil else {
|
||||
throw error!
|
||||
}
|
||||
throw AppError.Encryption
|
||||
}
|
||||
|
||||
let encryptedData = try keyRing.encrypt(CryptoNewPlainMessage(plainData.mutable as Data), privateKey: nil)
|
||||
if Defaults.encryptInArmored {
|
||||
var error: NSError?
|
||||
let armor = encryptedData.getArmored(&error)
|
||||
guard error == nil else {
|
||||
throw error!
|
||||
}
|
||||
return armor.data(using: .ascii)!
|
||||
}
|
||||
return encryptedData.getBinary()!
|
||||
}
|
||||
|
||||
var keyID: [String] {
|
||||
publicKeys.keys.map { $0.uppercased() }
|
||||
}
|
||||
|
||||
var shortKeyID: [String] {
|
||||
publicKeys.keys.map { $0.suffix(8).uppercased() }
|
||||
}
|
||||
|
||||
private func createPgpMessage(from encryptedData: Data) -> CryptoPGPMessage? {
|
||||
// Important note:
|
||||
// Even if Defaults.encryptInArmored is true now, it could be different during the encryption.
|
||||
var error: NSError?
|
||||
let message = CryptoNewPGPMessageFromArmored(String(data: encryptedData, encoding: .ascii), &error)
|
||||
if error == nil {
|
||||
return message
|
||||
}
|
||||
return CryptoNewPGPMessage(encryptedData.mutable as Data)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// ObjectivePGPInterface.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 08.09.19.
|
||||
// Copyright © 2019 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import ObjectivePGP
|
||||
|
||||
struct ObjectivePGPInterface: PGPInterface {
|
||||
private let publicKey: Key
|
||||
private let privateKey: Key
|
||||
|
||||
private let keyring = ObjectivePGP.defaultKeyring
|
||||
|
||||
init(publicArmoredKey: String, privateArmoredKey: String) throws {
|
||||
guard let publicKeyData = publicArmoredKey.data(using: .ascii), let privateKeyData = privateArmoredKey.data(using: .ascii) else {
|
||||
throw AppError.KeyImport
|
||||
}
|
||||
let publicKeys = try ObjectivePGP.readKeys(from: publicKeyData)
|
||||
let privateKeys = try ObjectivePGP.readKeys(from: privateKeyData)
|
||||
keyring.import(keys: publicKeys)
|
||||
keyring.import(keys: privateKeys)
|
||||
guard let publicKey = publicKeys.first, let privateKey = privateKeys.first else {
|
||||
throw AppError.KeyImport
|
||||
}
|
||||
self.publicKey = publicKey
|
||||
self.privateKey = privateKey
|
||||
}
|
||||
|
||||
func decrypt(encryptedData: Data, keyID _: String, passphrase: String) throws -> Data? {
|
||||
try ObjectivePGP.decrypt(encryptedData, andVerifySignature: false, using: keyring.keys) { _ in passphrase }
|
||||
}
|
||||
|
||||
func encrypt(plainData: Data, keyID _: String) throws -> Data {
|
||||
let encryptedData = try ObjectivePGP.encrypt(plainData, addSignature: false, using: keyring.keys, passphraseForKey: nil)
|
||||
if Defaults.encryptInArmored {
|
||||
return Armor.armored(encryptedData, as: .message).data(using: .ascii)!
|
||||
}
|
||||
return encryptedData
|
||||
}
|
||||
|
||||
func containsPublicKey(with keyID: String) -> Bool {
|
||||
keyring.findKey(keyID)?.isPublic ?? false
|
||||
}
|
||||
|
||||
func containsPrivateKey(with keyID: String) -> Bool {
|
||||
keyring.findKey(keyID)?.isSecret ?? false
|
||||
}
|
||||
|
||||
var keyID: [String] {
|
||||
keyring.keys.map(\.keyID.longIdentifier)
|
||||
}
|
||||
|
||||
var shortKeyID: [String] {
|
||||
keyring.keys.map(\.keyID.shortIdentifier)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//
|
||||
// PGPAgent.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Yishi Lin on 2019/7/17.
|
||||
// Copyright © 2019 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
public class PGPAgent {
|
||||
public static let shared = PGPAgent()
|
||||
|
||||
private let keyStore: KeyStore
|
||||
private var pgpInterface: PGPInterface?
|
||||
private var latestDecryptStatus: Bool = true
|
||||
|
||||
public init(keyStore: KeyStore = AppKeychain.shared) {
|
||||
self.keyStore = keyStore
|
||||
}
|
||||
|
||||
public func initKeys() throws {
|
||||
guard let publicKey: String = keyStore.get(for: PgpKey.PUBLIC.getKeychainKey()),
|
||||
let privateKey: String = keyStore.get(for: PgpKey.PRIVATE.getKeychainKey()) else {
|
||||
pgpInterface = nil
|
||||
throw AppError.KeyImport
|
||||
}
|
||||
do {
|
||||
pgpInterface = try GopenPGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
|
||||
} catch {
|
||||
pgpInterface = try ObjectivePGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
|
||||
}
|
||||
}
|
||||
|
||||
public func uninitKeys() {
|
||||
pgpInterface = nil
|
||||
}
|
||||
|
||||
public func getKeyID() throws -> [String] {
|
||||
try checkAndInit()
|
||||
return pgpInterface?.keyID ?? []
|
||||
}
|
||||
|
||||
public func getShortKeyID() throws -> [String] {
|
||||
try checkAndInit()
|
||||
return pgpInterface?.shortKeyID.sorted() ?? []
|
||||
}
|
||||
|
||||
public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? {
|
||||
// Init keys.
|
||||
try checkAndInit()
|
||||
guard let pgpInterface = pgpInterface else {
|
||||
throw AppError.Decryption
|
||||
}
|
||||
|
||||
var keyID = keyID
|
||||
if !pgpInterface.containsPrivateKey(with: keyID) {
|
||||
if pgpInterface.keyID.count == 1 {
|
||||
keyID = pgpInterface.keyID.first!
|
||||
} else {
|
||||
throw AppError.PgpPrivateKeyNotFound(keyID: keyID)
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the previous status and set the current status
|
||||
let previousDecryptStatus = latestDecryptStatus
|
||||
latestDecryptStatus = false
|
||||
|
||||
// Get the PGP key passphrase.
|
||||
var passphrase = ""
|
||||
if previousDecryptStatus == false {
|
||||
passphrase = requestPGPKeyPassphrase(keyID)
|
||||
} else {
|
||||
passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID)
|
||||
}
|
||||
// Decrypt.
|
||||
guard let result = try pgpInterface.decrypt(encryptedData: encryptedData, keyID: keyID, passphrase: passphrase) else {
|
||||
return nil
|
||||
}
|
||||
// The decryption step has succeed.
|
||||
latestDecryptStatus = true
|
||||
return result
|
||||
}
|
||||
|
||||
public func encrypt(plainData: Data, keyID: String) throws -> Data {
|
||||
try checkAndInit()
|
||||
guard let pgpInterface = pgpInterface else {
|
||||
throw AppError.Encryption
|
||||
}
|
||||
var keyID = keyID
|
||||
if !pgpInterface.containsPublicKey(with: keyID) {
|
||||
if pgpInterface.keyID.count == 1 {
|
||||
keyID = pgpInterface.keyID.first!
|
||||
} else {
|
||||
throw AppError.PgpPublicKeyNotFound(keyID: keyID)
|
||||
}
|
||||
}
|
||||
return try pgpInterface.encrypt(plainData: plainData, keyID: keyID)
|
||||
}
|
||||
|
||||
public var isPrepared: Bool {
|
||||
keyStore.contains(key: PgpKey.PUBLIC.getKeychainKey())
|
||||
&& keyStore.contains(key: PgpKey.PRIVATE.getKeychainKey())
|
||||
}
|
||||
|
||||
private func checkAndInit() throws {
|
||||
if pgpInterface == nil || !keyStore.contains(key: Globals.pgpKeyPassphrase) {
|
||||
try initKeys()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
//
|
||||
// PGPInterface.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 08.09.19.
|
||||
// Copyright © 2019 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
protocol PGPInterface {
|
||||
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data?
|
||||
|
||||
func encrypt(plainData: Data, keyID: String) throws -> Data
|
||||
|
||||
func containsPublicKey(with keyID: String) -> Bool
|
||||
|
||||
func containsPrivateKey(with keyID: String) -> Bool
|
||||
|
||||
var keyID: [String] { get }
|
||||
|
||||
var shortKeyID: [String] { get }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue