PGPInterface can encrypt with multiple keys, PGPAgent can encrypt with all keys

This commit is contained in:
Lysann Tranvouez 2026-03-11 00:21:30 +01:00
parent 8d4f3af475
commit 84eaf4ad7d
7 changed files with 158 additions and 19 deletions

View file

@ -123,26 +123,43 @@ struct GopenPGPInterface: PGPInterface {
}
}
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
func encrypt(plainData: Data, keyID: String?) throws -> Data {
let key: CryptoKey? = {
if let keyID {
return publicKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) })?.value
}
return publicKeys.first?.value
}()
guard let keyID = keyID ?? publicKeys.keys.first else {
// this is invalid, but we want the new function to throw the error for us
return try encrypt(plainData: plainData, keyIDs: [])
}
return try encrypt(plainData: plainData, keyIDs: [keyID])
}
guard let publicKey = key else {
func encryptWithAllKeys(plainData: Data) throws -> Data {
let keyIDs = publicKeys.keys.filter { key in privateKeys.keys.contains(key) }
return try encrypt(plainData: plainData, keyIDs: keyIDs)
}
func encrypt(plainData: Data, keyIDs: [String]) throws -> Data {
let keys: [CryptoKey] = keyIDs.compactMap { keyID in
publicKeys.first(where: { key, _ in key.hasSuffix(keyID.lowercased()) })?.value
}
guard let firstKey = keys.first else {
throw AppError.encryption
}
let otherKeys = keys.dropFirst()
var error: NSError?
guard let keyRing = CryptoNewKeyRing(publicKey, &error) else {
guard let keyRing = CryptoNewKeyRing(firstKey, &error) else {
guard error == nil else {
throw error!
}
throw AppError.encryption
}
do {
try otherKeys.forEach { key in
try keyRing.add(key)
}
} catch {
throw AppError.encryption
}
let encryptedData = try keyRing.encrypt(CryptoNewPlainMessage(plainData.mutable as Data), privateKey: nil)
if Defaults.encryptInArmored {

View file

@ -33,8 +33,25 @@ struct ObjectivePGPInterface: PGPInterface {
}
}
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
func encrypt(plainData: Data, keyID _: String?) throws -> Data {
let encryptedData = try ObjectivePGP.encrypt(plainData, addSignature: false, using: keyring.keys, passphraseForKey: nil)
// Backwards compatibility: ignore keyID parameter and encrypted with all keys in the keyring
try encryptWithAllKeys(plainData: plainData)
}
func encryptWithAllKeys(plainData: Data) throws -> Data {
try encrypt(plainData: plainData, keyIDs: keyID)
}
func encrypt(plainData: Data, keyIDs: [String]) throws -> Data {
let keys = try keyIDs.map { keyID in
guard let key = keyring.findKey(keyID) else {
throw AppError.pgpPublicKeyNotFound(keyID: keyID)
}
return key
}
let encryptedData = try ObjectivePGP.encrypt(plainData, addSignature: false, using: keys, passphraseForKey: nil)
if Defaults.encryptInArmored {
return Armor.armored(encryptedData, as: .message).data(using: .ascii)!
}

View file

@ -102,7 +102,7 @@ public class PGPAgent {
throw AppError.pgpPublicKeyNotFound(keyID: keyID)
}
}
return try pgpInterface.encrypt(plainData: plainData, keyID: keyID)
return try pgpInterface.encrypt(plainData: plainData, keyIDs: [keyID])
}
public func decrypt(encryptedData: Data, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? {
@ -127,6 +127,7 @@ public class PGPAgent {
return result
}
@available(*, deprecated, message: "Use encrypt(plainData:keyID:) or encryptWithAllKeys(plainData:) instead.")
public func encrypt(plainData: Data) throws -> Data {
try checkAndInit()
guard let pgpInterface else {
@ -135,6 +136,14 @@ public class PGPAgent {
return try pgpInterface.encrypt(plainData: plainData, keyID: nil)
}
public func encryptWithAllKeys(plainData: Data) throws -> Data {
try checkAndInit()
guard let pgpInterface else {
throw AppError.encryption
}
return try pgpInterface.encryptWithAllKeys(plainData: plainData)
}
public var isPrepared: Bool {
keyStore.contains(key: PGPKey.PUBLIC.getKeychainKey())
&& keyStore.contains(key: PGPKey.PRIVATE.getKeychainKey())

View file

@ -9,7 +9,11 @@
protocol PGPInterface {
func decrypt(encryptedData: Data, keyIDHint: String?, passPhraseForKey: @escaping (String) -> String) throws -> Data?
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
func encrypt(plainData: Data, keyID: String?) throws -> Data
// encrypt with all public keys for which we also have a private key
func encryptWithAllKeys(plainData: Data) throws -> Data
func encrypt(plainData: Data, keyIDs: [String]) throws -> Data
func containsPublicKey(with keyID: String) -> Bool