Add beta distrubution channel

This commit is contained in:
Mingshen Sun 2020-07-19 18:16:16 -07:00
parent 0bbda5a4b0
commit a92940e610
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
58 changed files with 1065 additions and 252 deletions

View file

@ -0,0 +1,144 @@
//
// GopenPGPInterface.swift
// passKit
//
// Created by Danny Moesch on 08.09.19.
// Copyright © 2019 Bob Sun. All rights reserved.
//
import Crypto
struct GopenPGPInterface: PGPInterface {
private static let errorMapping: [String: Error] = [
"gopenpgp: error in unlocking key: openpgp: invalid data: private key checksum failure": AppError.WrongPassphrase,
"openpgp: incorrect key": AppError.KeyExpiredOrIncompatible,
]
private var publicKeys: [String: CryptoKey] = [:]
private var privateKeys: [String: CryptoKey] = [:]
init(publicArmoredKey: String, privateArmoredKey: String) throws {
let pubKeys = extractKeysFromArmored(str: publicArmoredKey)
let prvKeys = extractKeysFromArmored(str: privateArmoredKey)
for key in pubKeys {
var error: NSError?
guard let k = CryptoNewKeyFromArmored(key, &error) else {
guard error == nil else {
throw error!
}
throw AppError.KeyImport
}
publicKeys[k.getFingerprint().lowercased()] = k
}
for key in prvKeys {
var error: NSError?
guard let k = CryptoNewKeyFromArmored(key, &error) else {
guard error == nil else {
throw error!
}
throw AppError.KeyImport
}
privateKeys[k.getFingerprint().lowercased()] = k
}
}
func extractKeysFromArmored(str: String) -> [String] {
var keys: [String] = []
var key: String = ""
for line in str.splitByNewline() {
if line.trimmed.uppercased().hasPrefix("-----BEGIN PGP") {
key = ""
key += line
} else if line.trimmed.uppercased().hasPrefix("-----END PGP") {
key += line
keys.append(key)
} else {
key += line
}
key += "\n"
}
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 {
throw AppError.Decryption
}
do {
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, keyID: String) throws -> Data {
guard let e = publicKeys.first(where: { (key, _) in key.hasSuffix(keyID.lowercased()) }),
let publicKey = publicKeys[e.key] else {
throw AppError.Encryption
}
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)
guard error == nil else {
throw error!
}
return armor.data(using: .ascii)!
}
return encryptedData.getBinary()!
}
var keyID: [String] {
return publicKeys.keys.map({ $0.uppercased() })
}
var shortKeyID: [String] {
return publicKeys.keys.map({ $0.suffix(8).uppercased()})
}
private func createPgpMessage(from encryptedData: Data) -> CryptoPGPMessage? {
// Important note:
// Even if Defaults.encryptInArmored is true now, it could be different during the encryption.
var error: NSError?
let message = CryptoNewPGPMessageFromArmored(String(data: encryptedData, encoding: .ascii), &error)
if error == nil {
return message
}
return CryptoNewPGPMessage(encryptedData.mutable as Data)
}
}

View file

@ -0,0 +1,60 @@
//
// ObjectivePGPInterface.swift
// passKit
//
// Created by Danny Moesch on 08.09.19.
// Copyright © 2019 Bob Sun. All rights reserved.
//
import ObjectivePGP
struct ObjectivePGPInterface: PGPInterface {
private let publicKey: Key
private let privateKey: Key
private let keyring = ObjectivePGP.defaultKeyring
init(publicArmoredKey: String, privateArmoredKey: String) throws {
guard let publicKeyData = publicArmoredKey.data(using: .ascii), let privateKeyData = privateArmoredKey.data(using: .ascii) else {
throw AppError.KeyImport
}
let publicKeys = try ObjectivePGP.readKeys(from: publicKeyData)
let privateKeys = try ObjectivePGP.readKeys(from: privateKeyData)
keyring.import(keys: publicKeys)
keyring.import(keys: privateKeys)
guard let publicKey = publicKeys.first, let privateKey = privateKeys.first else {
throw AppError.KeyImport
}
self.publicKey = publicKey
self.privateKey = privateKey
}
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, 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)!
}
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 })
}
var shortKeyID: [String] {
return keyring.keys.map({ $0.keyID.shortIdentifier })
}
}

