replace calls to deprecated function
changes PasswordStore.encrypt behavior when .gpg-id support is off
(default):
old:
* ignores passed in keyID
* encrypt with first public key in keychain (gopenPGP), or entire
keychain (ObjectivePGP)
new:
* honor passed in keyID
* encrypt with all keys in keychain
This commit is contained in:
parent
09b0b150ce
commit
e69e590e36
7 changed files with 11 additions and 110 deletions
|
|
@ -123,15 +123,6 @@ struct GopenPGPInterface: PGPInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
|
|
||||||
func encrypt(plainData: Data, keyID: String?) throws -> Data {
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
||||||
let keyIDs = publicKeys.keys.filter { key in privateKeys.keys.contains(key) }
|
let keyIDs = publicKeys.keys.filter { key in privateKeys.keys.contains(key) }
|
||||||
return try encrypt(plainData: plainData, keyIDs: keyIDs)
|
return try encrypt(plainData: plainData, keyIDs: keyIDs)
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,6 @@ struct ObjectivePGPInterface: PGPInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
|
|
||||||
func encrypt(plainData: Data, keyID _: String?) throws -> Data {
|
|
||||||
// Backwards compatibility: ignore keyID parameter and encrypted with all keys in the keyring
|
|
||||||
try encryptWithAllKeys(plainData: plainData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
||||||
try encrypt(plainData: plainData, keyIDs: keyID)
|
try encrypt(plainData: plainData, keyIDs: keyID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,23 +89,6 @@ public class PGPAgent {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) instead.")
|
|
||||||
public func encrypt(plainData: Data, keyID: String) throws -> Data {
|
|
||||||
try checkAndInit()
|
|
||||||
guard let 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, keyIDs: [keyID])
|
|
||||||
}
|
|
||||||
|
|
||||||
public func encrypt(plainData: Data, keyIDs: [String]) throws -> Data {
|
public func encrypt(plainData: Data, keyIDs: [String]) throws -> Data {
|
||||||
try checkAndInit()
|
try checkAndInit()
|
||||||
guard let pgpInterface else {
|
guard let pgpInterface else {
|
||||||
|
|
@ -114,15 +97,6 @@ public class PGPAgent {
|
||||||
return try pgpInterface.encrypt(plainData: plainData, keyIDs: keyIDs)
|
return try pgpInterface.encrypt(plainData: plainData, keyIDs: keyIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use encrypt(plainData:keyIDs:) or encryptWithAllKeys(plainData:) instead.")
|
|
||||||
public func encrypt(plainData: Data) throws -> Data {
|
|
||||||
try checkAndInit()
|
|
||||||
guard let pgpInterface else {
|
|
||||||
throw AppError.encryption
|
|
||||||
}
|
|
||||||
return try pgpInterface.encrypt(plainData: plainData, keyID: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func encryptWithAllKeys(plainData: Data) throws -> Data {
|
public func encryptWithAllKeys(plainData: Data) throws -> Data {
|
||||||
try checkAndInit()
|
try checkAndInit()
|
||||||
guard let pgpInterface else {
|
guard let pgpInterface else {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@
|
||||||
protocol PGPInterface {
|
protocol PGPInterface {
|
||||||
func decrypt(encryptedData: Data, keyIDHint: String?, passPhraseForKey: @escaping (String) -> String) throws -> Data?
|
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
|
// encrypt with all public keys for which we also have a private key
|
||||||
func encryptWithAllKeys(plainData: Data) throws -> Data
|
func encryptWithAllKeys(plainData: Data) throws -> Data
|
||||||
func encrypt(plainData: Data, keyIDs: [String]) throws -> Data
|
func encrypt(plainData: Data, keyIDs: [String]) throws -> Data
|
||||||
|
|
|
||||||
|
|
@ -420,12 +420,15 @@ public class PasswordStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encrypt(password: Password, keyID: String? = nil) throws -> Data {
|
public func encrypt(password: Password, keyID: String? = nil) throws -> Data {
|
||||||
|
var keyID = keyID
|
||||||
if Defaults.isEnableGPGIDOn {
|
if Defaults.isEnableGPGIDOn {
|
||||||
let encryptedDataPath = password.fileURL(in: storeURL)
|
let encryptedDataPath = password.fileURL(in: storeURL)
|
||||||
let keyID = keyID ?? findGPGID(from: encryptedDataPath)
|
keyID = keyID ?? findGPGID(from: encryptedDataPath)
|
||||||
return try PGPAgent.shared.encrypt(plainData: password.plainData, keyID: keyID)
|
|
||||||
}
|
}
|
||||||
return try PGPAgent.shared.encrypt(plainData: password.plainData)
|
if let keyID {
|
||||||
|
return try PGPAgent.shared.encrypt(plainData: password.plainData, keyIDs: [keyID])
|
||||||
|
}
|
||||||
|
return try PGPAgent.shared.encryptWithAllKeys(plainData: password.plainData)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeGitSSHKeys() {
|
public func removeGitSSHKeys() {
|
||||||
|
|
|
||||||
|
|
@ -526,28 +526,13 @@ final class PGPAgentLowLevelTests: XCTestCase {
|
||||||
XCTAssertEqual(passphraseRequests, [shortID])
|
XCTAssertEqual(passphraseRequests, [shortID])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt with short ID: when containsPublicKey matches (via suffix), the short ID is passed to interface.
|
|
||||||
func testEncryptWithKeyID_shortIDRecognized_shortIDFlowsThrough() throws {
|
|
||||||
let shortID = "a1024dae"
|
|
||||||
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
|
||||||
mockPGP.publicKeyIDs = [longFingerprint]
|
|
||||||
mockPGP.keyIDs = [longFingerprint]
|
|
||||||
|
|
||||||
_ = try agent.encrypt(plainData: testDecryptedData, keyID: shortID)
|
|
||||||
|
|
||||||
XCTAssertEqual(mockPGP.containsPublicKeyCalls, [shortID])
|
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 1)
|
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls[0].keyIDs, [shortID])
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Encrypt passthrough tests (for completeness of mock interaction)
|
// MARK: - Encrypt passthrough tests (for completeness of mock interaction)
|
||||||
|
|
||||||
/// encrypt(plainData:keyID:) calls containsPublicKey and passes data through via encrypt(plainData:keyIDs:).
|
func testEncryptWithKeyIDs_passesThrough() throws {
|
||||||
func testEncryptWithKeyID_keyFound_callsInterface() throws {
|
|
||||||
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
||||||
mockPGP.publicKeyIDs = [longFingerprint]
|
mockPGP.publicKeyIDs = [longFingerprint]
|
||||||
|
|
||||||
let result = try agent.encrypt(plainData: testDecryptedData, keyID: longFingerprint)
|
let result = try agent.encrypt(plainData: testDecryptedData, keyIDs: [longFingerprint])
|
||||||
|
|
||||||
XCTAssertEqual(result, mockPGP.encryptResult)
|
XCTAssertEqual(result, mockPGP.encryptResult)
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 1)
|
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 1)
|
||||||
|
|
@ -555,48 +540,14 @@ final class PGPAgentLowLevelTests: XCTestCase {
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls[0].plainData, testDecryptedData)
|
XCTAssertEqual(mockPGP.encryptMultiKeyCalls[0].plainData, testDecryptedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// encrypt with unknown key and single available key falls back.
|
|
||||||
func testEncryptWithKeyID_keyNotFound_singleKey_fallsBack() throws {
|
|
||||||
let shortID = "e9444483"
|
|
||||||
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
|
||||||
mockPGP.publicKeyIDs = []
|
|
||||||
mockPGP.keyIDs = [longFingerprint]
|
|
||||||
|
|
||||||
let result = try agent.encrypt(plainData: testDecryptedData, keyID: shortID)
|
|
||||||
|
|
||||||
XCTAssertEqual(result, mockPGP.encryptResult)
|
|
||||||
XCTAssertEqual(mockPGP.containsPublicKeyCalls, [shortID])
|
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls[0].keyIDs, [longFingerprint])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// encrypt with unknown key and multiple keys throws.
|
|
||||||
func testEncryptWithKeyID_keyNotFound_multipleKeys_throws() {
|
|
||||||
mockPGP.publicKeyIDs = []
|
|
||||||
mockPGP.keyIDs = ["4712286271220db299883ea7062e678da1024dae", "787eae1a5fa3e749aa34cc6aa0645ebed862027e"]
|
|
||||||
|
|
||||||
XCTAssertThrowsError(try agent.encrypt(plainData: testDecryptedData, keyID: "a1024dae")) { error in
|
|
||||||
XCTAssertEqual(error as? AppError, AppError.pgpPublicKeyNotFound(keyID: "a1024dae"))
|
|
||||||
}
|
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// encrypt(plainData:) without keyID passes nil to the deprecated interface method.
|
|
||||||
func testEncryptNoKeyID_passesNilToInterface() throws {
|
|
||||||
let result = try agent.encrypt(plainData: testDecryptedData)
|
|
||||||
|
|
||||||
XCTAssertEqual(result, mockPGP.encryptResult)
|
|
||||||
XCTAssertEqual(mockPGP.encryptCalls.count, 1)
|
|
||||||
XCTAssertNil(mockPGP.encryptCalls[0].keyID)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// encrypt propagates errors from interface.
|
/// encrypt propagates errors from interface.
|
||||||
func testEncrypt_interfaceThrows_propagatesError() {
|
func testEncryptWithKeyIDs_interfaceThrows_propagatesError() {
|
||||||
let shortID = "a1024dae"
|
let shortID = "a1024dae"
|
||||||
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
let longFingerprint = "4712286271220db299883ea7062e678da1024dae"
|
||||||
mockPGP.publicKeyIDs = [longFingerprint]
|
mockPGP.publicKeyIDs = [longFingerprint]
|
||||||
mockPGP.encryptError = AppError.encryption
|
mockPGP.encryptError = AppError.encryption
|
||||||
|
|
||||||
XCTAssertThrowsError(try agent.encrypt(plainData: testDecryptedData, keyID: shortID)) { error in
|
XCTAssertThrowsError(try agent.encrypt(plainData: testDecryptedData, keyIDs: [shortID])) { error in
|
||||||
XCTAssertEqual(error as? AppError, AppError.encryption)
|
XCTAssertEqual(error as? AppError, AppError.encryption)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -614,7 +565,6 @@ final class PGPAgentLowLevelTests: XCTestCase {
|
||||||
XCTAssertEqual(mockPGP.encryptWithAllKeysCalls[0].plainData, testDecryptedData)
|
XCTAssertEqual(mockPGP.encryptWithAllKeysCalls[0].plainData, testDecryptedData)
|
||||||
// Does not call containsPublicKey or the single/multi-key encrypt methods.
|
// Does not call containsPublicKey or the single/multi-key encrypt methods.
|
||||||
XCTAssertEqual(mockPGP.containsPublicKeyCalls.count, 0)
|
XCTAssertEqual(mockPGP.containsPublicKeyCalls.count, 0)
|
||||||
XCTAssertEqual(mockPGP.encryptCalls.count, 0)
|
|
||||||
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 0)
|
XCTAssertEqual(mockPGP.encryptMultiKeyCalls.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -627,7 +577,7 @@ final class PGPAgentLowLevelTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// encryptWithAllKeys throws encryption error when pgpInterface is nil (checkAndInit fails).
|
/// encryptWithAllKeys throws keyImport when checkAndInit triggers initKeys without PGP keys.
|
||||||
func testEncryptWithAllKeys_checkAndInit_requiresPGPKeyPassphraseInKeystore() throws {
|
func testEncryptWithAllKeys_checkAndInit_requiresPGPKeyPassphraseInKeystore() throws {
|
||||||
keychain.removeContent(for: Globals.pgpKeyPassphrase)
|
keychain.removeContent(for: Globals.pgpKeyPassphrase)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ class MockPGPInterface: PGPInterface {
|
||||||
|
|
||||||
var decryptCalls: [DecryptCall] = []
|
var decryptCalls: [DecryptCall] = []
|
||||||
var resolvedPassphrases: [String] = []
|
var resolvedPassphrases: [String] = []
|
||||||
var encryptCalls: [EncryptCall] = []
|
|
||||||
var encryptMultiKeyCalls: [EncryptMultiKeyCall] = []
|
var encryptMultiKeyCalls: [EncryptMultiKeyCall] = []
|
||||||
var encryptWithAllKeysCalls: [EncryptWithAllKeysCall] = []
|
var encryptWithAllKeysCalls: [EncryptWithAllKeysCall] = []
|
||||||
var containsPublicKeyCalls: [String] = []
|
var containsPublicKeyCalls: [String] = []
|
||||||
|
|
@ -66,14 +65,6 @@ class MockPGPInterface: PGPInterface {
|
||||||
return decryptResult
|
return decryptResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypt(plainData: Data, keyID: String?) throws -> Data {
|
|
||||||
encryptCalls.append(EncryptCall(plainData: plainData, keyID: keyID))
|
|
||||||
if let error = encryptError {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
return encryptResult
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
func encryptWithAllKeys(plainData: Data) throws -> Data {
|
||||||
encryptWithAllKeysCalls.append(EncryptWithAllKeysCall(plainData: plainData))
|
encryptWithAllKeysCalls.append(EncryptWithAllKeysCall(plainData: plainData))
|
||||||
if let error = encryptError {
|
if let error = encryptError {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue