mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Bot auth
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import LocalAuthentication
|
||||
import SwiftSignalKit
|
||||
import Security
|
||||
|
||||
public enum LocalAuthBiometricAuthentication {
|
||||
case touchId
|
||||
@@ -8,10 +9,65 @@ public enum LocalAuthBiometricAuthentication {
|
||||
}
|
||||
|
||||
public struct LocalAuth {
|
||||
public static let biometricAuthentication: LocalAuthBiometricAuthentication? = {
|
||||
private static let customKeyIdPrefix = "$#_".data(using: .utf8)!
|
||||
|
||||
public enum DecryptionResult {
|
||||
public enum Error {
|
||||
case cancelled
|
||||
case generic
|
||||
}
|
||||
|
||||
case result(Data)
|
||||
case error(Error)
|
||||
}
|
||||
|
||||
public final class PrivateKey {
|
||||
private let privateKey: SecKey
|
||||
private let publicKey: SecKey
|
||||
public let publicKeyRepresentation: Data
|
||||
|
||||
fileprivate init(privateKey: SecKey, publicKey: SecKey, publicKeyRepresentation: Data) {
|
||||
self.privateKey = privateKey
|
||||
self.publicKey = publicKey
|
||||
self.publicKeyRepresentation = publicKeyRepresentation
|
||||
}
|
||||
|
||||
public func encrypt(data: Data) -> Data? {
|
||||
var error: Unmanaged<CFError>?
|
||||
let cipherText = SecKeyCreateEncryptedData(self.publicKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &error)
|
||||
if let error {
|
||||
error.release()
|
||||
}
|
||||
guard let cipherText else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = cipherText as Data
|
||||
return result
|
||||
}
|
||||
|
||||
public func decrypt(data: Data) -> DecryptionResult {
|
||||
var maybeError: Unmanaged<CFError>?
|
||||
let plainText = SecKeyCreateDecryptedData(self.privateKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &maybeError)
|
||||
let error = maybeError?.takeRetainedValue()
|
||||
|
||||
guard let plainText else {
|
||||
if let error {
|
||||
if CFErrorGetCode(error) == -2 {
|
||||
return .error(.cancelled)
|
||||
}
|
||||
}
|
||||
return .error(.generic)
|
||||
}
|
||||
|
||||
let result = plainText as Data
|
||||
return .result(result)
|
||||
}
|
||||
}
|
||||
|
||||
public static var biometricAuthentication: LocalAuthBiometricAuthentication? {
|
||||
let context = LAContext()
|
||||
if context.canEvaluatePolicy(LAPolicy(rawValue: Int(kLAPolicyDeviceOwnerAuthenticationWithBiometrics))!, error: nil) {
|
||||
#if swift(>=5.9)
|
||||
switch context.biometryType {
|
||||
case .faceID, .opticID:
|
||||
return .faceId
|
||||
@@ -22,22 +78,10 @@ public struct LocalAuth {
|
||||
@unknown default:
|
||||
return nil
|
||||
}
|
||||
#else
|
||||
switch context.biometryType {
|
||||
case .faceID://, .opticID:
|
||||
return .faceId
|
||||
case .touchID:
|
||||
return .touchId
|
||||
case .none:
|
||||
return nil
|
||||
@unknown default:
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
public static let evaluatedPolicyDomainState: Data? = {
|
||||
let context = LAContext()
|
||||
@@ -78,4 +122,145 @@ public struct LocalAuth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func bundleSeedId() -> String? {
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword as String,
|
||||
kSecAttrAccount as String: "bundleSeedID",
|
||||
kSecAttrService as String: "",
|
||||
kSecReturnAttributes as String: true
|
||||
]
|
||||
var result: CFTypeRef?
|
||||
var status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||
if status == errSecItemNotFound {
|
||||
status = SecItemAdd(query as CFDictionary, &result)
|
||||
}
|
||||
if status != errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
guard let result = result else {
|
||||
return nil
|
||||
}
|
||||
if CFGetTypeID(result) != CFDictionaryGetTypeID() {
|
||||
return nil
|
||||
}
|
||||
guard let resultDict = (result as! CFDictionary) as? [String: Any] else {
|
||||
return nil
|
||||
}
|
||||
guard let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String else {
|
||||
return nil
|
||||
}
|
||||
let components = accessGroup.components(separatedBy: ".")
|
||||
guard let seedId = components.first else {
|
||||
return nil
|
||||
}
|
||||
return seedId;
|
||||
}
|
||||
|
||||
public static func getPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassKey as String,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrAccessGroup as String: accessGroup,
|
||||
kSecReturnRef as String: true
|
||||
]
|
||||
|
||||
var maybePrivateKey: CFTypeRef?
|
||||
let status = SecItemCopyMatching(query as CFDictionary, &maybePrivateKey)
|
||||
if status != errSecSuccess {
|
||||
return nil
|
||||
}
|
||||
guard let maybePrivateKey else {
|
||||
return nil
|
||||
}
|
||||
if CFGetTypeID(maybePrivateKey) != SecKeyGetTypeID() {
|
||||
return nil
|
||||
}
|
||||
let privateKey = maybePrivateKey as! SecKey
|
||||
|
||||
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||
return nil
|
||||
}
|
||||
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public static func removePrivateKey(baseAppBundleId: String, keyId: Data) -> Bool {
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return false
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassKey as String,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrIsPermanent as String: true,
|
||||
kSecAttrAccessGroup as String: accessGroup
|
||||
]
|
||||
|
||||
let status = SecItemDelete(query as CFDictionary)
|
||||
if status != errSecSuccess {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public static func addPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||
guard let bundleSeedId = self.bundleSeedId() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let applicationTag = customKeyIdPrefix + keyId
|
||||
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||
|
||||
guard let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.userPresence, .privateKeyUsage], nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let attributes: [String: Any] = [
|
||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||
kSecAttrKeySizeInBits as String: 256 as NSNumber,
|
||||
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave as String,
|
||||
kSecPrivateKeyAttrs as String: [
|
||||
kSecAttrIsPermanent as String: true,
|
||||
kSecAttrApplicationTag as String: applicationTag,
|
||||
kSecAttrAccessControl as String: access,
|
||||
kSecAttrAccessGroup as String: accessGroup,
|
||||
] as [String: Any]
|
||||
]
|
||||
var error: Unmanaged<CFError>?
|
||||
let maybePrivateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
|
||||
if let error {
|
||||
error.release()
|
||||
}
|
||||
guard let privateKey = maybePrivateKey else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||
return nil
|
||||
}
|
||||
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user