diff --git a/pass/en.lproj/Localizable.strings b/pass/en.lproj/Localizable.strings index 6690aa5..e363442 100644 --- a/pass/en.lproj/Localizable.strings +++ b/pass/en.lproj/Localizable.strings @@ -136,6 +136,8 @@ "PgpCopyPublicAndPrivateKeyToPass." = "Copy your ASCII-armored public and private keys to Pass with names \"gpg_key.pub\" and \"gpg_key\" (without quotes) via iTunes. Then come back and click \"iTunes File Sharing\" to finish."; "KeyExpiredOrIncompatibleError." = "PGP public key may be expired or incompatible with the private key."; "WrongPassphraseError." = "Passphrase of your PGP secret key is wrong."; +"PgpPublicKeyNotFoundError." = "PGP public key (%@) not found."; +"PgpPrivateKeyNotFoundError." = "PGP private key (%@) not found."; "CannotImportFile" = "Cannot Import File"; "LoadFromFiles" = "Load From File"; "FileCannotBeImported." = "An error occurred importing the file '%@'. Please make sure its location is readable."; diff --git a/passKit/Crypto/GopenPgp.swift b/passKit/Crypto/GopenPgp.swift index 589352d..2e9e98b 100644 --- a/passKit/Crypto/GopenPgp.swift +++ b/passKit/Crypto/GopenPgp.swift @@ -30,7 +30,7 @@ struct GopenPgp: PgpInterface { } throw AppError.KeyImport } - publicKeys[k.getFingerprint()] = k + publicKeys[k.getFingerprint().lowercased()] = k } for key in prvKeys { @@ -41,7 +41,7 @@ struct GopenPgp: PgpInterface { } throw AppError.KeyImport } - privateKeys[k.getFingerprint()] = k + privateKeys[k.getFingerprint().lowercased()] = k } } @@ -64,6 +64,14 @@ struct GopenPgp: PgpInterface { return keys } + func containsPublicKey(with keyID: String) -> Bool { + publicKeys.keys.contains(where: { key in key.hasSuffix(keyID.lowercased()) }) + } + + func containsPrivateKey(with keyID: String) -> Bool { + privateKeys.keys.contains(where: { 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 { diff --git a/passKit/Crypto/ObjectivePgp.swift b/passKit/Crypto/ObjectivePgp.swift index 8262867..af25465 100644 --- a/passKit/Crypto/ObjectivePgp.swift +++ b/passKit/Crypto/ObjectivePgp.swift @@ -42,6 +42,14 @@ struct ObjectivePgp: PgpInterface { 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] { return keyring.keys.map({ $0.keyID.longIdentifier }) } diff --git a/passKit/Crypto/PGPAgent.swift b/passKit/Crypto/PGPAgent.swift index cdd8430..a3a5002 100644 --- a/passKit/Crypto/PGPAgent.swift +++ b/passKit/Crypto/PGPAgent.swift @@ -51,6 +51,15 @@ public class PGPAgent { self.latestDecryptStatus = false // Init keys. try checkAndInit() + + guard let pgpInterface = pgpInterface else { + throw AppError.Decryption + } + + if !pgpInterface.containsPrivateKey(with: keyID) { + throw AppError.PgpPrivateKeyNotFound(keyID: keyID) + } + // Get the PGP key passphrase. var passphrase = "" if previousDecryptStatus == false { @@ -59,7 +68,7 @@ public class PGPAgent { passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID) } // Decrypt. - guard let result = try pgpInterface!.decrypt(encryptedData: encryptedData, keyID: keyID, passphrase: passphrase) else { + guard let result = try pgpInterface.decrypt(encryptedData: encryptedData, keyID: keyID, passphrase: passphrase) else { return nil } // The decryption step has succeed. @@ -72,6 +81,9 @@ public class PGPAgent { guard let pgpInterface = pgpInterface else { throw AppError.Encryption } + if !pgpInterface.containsPublicKey(with: keyID) { + throw AppError.PgpPublicKeyNotFound(keyID: keyID) + } return try pgpInterface.encrypt(plainData: plainData, keyID: keyID) } diff --git a/passKit/Crypto/PgpInterface.swift b/passKit/Crypto/PgpInterface.swift index 2a3af66..db4c320 100644 --- a/passKit/Crypto/PgpInterface.swift +++ b/passKit/Crypto/PgpInterface.swift @@ -12,6 +12,10 @@ protocol PgpInterface { 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 } diff --git a/passKit/Helpers/AppError.swift b/passKit/Helpers/AppError.swift index 5c183d6..ad5ca64 100644 --- a/passKit/Helpers/AppError.swift +++ b/passKit/Helpers/AppError.swift @@ -16,7 +16,8 @@ public enum AppError: Error, Equatable { case GitReset case GitCommit case PasswordEntity - case PgpPublicKeyNotExist + case PgpPublicKeyNotFound(keyID: String) + case PgpPrivateKeyNotFound(keyID: String) case KeyExpiredOrIncompatible case WrongPassphrase case WrongPasswordFilename @@ -32,6 +33,8 @@ extension AppError: LocalizedError { switch self { case let .RepositoryRemoteBranchNotFound(name), let .RepositoryBranchNotFound(name), let .ReadingFile(name): return localizationKey.localize(name) + case let .PgpPublicKeyNotFound(keyID), let .PgpPrivateKeyNotFound(keyID): + return localizationKey.localize(keyID) default: return localizationKey.localize() }