Check existence of PGP keys before encrypt/decrypt

This commit is contained in:
Mingshen Sun 2020-04-14 20:20:16 -07:00
parent 50dec23b02
commit 0cae6af60d
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
6 changed files with 41 additions and 4 deletions

View file

@ -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."; "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."; "KeyExpiredOrIncompatibleError." = "PGP public key may be expired or incompatible with the private key.";
"WrongPassphraseError." = "Passphrase of your PGP secret key is wrong."; "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"; "CannotImportFile" = "Cannot Import File";
"LoadFromFiles" = "Load From File"; "LoadFromFiles" = "Load From File";
"FileCannotBeImported." = "An error occurred importing the file '%@'. Please make sure its location is readable."; "FileCannotBeImported." = "An error occurred importing the file '%@'. Please make sure its location is readable.";

View file

@ -30,7 +30,7 @@ struct GopenPgp: PgpInterface {
} }
throw AppError.KeyImport throw AppError.KeyImport
} }
publicKeys[k.getFingerprint()] = k publicKeys[k.getFingerprint().lowercased()] = k
} }
for key in prvKeys { for key in prvKeys {
@ -41,7 +41,7 @@ struct GopenPgp: PgpInterface {
} }
throw AppError.KeyImport throw AppError.KeyImport
} }
privateKeys[k.getFingerprint()] = k privateKeys[k.getFingerprint().lowercased()] = k
} }
} }
@ -64,6 +64,14 @@ struct GopenPgp: PgpInterface {
return keys 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? { func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
guard let e = privateKeys.first(where: { (key, _) in key.hasSuffix(keyID.lowercased()) }), guard let e = privateKeys.first(where: { (key, _) in key.hasSuffix(keyID.lowercased()) }),
let privateKey = privateKeys[e.key] else { let privateKey = privateKeys[e.key] else {

View file

@ -42,6 +42,14 @@ struct ObjectivePgp: PgpInterface {
return encryptedData 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] { var keyID: [String] {
return keyring.keys.map({ $0.keyID.longIdentifier }) return keyring.keys.map({ $0.keyID.longIdentifier })
} }

View file

@ -51,6 +51,15 @@ public class PGPAgent {
self.latestDecryptStatus = false self.latestDecryptStatus = false
// Init keys. // Init keys.
try checkAndInit() 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. // Get the PGP key passphrase.
var passphrase = "" var passphrase = ""
if previousDecryptStatus == false { if previousDecryptStatus == false {
@ -59,7 +68,7 @@ public class PGPAgent {
passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID) passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID)
} }
// Decrypt. // 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 return nil
} }
// The decryption step has succeed. // The decryption step has succeed.
@ -72,6 +81,9 @@ public class PGPAgent {
guard let pgpInterface = pgpInterface else { guard let pgpInterface = pgpInterface else {
throw AppError.Encryption throw AppError.Encryption
} }
if !pgpInterface.containsPublicKey(with: keyID) {
throw AppError.PgpPublicKeyNotFound(keyID: keyID)
}
return try pgpInterface.encrypt(plainData: plainData, keyID: keyID) return try pgpInterface.encrypt(plainData: plainData, keyID: keyID)
} }

View file

@ -12,6 +12,10 @@ protocol PgpInterface {
func encrypt(plainData: Data, keyID: 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 keyID: [String] { get }
var shortKeyID: [String] { get } var shortKeyID: [String] { get }

View file

@ -16,7 +16,8 @@ public enum AppError: Error, Equatable {
case GitReset case GitReset
case GitCommit case GitCommit
case PasswordEntity case PasswordEntity
case PgpPublicKeyNotExist case PgpPublicKeyNotFound(keyID: String)
case PgpPrivateKeyNotFound(keyID: String)
case KeyExpiredOrIncompatible case KeyExpiredOrIncompatible
case WrongPassphrase case WrongPassphrase
case WrongPasswordFilename case WrongPasswordFilename
@ -32,6 +33,8 @@ extension AppError: LocalizedError {
switch self { switch self {
case let .RepositoryRemoteBranchNotFound(name), let .RepositoryBranchNotFound(name), let .ReadingFile(name): case let .RepositoryRemoteBranchNotFound(name), let .RepositoryBranchNotFound(name), let .ReadingFile(name):
return localizationKey.localize(name) return localizationKey.localize(name)
case let .PgpPublicKeyNotFound(keyID), let .PgpPrivateKeyNotFound(keyID):
return localizationKey.localize(keyID)
default: default:
return localizationKey.localize() return localizationKey.localize()
} }