Update to gopengpg v2.0.0

This commit is contained in:
Mingshen Sun 2020-04-11 23:23:38 -07:00
parent 9a688b518f
commit 84b1c07f64
13 changed files with 314 additions and 56 deletions

View file

@ -11,42 +11,56 @@ import Crypto
struct GopenPgp: PgpInterface {
private static let errorMapping: [String: Error] = [
"openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
"gopenpgp: error in unlocking key: openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
"openpgp: incorrect key": AppError.KeyExpiredOrIncompatible,
]
private let publicKey: CryptoKeyRing
private let privateKey: CryptoKeyRing
private let publicKey: CryptoKey
private let privateKey: CryptoKey
init(publicArmoredKey: String, privateArmoredKey: String) throws {
var error: NSError?
guard let publicKey = CryptoBuildKeyRingArmored(publicArmoredKey, &error),
let privateKey = CryptoBuildKeyRingArmored(privateArmoredKey, &error) else {
guard let publicKey = CryptoNewKeyFromArmored(publicArmoredKey, &error),
let privateKey = CryptoNewKeyFromArmored(privateArmoredKey, &error) else {
guard error == nil else {
throw error!
}
throw AppError.KeyImport
}
guard error == nil else {
throw error!
}
self.publicKey = publicKey
self.privateKey = privateKey
}
func decrypt(encryptedData: Data, passphrase: String) throws -> Data? {
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
do {
try privateKey.unlock(withPassphrase: passphrase)
} catch {
throw Self.errorMapping[error.localizedDescription, default: error]
}
let message = createPgpMessage(from: encryptedData)
do {
return try privateKey.decrypt(message, verifyKey: nil, verifyTime: 0).data
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) throws -> Data {
let encryptedData = try publicKey.encrypt(CryptoNewPlainMessage(plainData.mutable as Data), privateKey: nil)
func encrypt(plainData: Data, keyID: String) throws -> Data {
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)
@ -60,8 +74,14 @@ struct GopenPgp: PgpInterface {
var keyId: String {
var error: NSError?
let fingerprint = publicKey.getFingerprint(&error)
return error == nil ? String(fingerprint.suffix(8)).uppercased() : ""
let fingerprint = publicKey.getHexKeyID()
return String(fingerprint).uppercased()
}
var shortKeyId: String {
var error: NSError?
let fingerprint = publicKey.getHexKeyID()
return String(fingerprint.suffix(8)).uppercased()
}
private func createPgpMessage(from encryptedData: Data) -> CryptoPGPMessage? {

View file

@ -30,11 +30,11 @@ struct ObjectivePgp: PgpInterface {
self.privateKey = privateKey
}
func decrypt(encryptedData: Data, passphrase: String) throws -> Data? {
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data? {
return try ObjectivePGP.decrypt(encryptedData, andVerifySignature: false, using: keyring.keys) { _ in passphrase }
}
func encrypt(plainData: Data) throws -> Data {
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)!
@ -43,6 +43,10 @@ struct ObjectivePgp: PgpInterface {
}
var keyId: String {
return publicKey.keyID.longIdentifier
}
var shortKeyId: String {
return publicKey.keyID.shortIdentifier
}
}

View file

@ -40,6 +40,11 @@ public class PGPAgent {
return pgpInterface?.keyId
}
public func getShortKeyId() throws -> String? {
try checkAndInit()
return pgpInterface?.shortKeyId
}
public func decrypt(encryptedData: Data, requestPGPKeyPassphrase: () -> String) throws -> Data? {
// Remember the previous status and set the current status
let previousDecryptStatus = self.latestDecryptStatus
@ -54,7 +59,7 @@ public class PGPAgent {
passphrase = keyStore.get(for: Globals.pgpKeyPassphrase) ?? requestPGPKeyPassphrase()
}
// Decrypt.
guard let result = try pgpInterface!.decrypt(encryptedData: encryptedData, passphrase: passphrase) else {
guard let result = try pgpInterface!.decrypt(encryptedData: encryptedData, keyID: "", passphrase: passphrase) else {
return nil
}
// The decryption step has succeed.
@ -67,7 +72,7 @@ public class PGPAgent {
guard let pgpInterface = pgpInterface else {
throw AppError.Encryption
}
return try pgpInterface.encrypt(plainData: plainData)
return try pgpInterface.encrypt(plainData: plainData, keyID: "")
}
public var isPrepared: Bool {

View file

@ -8,9 +8,11 @@
protocol PgpInterface {
func decrypt(encryptedData: Data, passphrase: String) throws -> Data?
func decrypt(encryptedData: Data, keyID: String, passphrase: String) throws -> Data?
func encrypt(plainData: Data) throws -> Data
func encrypt(plainData: Data, keyID: String) throws -> Data
var keyId: String { get }
var shortKeyId: String { get }
}

View file

@ -21,9 +21,12 @@ public class PasswordStore {
dateFormatter.timeStyle = .short
return dateFormatter
}()
public let storeURL = URL(fileURLWithPath: "\(Globals.repositoryPath)")
public let tempStoreURL = URL(fileURLWithPath: "\(Globals.repositoryPath)-temp")
public var storeURL: URL
public var tempStoreURL: URL {
get {
URL(fileURLWithPath: "\(storeURL.path)-temp")
}
}
public var storeRepository: GTRepository?
@ -100,11 +103,13 @@ public class PasswordStore {
public var numberOfCommits: UInt? {
return storeRepository?.numberOfCommits(inCurrentBranch: nil)
}
private init() {
init(url: URL = URL(fileURLWithPath: "\(Globals.repositoryPath)")) {
storeURL = url
// Migration
importExistingKeysIntoKeychain()
do {
if fm.fileExists(atPath: storeURL.path) {
try storeRepository = GTRepository.init(url: storeURL)
@ -175,13 +180,26 @@ public class PasswordStore {
requestCredentialPassword: @escaping (GitCredential.Credential, String?) -> String?,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
checkoutProgressBlock: @escaping (String, UInt, UInt) -> Void) throws {
do {
let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword)
let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
try self.cloneRepository(remoteRepoURL: remoteRepoURL, options: options, branchName: branchName, transferProgressBlock: transferProgressBlock, checkoutProgressBlock: checkoutProgressBlock)
} catch {
credential.delete()
throw(error)
}
}
public func cloneRepository(remoteRepoURL: URL,
options: [AnyHashable : Any]? = nil,
branchName: String,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
checkoutProgressBlock: @escaping (String, UInt, UInt) -> Void) throws {
try? fm.removeItem(at: storeURL)
try? fm.removeItem(at: tempStoreURL)
self.gitPassword = nil
self.gitSSHPrivateKeyPassphrase = nil
do {
let credentialProvider = try credential.credentialProvider(requestCredentialPassword: requestCredentialPassword)
let options = [GTRepositoryCloneOptionsCredentialProvider: credentialProvider]
storeRepository = try GTRepository.clone(from: remoteRepoURL,
toWorkingDirectory: tempStoreURL,
options: options,
@ -192,7 +210,6 @@ public class PasswordStore {
try checkoutAndChangeBranch(withName: branchName, progressBlock: checkoutProgressBlock)
}
} catch {
credential.delete()
Defaults.lastSyncedTime = nil
DispatchQueue.main.async {
self.deleteCoreData(entityName: "PasswordEntity")