View file

@ -0,0 +1,110 @@
//
// PGPAgent.swift
// passKit
//
// Created by Yishi Lin on 2019/7/17.
// Copyright © 2019 Bob Sun. All rights reserved.
//
public class PGPAgent {
public static let shared: PGPAgent = PGPAgent()
private let keyStore: KeyStore
private var pgpInterface: PGPInterface?
private var latestDecryptStatus: Bool = true
public init(keyStore: KeyStore = AppKeychain.shared) {
self.keyStore = keyStore
}
public func initKeys() throws {
guard let publicKey: String = keyStore.get(for: PgpKey.PUBLIC.getKeychainKey()),
let privateKey: String = keyStore.get(for: PgpKey.PRIVATE.getKeychainKey()) else {
pgpInterface = nil
throw AppError.KeyImport
}
do {
pgpInterface = try GopenPGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
} catch {
pgpInterface = try ObjectivePGPInterface(publicArmoredKey: publicKey, privateArmoredKey: privateKey)
}
}
public func uninitKeys() {
pgpInterface = nil
}
public func getKeyID() throws -> [String] {
try checkAndInit()
return pgpInterface?.keyID ?? []
}
public func getShortKeyID() throws -> [String] {
try checkAndInit()
return pgpInterface?.shortKeyID.sorted() ?? []
}
public func decrypt(encryptedData: Data, keyID: String, requestPGPKeyPassphrase: @escaping (String) -> String) throws -> Data? {
// Init keys.
try checkAndInit()
guard let pgpInterface = pgpInterface else {
throw AppError.Decryption
}
var keyID = keyID;
if !pgpInterface.containsPrivateKey(with: keyID) {
if pgpInterface.keyID.count == 1 {
keyID = pgpInterface.keyID.first!
} else {
throw AppError.PgpPrivateKeyNotFound(keyID: keyID)
}
}
// Remember the previous status and set the current status
let previousDecryptStatus = self.latestDecryptStatus
self.latestDecryptStatus = false
// Get the PGP key passphrase.
var passphrase = ""
if previousDecryptStatus == false {
passphrase = requestPGPKeyPassphrase(keyID)
} else {
passphrase = keyStore.get(for: AppKeychain.getPGPKeyPassphraseKey(keyID: keyID)) ?? requestPGPKeyPassphrase(keyID)
}
// Decrypt.
guard let result = try pgpInterface.decrypt(encryptedData: encryptedData, keyID: keyID, passphrase: passphrase) else {
return nil
}
// The decryption step has succeed.
self.latestDecryptStatus = true
return result
}
public func encrypt(plainData: Data, keyID: String) throws -> Data {
try checkAndInit()
guard let pgpInterface = 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, keyID: keyID)
}
public var isPrepared: Bool {
return keyStore.contains(key: PgpKey.PUBLIC.getKeychainKey())
&& keyStore.contains(key: PgpKey.PRIVATE.getKeychainKey())
}
private func checkAndInit() throws {
if pgpInterface == nil || !keyStore.contains(key: Globals.pgpKeyPassphrase) {
try initKeys()
}
}
}

View file

@ -0,0 +1,22 @@
//
// PGPInterface.swift
// passKit
//
// Created by Danny Moesch on 08.09.19.
// Copyright © 2019 Bob Sun. All rights reserved.
//
protocol PGPInterface {
func decrypt(encryptedData: Data, keyID: String, passphrase: 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 shortKeyID: [String] { get }
}

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.me.mssun.passforiosbeta</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)group.me.mssun.passforiosbeta</string>
</array>
</dict>
</plist>